1.文件信息结构体

struct stat{
    mode_t st_mode; //file type and permissions
    ino_t st_ino;         //i-node number (serial number)
    dev_t st_dev;       //device number (file system)
    dev_t st_rdev;      //device number for special files
    nlink_t st_nlink;   //number of links
    uid_t st_uid;        //user ID of owner
    gid_t st_gid;        //group ID of owner
    off_t st_size;      //size in bytes,for regular files
    struct timespec st_atime;//time of last access
    struct timespec st_mtime;//time of last modification
    struct timespec st_ctime;//time of last file status change
    blksize_t st_blksize;       //best I/O block size
    blkcnt_t st_blocks;         //number of disk blocks allocated
};

2.文件类型

(1)普通文件。这是最常用的文件类型,这种文件包含了某种形式的数据。

(2)目录文件。这种文件包含了其他文件的名字以及指向与这些文件有关信息的指针。

(3)块特殊文件。这种类型的文件提供对设备带缓冲的访问,每次访问以固定长度为单位进行。

(4)字符特殊文件。这种类型的文件提供对设备不带缓冲的访问,每次访问长度可变。系统中的所有设备要么是字符特殊文件,要么是块特殊文件。

(5)FIFO。这种类型的文件用于进程间通信,有时也称为命名管道。

(6)套接字。这种类型的文件用于进程间的网络通信。套接字也可用于在一台宿主机上进程之间的非网络通信。

(7)符号链接。这种类型的文件指向另一个文件。

示例程序:判断路径对应文件的类型:

#include "apue.h"
#include <iostream>
using namespace std;
int main(int argc,char *argv[]){
    int i;
    struct stat buf;
    char *ptr;
    ;i<argc;i++){
        cout<<argv[i]<<" :";
        ){
            cout<<"latat error!"<<endl;
        }
        if(S_ISREG(buf.st_mode))
            ptr="regular";
        else if(S_ISDIR(buf.st_mode))
            ptr="directory";
        else if(S_ISCHR(buf.st_mode))
            ptr="character special";
        else if(S_ISBLK(buf.st_mode))
            ptr="block special";
        else if(S_ISFIFO(buf.st_mode))
            ptr="fifo";
        else if(S_ISLNK(buf.st_mode))
            ptr="symbolic link";
        else if(S_ISSOCK(buf.st_mode))
            ptr="socket";
        else
            ptr="* * unknown! * *";
        cout<<ptr<<endl;
    }
    ;
}

运行输出:

/etc/passwd :regular
/etc :directory
/dev/log :socket
/var/lib :directory
/dev/sr0 :block special
/dev/tty :character special
/dev/cdrom :symbolic link

3.文件访问权限:

  每个文件有9个访问权限位,可将它们分成3类。

st_mode 屏蔽 含义
S_IRUSR 用户读
S_IWUSR 用户写
S_IXUSR 用户执行
S_IRGRP 组读
S_IWGRP 组写
S_IXGRP 组执行
S_IROTH 其他读
S_IWOTH 其他写
S_IXOTH 其他执行

文件访问权限:

  1. 我们用名字打开任一类型的文件时,对该名字中包含的每一个目录,包括它可能隐含的当前工作目录都应该具有执行权限。
  2. 对于一个文件的读权限决定了我们是否能够打开现有文件进行读操作。
  3. 对于一个文件的写权限决定了我们是否能够打开现有文件进行写操作。
  4. 为了在一个目录中创建一个新文件,必须对该目录具有写权限和执行权限。
  5. 为了删除一个现有文件,必须对包含该文件的目录具有写权限和执行权限,对该文件本身不需要读、写权限。

进程每次打开、创建或者删除一个文件的时候,内核就进行文件访问权限测试,如下:

(1)若进程的有效用户ID为0(超级用户),则允许访问。

(2)若进程的有效用户ID等于文件的所有者ID,那么如果所有者适当的访问权限位被设置,则允许访问。访问权限与文件中该用户对应的权限设置有关。

(3)若进程的有效ID或者进程的附属组ID之一等于文件的组ID,那么如果组适当的访问权限位被设置,则允许访问。

(4)若其他用户适当的访问权限被设置,则允许访问。

4.新文件和目录的所有权

  新文件的用户ID设置为进程的有效用户ID;

  新文件的组ID可以设置为进程的有效组ID或者是它所在的目录的组ID。

5.函数access和faccessat

  函数access和faccessat按照实际用户ID和实际组ID进程访问的权限测试。

#include <unistd.h>
int access(const char *pathname ,int mode);
int faccessat(int fd,const char *pathname,int mode ,int flag);
//flag用于改变faccessat函数的行为,如果flag设置为AT_EACCESS,访问检查用的是调用进程的有效用户ID和有效组ID,而不是实际用户ID和实际组ID。
//两个函数的返回值:若成功,返回0,否则返回-1

示例程序:

#include <iostream>
#include "apue.h"
#include <fcntl.h>
using namespace std;

int main(int argc ,char *argv[] ) {
    ){
        cout<<"usage: apue <pathname>"<<endl;
        ;
    }
    ],R_OK)<){
        cout<<]<<endl;
    }
    else
        cout<<"read access ok!"<<endl;
    ],O_RDONLY)<){
        cout<<]<<endl;
    }
    else
        cout<<"open reading ok!"<<endl;
    ;
}

程序运行:

apue apue
read access ok!
open for reading ok!

apue /etc/shadow
read access error for /etc/shadow
open error fro /etc/shadow

6.函数umask

  至此,我们说明了与每个文件相关联的9个访问权限位,在此基础上我们可以说明与每个进程相关联的文件模式创建屏蔽字。

  umask函数为进程设置文件模式创建屏蔽字,并返回之前的值。

#include <sys/stat.h>
mode_t umask(mode_t cmask);
//返回值:之前的文件模式创建屏蔽字

  其中,参数cmask是由3中表格列出的9个常量(S_IRUSR , S_IWUSR)中的若干位按照或构成的。

示例程序:

#include "apue.h"
#include <iostream>
#include <fcntl.h>
using namespace std;
#define PWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)//禁止所有用户的执行权限
int main(){
    umask();
    ){
        cout<<"creat error for foo"<<endl;
    }
    umask(S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
    )
        cout<<"creat error for bar"<<endl;

    ;
}

测试生成的文件的权限:

ll foo
ll bar

测试结果:

-rw-rw-rw-  1 zhouyang  staff  0  3 25 23:34 foo
-rw-------  1 zhouyang  staff  0  3 25 23:34 bar

  所以,为了使得所有的用户对文件都有访问权限,那么应该设置umask值为0。可见,最终文件的权限为PWRWRW设定的权限 - umask设定的权限。也就是说umask设定的是需要屏蔽的权限。

  在bash中,umask的值也设定了创建文件的时候屏蔽的权限位,如下所示:

zhouyangdeMacBook-Pro:Debug zhouyang$ umask -S
u=rwx,g=rx,o=rx
zhouyangdeMacBook-Pro:Debug zhouyang$ umask 0002
zhouyangdeMacBook-Pro:Debug zhouyang$ umask -S
u=rwx,g=rwx,o=rx

7.函数chmod,fchmod和fchmodat

  chmod,fchmod和fchmodat函数使我们可以更改现有文件的访问权限。

#include <sys/stat.h>
int chmod(const char *pathname);
int fchmod(int fd,mode_t mode);
int fchmodat(int fd,const char * pathname,mode_t mode,int flag);
//成功返回0,出错返回-1.

  为了改变一个文件的权限位,进程的有效用户ID必须等于文件的所有者ID,或者改进程必须具有超级用户权限。

  参数mode是下图中所示的常量的按位或:

mode 说明
S_ISUID 执行时设置用户ID
S_ISGID 执行时设置组ID
S_ISVTX 保存正文(粘着位)
S_IRWXU 用户读、写、执行
S_IRUSR 用户读
S_IWUSR 用户写
S_IXUSR 用户执行
S_IRWXG 组读、写、执行
S_IRGRP 组读
S_IWGRP 组写
S_IXGRP 组执行
S_IRWXO 其他读、写、执行
S_IROTH 其他读
S_IWOTH 其他写
S_IXOTH 其他执行

  

  

  

  注意,我们增加了6项,其中9个是3中表格中的权限。我们另外增加了6个,分别是两个设置ID常量(S_ISUID,S_ISGID)、保存正文常量(S_ISVTX),以及3个组合常量。

  程序示例:修改6中程序生成的文件的权限:

#include "apue.h"
#include <iostream>
#include <fcntl.h>
using namespace std;
int main(){
    struct stat statbuf;
    ){
        cout<<"stat error for foo"<<endl;
    }
    //foo.mode_t = ( rw-rw-rw- & ~ S_IXGRP )|S_ISGID
    //turn on set-group-id and turn off group-execute
    //S_ISGID 执行时设置组ID
    ){
        cout<<"chmod error for foo \n";
    }
    //set absolute mode to "rw-r--r--"
    )
        cout<<"chmod error for bar"<<endl;
    ;

}

最终文件foo和bar的权限分别为:

rw-rwSrw-

-rw-r--r--

8.粘着位

  S_ISVTX位有一段有趣的历史。在UNIX尚未使用请求分页式技术的早期版本中,S_ISVTX位被称为粘着位。如果一个可执行程序文件的这一位置被设置了,那么当改程序第一次被执行的时候,在其终止的时候,程序正文的一个副本仍然板寸在交换区。这使得下次执行改程序时能够较快地将其载入内存。

9.函数chown/fchown/fchownat 和lchown

  标题所示的函数用于改变文件的用户ID和组ID。如果来个参数owner或者group中的任意一个是-1,则对应的ID不变。

#include <unistd.h>
int chown(const char *pathname,uid_t owner,gid_t group)
int fchown(int fd,uid_t owner,gid_t group)
int fchownat(int fd,const char *pathname,uid_t owner,gid_t group,int flag)
int lchown(const char *pathname,uid_t owner,gid_t group)
//成功返回0,否则返回-1

10.文件长度

 stat结构成员st_size 表示以字节为单位的文件的长度。此字段只对普通的文件、目录文件和符号链接有意义。

  现今,大多数现代的UNIX系统提供字段st_blksize和st_blocks。其中,第一个是对文件I/O较为合适的块长度,第二个是所分配的实际的512字节块块数目。

11.文件截断 

#include <unistd.h>
int truncate (const char *pathname,off_t length);
int ftruncate(int fd,off_t length);
//两个函数的返回值;若成功,返回0,若出错,则返回-1

  这两个函数将一个现有文件长度截断位length。如果该文件以前的长度大于length,则超过length以外的数据就不能再访问。如果以前的长度小于length,文件长度将增加,在以前的文件尾端和新的文件尾端之间的数据将读作0。

12.文件系统

                      文件系统结构图

(1)硬链接和软链接

  在Linux中,多个文件名指向同一索引节点是存在的。一般这种连接就是硬链接。

  软链接文件的实际内容包含了该符号链接所指向的文件的名字。(符号链接就是软链接)

(2)i结点

  i结点包含了文件有关的所有信息:文件类型、文件访问权限位、文件长度和指向文件数据块的指针等。stat结构中的大多数信息来自结点。只有两项重要数据存放在目录项中:文件名和结点编号。

13.函数link、linkat、unlink、unlinkat和remove

创建硬链接:

#include <unistd.h>
int link(const char * existpath,const char * newpath);
int linkat(int efd,const char *existingpath,int nfd,const char *newpath,int flag);
//成功后返回0,否则返回-1

删除硬链接:

#include <unistd.h>
int unlink(const char *pathname);
int unlinkat(int fd,const char *pathname,int flag);
//成功后返回0,否则返回-1

示例程序:

#include "apue.h"
#include <iostream>
#include <fcntl.h>
using namespace std;
int main(){
    //是否打开文件出错
    ){
        cout<<"open error!"<<endl;
    }
    //删除tempfile的硬链接
    )
        cout<<"unlink error!"<<endl;
    cout<<"file unlinked!"<<endl;
    //此时文件占有的空间仍然没有释放
    sleep();
    cout<<"done!"<<endl;
    ;
}
//此时文件tempfile已经完全删除

14.重命名文件

#include <stdio.h>
int rename(const char *oldname,const char * newname);
int renameat(int oldfd,const char *oldname,int newfd,const char *newname);
//成功返回0,失败返回-1

15.创建和读取符号链接

可以用symlink或者symlinkat函数创建一个符号链接:

#include <unistd.h>
int symlink(const char * actualpath,const char *sympath);
int symlinkat(const char *actualpath,int fd,const char *sympath);

使用下述函数可以打开符号链接本身:

#include <unistd.h>
ssize_t readlink(const char *restrict pathname,char *restrict buf,size_t bufsize);
ssize_t readlink(int fd,const char *restrict pathname,char *restrict buf,size_t bufsize)

16.文件的时间

对每一个文件维护以下3个时间字段,如下所示:

字段 说明 例子 ls(1)选项
st_atim 文件数据的最后访问时间 read -u
st_mtim 文件数据的最后修改时间 write 默认
st_ctim i节点状态的最后更改时间 chmod、chown -c

17.函数mkdir、mkdirat和rmdir

用mkdir和mkdirat函数创建目录,用rmdir函数删除目录。

#include <sys/stat.h>
int mkdir(const char *pathname,mode_t mode);
int mkdirat(int fd,const char *pathname,mode_t mode);

使用rmdir可以删除一个空的目录,空的目录是只包含.和..这两项的目录。

#include <unistd.h>
int rmdir(const char *pathname)

18.读目录

#include <dirent.h>
DIR *opendir(const char *pathname);//返回路径对应的DIR结构
DIR * fdopendir(int fd);//返回文件描述符对应的DIR结构
//  成功返回指针,出错返回NULL
struct dirent *readdir(DIR *dp);
//若成功,返回指针,若在目录尾或者出错,返回NULL
void rewinddir(DIR * dp);
void closedir(DIR*dp) ;
//成功返回0,失败返回-1
long telldir(DIR *dp);
void seekdir(DIR *dp,long loc);
//返回与dp关联的目录中的当前位置

下面是一个示例程序,可以递归降序遍历文件的层次结构:

#include "apue.h"
#include <iostream>
#include <dirent.h>
#include <limits.h>
using namespace std;
typedef  int Myfunc(const char *,const struct stat *,int );
static  Myfunc myfunc;
static int myftw(char *,Myfunc *);
static int dopath(Myfunc *);
static long nreg,ndir,nblk,nchr,nfifo,nslink,nsock,ntot;

int main(int argc,char **argv){
    int ret;
    //保证被正确执行
    ){
        cout<<"usage: apue <starting-pathname>";
        exit(-);
    }
    //调用函数myftw
    ret = myftw(argv[],myfunc);
    ntot=nreg+ndir+nblk+nchr+nfifo+nslink+nsock;
    )//避免除0
        ntot=;
    //输出各种文件所占的比率
    printf("regular files = %7ld,%5.2f %%\n",nreg,nreg*100.0/ntot);
    printf("directories = %7ld,%5.2f %%\n",ndir,ndir*100.0/ntot);
    printf("block special = %7ld,%5.2f %%\n",nblk,nblk*100.0/ntot);
    printf("char special = %7ld,%5.2f %%\n",nchr,nchr*100.0/ntot);
    printf("FIFOs = %7ld,%5.2f %%\n",nfifo,nfifo*100.0/ntot);
    printf("symbolic links = %7ld,%5.2f %%\n",nslink,nslink*100.0/ntot);
    printf("sockets = %7ld,%5.2f %%\n",nsock,nsock*100.0/ntot);
    exit(ret);
}
/*
 * Descend through the hierarchy,starting at "pathname".
 *  the caller's func() is called for every file.
 */
#define FTW_F 1   //file other than directory
#define FTW_D 2   //directory
#define FTW_DNR 3 //directory that can't be read
#define FTW_NS 4  //file that we can't stat
static char *fullpath; //contains full func() returns
static size_t pathlen;
static int myftw(char *pathname,Myfunc *func){
    fullpath=path_alloc(&pathlen);//malloc PATH_MAX+1 bytes
    if(pathlen<= strlen(pathname)){
        pathlen=strlen(pathname)*;
        if((fullpath=(char *)realloc(fullpath,pathlen))==NULL){
            cout<<"realloc error!"<<endl;
        }
    }
    strcpy(fullpath,pathname);
    return dopath(func);
}
/*
 * Descend through the hierarchy,starting at "fullpath".
 * if "fullpath" is anything other than a directory,we lstat() it, //不是目录
 * call func(),and return.for a directory,we call ourself
 * recursively for each name in the directory
 */
static int dopath(Myfunc *func){//we return whatever func() returns
    struct stat statbuf;
    struct dirent *dirp;
    DIR *dp;
    int ret ,n;
    ){//stat error
        return (func(fullpath,&statbuf,FTW_NS));
    }
    ){//not a directory
        return (func(fullpath,&statbuf,FTW_F));
    }
    ){
        return ret;
    }
    n=strlen(fullpath);
    >pathlen){//expand path buffer
        pathlen*=;
        if((fullpath=(char *)realloc(fullpath,pathlen))==NULL){
            cout<<"realloc error!"<<endl;
        }
    }
    fullpath[n++]='/';
    fullpath[n]=;
    if((dp=opendir(fullpath))==NULL){// 不能打开目录
        return func(fullpath,&statbuf,FTW_DNR);
    }
    while((dirp=readdir(dp))!=NULL){
        ||strcmp(dirp->d_name,){
            continue;
        }//忽略. 和..
        strcpy(&fullpath[n],dirp->d_name);//append name after /
        )//recursive
            break;
    }
    fullpath[n-] =;
    ){
        cout<<"can't close directory "<<fullpath<<endl;
    }
    return ret;
}
static  int myfunc(const char *pathname,const struct stat *statptr,int type){
    switch (type){
        case FTW_F:
            switch (statptr->st_mode &S_IFMT){
                case S_IFREG:nreg++;break;
                case S_IFBLK:nblk++;break;
                case S_IFCHR:nchr++;break;
                case S_IFIFO:nfifo++;break;
                case S_IFLNK:nslink++;break;
                case S_IFSOCK:nsock++;break;
                case S_IFDIR:
                {
                    cout<<"for S_IFDIR for "<<pathname<<endl;
                    exit(-);
                }

            }
        break;
        case FTW_D:
            ndir++;
            break;
        case FTW_DNR:
            cout<<"can't read directory "<<pathname<<endl;
            break;
        case FTW_NS:
            cout<<"stat error for "<<pathname<<endl;
            break;
        default:
            cout<<"unknown type "<<type <<" for pathname "<<pathname<<endl;
            exit(-);

    }
    ;
}

运行示例:

19.函数chdir,fchdir和getcwd

  每个进程都有一个当前的工作目录,这个目录是搜索所有相对路径名的起点。当用户登录到UNIX系统的时候,其当前的工作目录通常是用户的home directory。

  调用函数chdir或者函数fchdir可以更改当前的工作目录。

#include <unistd.h>
int chdir(const char *pathname);
int fchdir(int fd);
//成功返回0,失败返回-1.

  通过函数getcwd获取当前工作路径。

#include <unistd.h>
char *getcwd(char *buf,size_t size);
//成功返回buf,失败返回NULL

示例程序:

#include "apue.h"
#include <iostream>

using namespace std;
int main(){
    char *ptr;
    size_t size;
    ){
        cout<<"chdir failed!"<<endl;
    }
    ptr=path_alloc(&size);
    if(getcwd(ptr,size)==NULL){
        cout<<"getcwd failed!"<<endl;
    }
    cout<<"cwd= "<<ptr<<endl;
    ;
}

运行输出:

cwd= /Users/zhouyang

apue chapter 4 文件和目录的更多相关文章

  1. Notes for Apue &mdash;&mdash; chapter 4 Files and Directories(文件和目录)

    4.1 Introduction 4.2 stat, fstat, fstatat, and lstat Functions The lstat function is similar to stat ...

  2. (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  3. APUE(4)---文件和目录 (3)

    十三.函数rename和renameat #include <stdio.h> int rename(const char *oldname, const char *newname); ...

  4. APUE(4)---文件和目录 (2)

    七.函数umask umask函数为进程设置文件模式创建屏蔽字,并返回之前的值,这是少数几个没有出错返回函数中的一个.其中cmask是9个常量(S_IR/W/XUSR.S_IR/W/XGRP.S_IR ...

  5. apue学习笔记(第四章 文件和目录)

    本章将描述文件系统的其他特性和文件的性质. 函数stat.fstat.fstatat和lstat #include <sys/stat.h> int stat(const char *re ...

  6. [APUE]文件和目录(中)

    一.link.unlink.remove和rename 一个文件可以有多个目录项指向其i节点.使用link函数可以创建一个指向现存文件连接 #include <unistd.h> int ...

  7. [APUE]文件和目录(上)

    一.文件权限 1. 各种ID 我在读这一章时遇到了各种ID,根据名字完全不清楚什么意思,幸好看到了这篇文章,http://blog.csdn.net/ccjjnn19890720/article/de ...

  8. [04]APUE:文件与目录

    [a] stat / lstat / fstat #include <sys/stat.h> int stat(const char *restrict pathname, struct ...

  9. C4 文件和目录:APUE 笔记

    C4: 文件和目录 本章主要讨论stat函数及其返回信息,通过修改stat结构字段,了解文件属性. struct stat结构定义如下: struct stat { __dev_t st_dev; / ...

随机推荐

  1. ASP.NET MVC 路由调试工具Router Debugger

    直接上图 安装好手动启用或关闭 我开始调试我之前程序中由于路由遇到的问题 当发出 http://localhost:2362/109.html 这样请求, 我的意图是想Article的Details被 ...

  2. makefile中引用其他makefile方法

    在Makefile中引用其他Makefile文件的方法是,使用inclue   filename.mk

  3. 【推荐】使用Jquery+EasyUI进行框架项目开发案例讲解之一---员工管理源码分享

    使用Jquery+EasyUI 进行框架项目开发案例讲解之一 员工管理源码分享   在开始讲解之前,我们先来看一下什么是Jquery EasyUI?jQuery EasyUI是一组基于jQuery的U ...

  4. int和integer;Math.round(11.5)和Math.round(-11.5)

    int是java提供的8种原始数据类型之一.Java为每个原始类型提供了封装类,Integer是java为int提供的封装类.int的默认值为0,而Integer的默认值为null,即Integer可 ...

  5. UVa202 Repeating Decimals

    #include <stdio.h>#include <map>using namespace std; int main(){    int a, b, c, q, r, p ...

  6. ReactiveSwift源码解析(七) Signal的CombineLatest的代码实现

    本篇博客我们就来聊一下combineLatest()的使用以及具体的实现方式.在之前的<iOS开发之ReactiveCocoa下的MVVM>的博客中我们已经聊过combineLatest( ...

  7. tp5命令行基础

    命令行工具需要在命令行下面执行,请先确保你的php.exe已经加入了系统环境变量Path. 要执行命令,首先进入命令行,并切换当前目录到应用的根目录(也就是think文件所在目录)下面,执行: php ...

  8. vmware安装centos7

    VMware下安装CentOS7.2 http://www.mamicode.com/info-detail-1455647.html centos7.2配置网络 http://blog.csdn.n ...

  9. 强大核心功能矩阵,详解腾讯云负载均衡CLB高可靠高性能背后架构

    1 前言 腾讯云负载均衡(Cloud LoadBalancer),简称CLB, 负载均衡通过设置虚拟服务地址(VIP)将来自客户端的请求按照指定方式分发到其关联的多台后端云服务器,服务器将请求的响应返 ...

  10. Findbug在项目中的运用--提高代码质量

     FindBugs是一个静态分析工具,它检查类或者 JAR文件,将字节码与一组缺陷模式进行对比以发现可能的问题.有了静态分析工具,就可以在不实际运行程序的情况对软件进行分析 第一 手动安装 在Ec ...