zoukankan      html  css  js  c++  java
  • 《UNIX环境高级编程》笔记——4.文件和目录

    一.引言

      本章描述文件系统的其他特征和文件的性质。有些背景知识需要注意,例如用户ID与文件权限、文件系统等。

    二.函数stat、fstat、fstatat和lstat

    #include <sys/stat.h>
    int stat( const char * restrict pathname, struct stat * restrict buf );
    int fstat( int fd, struct stat * buf );
    int lstat( const char * restrict pathname,struct stat * restrict buf );
    int fstatat( int fd, const char * restrict pathname, struct stat *restrict buf,int flag );

    返回值:成功,0
        出错,-1

      以上四个函数都是为了获取文件的stat结构体,稍有差别:

    • stat和fstat:stat依据文件名,fstat依据fd
    • stat和lstat:对于符号链接,stat返回该符号链接指向的文件,lstat返回符号链接本身;
    • fstatat:相对当前打开的目录(fd)的目录经。pathname为相对路径且fd=AT_FDCWD,则会计算相对于当前目录的正真路径;如果pathname是绝对路径,fd参数被忽略。 flag控制是否跟随符号链接,默认跟随,AT_SYMLINK_NOFOLLOW时,不跟随。

      stat是很重要的结构体,如下。ls -l 时会显示很多stat的内容。后面很多函数会用到这个结构体。

    struct stat
      {
        __dev_t st_dev;            /* Device.  文件系统的设备号*/
        unsigned short int __pad1;
    #ifndef __USE_FILE_OFFSET64
        __ino_t st_ino;            /* File serial number. i-node number    */
    #else
        __ino_t __st_ino;            /* 32bit file serial number.    */
    #endif
        __mode_t st_mode;            /* File mode.  */
        __nlink_t st_nlink;            /* Link count.  */
        __uid_t st_uid;            /* User ID of the file's owner.    */
        __gid_t st_gid;            /* Group ID of the file's group.*/
        __dev_t st_rdev;            /* Device number, if device. device设备的major和minor号 */
        unsigned short int __pad2;
    #ifndef __USE_FILE_OFFSET64
        __off_t st_size;            /* Size of file, in bytes.  */
    #else
        __off64_t st_size;            /* Size of file, in bytes.  */
    #endif
        __blksize_t st_blksize;        /* Optimal block size for I/O. 操作IO时的最优block大小 */
    
    #ifndef __USE_FILE_OFFSET64
        __blkcnt_t st_blocks;        /* Number 512-byte blocks allocated. */
    #else
        __blkcnt64_t st_blocks;        /* Number 512-byte blocks allocated. */
    #endif
    #if defined __USE_MISC || defined __USE_XOPEN2K8
        /* Nanosecond resolution timestamps are stored in a format
           equivalent to 'struct timespec'.  This is the type used
           whenever possible but the Unix namespace rules do not allow the
           identifier 'timespec' to appear in the <sys/stat.h> header.
           Therefore we have to handle the use of this header in strictly
           standard-compliant sources special.  */
        struct timespec st_atim;        /* Time of last access.  */
        struct timespec st_mtim;        /* Time of last modification.  */
        struct timespec st_ctim;        /* Time of last status change.  */
    # define st_atime st_atim.tv_sec    /* Backward compatibility.  */
    # define st_mtime st_mtim.tv_sec
    # define st_ctime st_ctim.tv_sec
    #else
        __time_t st_atime;            /* Time of last access.  */
        unsigned long int st_atimensec;    /* Nscecs of last access.  */
        __time_t st_mtime;            /* Time of last modification.  */
        unsigned long int st_mtimensec;    /* Nsecs of last modification.  */
        __time_t st_ctime;            /* Time of last status change.  */
        unsigned long int st_ctimensec;    /* Nsecs of last status change.  */
    #endif
    #ifndef __USE_FILE_OFFSET64
        unsigned long int __unused4;
        unsigned long int __unused5;
    #else
        __ino64_t st_ino;            /* File serial number.    */
    #endif
      };

    三.文件类型  stat.st_mode

     1.文件类型

     (1) regular file,普通文件

     (2) directory file,目录文件。目录文件中包含其他文件的名字和与这些文件有关的指针,也就是后面文件系统里说的“文件名”和“i-node”。对目录有度权限的进程可以读取目录内容,只要内核才能写,只能用系统调用才能更改目录

     (3) block special file, 块特殊文件,例如磁盘,待缓冲

     (4) char special file,字符特殊文件,不带缓冲。 系统中的设备,要么是块设备,要么是字符设备。

     (5) FIFO,进程间通讯用

     (6) socket,套接字,网络通信

     (7) symbolic link,符号链接,指向另一个文件

     2.相关宏定义

    以下宏判断文件类型, 传入的参数是

    #include <sys/stat.h>
    #define    __S_ISTYPE(mode, mask)    (((mode) & __S_IFMT) == (mask))
    
    #define    S_ISDIR(mode)     __S_ISTYPE((mode), __S_IFDIR)
    #define    S_ISCHR(mode)     __S_ISTYPE((mode), __S_IFCHR)
    #define    S_ISBLK(mode)     __S_ISTYPE((mode), __S_IFBLK)
    #define    S_ISREG(mode)     __S_ISTYPE((mode), __S_IFREG)
    #ifdef __S_IFIFO
    # define S_ISFIFO(mode)     __S_ISTYPE((mode), __S_IFIFO)
    #endif
    #ifdef __S_IFLNK
    # define S_ISLNK(mode)     __S_ISTYPE((mode), __S_IFLNK)
    #endif
    
    #if defined __USE_BSD && !defined __S_IFLNK
    # define S_ISLNK(mode)  0
    #endif
    
    #if (defined __USE_BSD || defined __USE_UNIX98 || defined __USE_XOPEN2K) 
        && defined __S_IFSOCK
    # define S_ISSOCK(mode) __S_ISTYPE((mode), __S_IFSOCK)
    #elif defined __USE_XOPEN2K
    # define S_ISSOCK(mode) 0
    #endif
    

     进程间通信(IPC)对象,如消息队列和信号灯,也可以说明为文件,如下宏可以判断这列文件的类型。注意,传入的参数是stat结构体指针

    /* These are from POSIX.1b.  If the objects are not implemented using separate
       distinct file types, the macros always will evaluate to zero.  Unlike the
       other S_* macros the following three take a pointer to a `struct stat'
       object as the argument.  */
    #ifdef    __USE_POSIX199309
    # define S_TYPEISMQ(buf) __S_TYPEISMQ(buf)    // 消息队列
    # define S_TYPEISSEM(buf) __S_TYPEISSEM(buf)   // 信号量
    # define S_TYPEISSHM(buf) __S_TYPEISSHM(buf)    // 共享存储对象
    #endif

    3.例程

     判断文件类型,并打印

    /* lseek test */
    #include <stdio.h>    // printf
    #include <stdlib.h>    // exit
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>    // errno
    #include <string.h>    // strerror
    #include <sys/stat.h>    // mode,stat函数
    
    int main(int args, char *argv[])
    {
        int i;
        struct stat fl_stat;
        
        if( args<2 ){
            printf("args error.
    ");
            exit(1);
        }        
        
        for( i=1;i<args;i++ ){
            printf("%s:",argv[i]);
            if( lstat(argv[i],&fl_stat) != 0 ){
                printf("lstat error.
    ");
                exit(1);        }    
            
            if( S_ISREG(fl_stat.st_mode) )
                printf("reguler file.
    ");
            else if( S_ISDIR(fl_stat.st_mode) )
                printf("directory file.
    ");
            else if( S_ISCHR(fl_stat.st_mode) )
                printf("char special file.
    ");    
            else if( S_ISBLK(fl_stat.st_mode) )
                printf("BLOCK special file.
    ");    
            else if( S_ISFIFO(fl_stat.st_mode) )
                printf("FIFO file.
    ");            
            else if( S_ISLNK(fl_stat.st_mode) )
                printf("symble link file.
    ");
            else if( S_ISSOCK(fl_stat.st_mode) )
                printf("socket file.
    ");
            else
                printf("unknow file.
    ");
        }    
            
        exit(0);
    }

    $./example * /dev/cdrom /dev
    example:reguler file.
    example.c:reguler file.
    example.o:reguler file.
    Makefile:reguler file.
    result:reguler file.
    /dev/cdrom:symble link file.
    /dev:directory file.

    四.SUID(设置用户ID)和SGID(设置组ID) stat.st_mode

     1. 进程相关的ID

        

    •  实际用户ID/实际组ID,系统登录者,登录期间一般不变;
    •  有效用户ID/组ID/附属组ID 用于文件访问权限检查。一般情况下  实际用户ID=有效用户ID,也有特例,后面说明。
    •  保存的设置用户ID/组ID,执行一个程序时,包含有效用户ID/组ID的副本。

     2. 文件所有者和组所有者

      文件有所有者和组所有者,用stat结构的st_uid/st_gid表示。

     3. 执行程序文件时的ID

       程序文件——执行——>变成进程,进程就有有效用户ID。

       情况1(通常情况):有效用户ID/组ID = 实际用户ID/组ID;例如登录者是login_name, 则程序文件执行以后,该进程的有效用户ID就是login_name;

       情况2(设置SUID/SGID):SUID/SGID包含在文件的stat的st_mode字段中,如果程序文件的属性里,设置了SUID/SGID,则该程序文件执行以后,该进程的有效用户ID/组ID=该程序文件的用户ID/组ID。可用S_ISUID/S_ISGID宏测试是否设置了这两个bit

      举例说明,以用户ID为例,组ID也类似, 为便于说明,uid使用用户名代替数字:

    登录者ID = login_name
    可执行文件exe_file的用户ID,uid = file_owner_name
    1.如果没有设置文件exe_file的SUID属性,则文件执行后,该进程的有效用户ID = 登录者ID(实际用户ID) = login_name,
    文件权限校验时用login_name校验。如果exe_file的other权限里没有x,则不能执行。
    2.如果设置文件exe_file的SUID属性,则文件执行后,该进程的有效用户ID = 该文件的UID = file_owner_name,
     文件权限校验时用file_owner_name,如果exe_file的所有者权限里没有x,则不能执行


    另一个典型的例子是 /etc/passed,该文件所有者是root,且设置了SUID,则其他用户执行时,有效进程ID=文件所有者ID=root,所以能够执行。

    五.文件访问权限 stat.st_mode

     1. 文件访问权限

    【注意】chmod 777 file,其中777是八进制,在编写程序时,应使用八进制。

     2. 权限与文件操作

     (1)打开文件对目录的权限要求

    open一个文件时,需要搜索目录,需要对目录具有执行权限。例如 /path/file,对/path必须有执行权限。

     【扩展】目录读权限与执行权限不同, 读权限允许读目录,获取所有的文件列表;执行权限允许对目录进行搜索,寻找特定文件。

     (2)读权限

     决定是否可以打开文件进行读操作,open的O_RDONLY和O_RDWR

     (3)写权限

     决定是否可以打开文件进行写操作,open的O_WRONLY和O_RDWR

     (4)O_TRUNC

     需写权限

     (5)创建新文件对目录的权限要求

     创建文件时,需要写目录,在目录中增加文件名和对应的i-node,所以对目录要有写权限

     (6)删除文件对目录的权限要求

     只删除目录里的文件信息,对目录有写+执行权限即可。对文件权限没有要求。

     (7)exec执行文件

     需执行权限。

     3. 文件访问权限测试

     每次打开、创建、删除文件时,内核都进行文件权限测试,是否具有权限决定于文件ID/组ID和进程有效ID/组ID。

    (1)root,有效用户ID=0,允许访问系统中所有文件

    (2)有效用户ID=文件所有者ID,则允许操作,具体是否有RWX全下, 要看对应的权限位是否设置。

    (3)有效组ID或附属组ID之一=文件组ID,允许

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

    以上4点按顺序检查,上一条不满足才检查下一条。

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

    open和creat新建函数时,没有指定各种ID,有默认的分配规则,linux的规则为:

    (1)新文件用户ID = 进程有效用户ID

     (2) 新文件组ID规则:

    if( 目录的SGID )
        新文件的GID=所在目录的GID
    else    
        新文件的GID=进程的有效GID

    七.函数access和faccessat

     前面提到权限测试都是按照进程的有效用户ID和有效组ID进行的,这两个函数用实际用户ID进行权限测试。

    #include <unistd.h>
    int access( const char * pathname, int mode );
    int faccessat(  int fd, const char * pathname, int mode, int flag);
    返回值:
        成功,0
        失败,-1        

    mode参数(可以按位或):
    F_OK:测试文件是否存在
    W_OK:测试写权限
    R_OK:测试读权限
    X_OK:测试执行权限
    faccessat的fd和pathname:
    pathname绝对路径,fd忽略
    fd为AT_FDCWD,pathname是相对路径
    其他:计算相对于打开目录fd的pathname
    faccessat的flag:
    可改变faccessat的行为,如果AT_EACCESS,则检查进程的有效用户ID和有效组ID,而不是实际用户ID和实际组ID。

     

    八.函数umask

    九.函数chmod、fchmod和fchmodat

    十.粘着位

    十一.函数chown、fchown、fchownat和lchown

    十二.文件长度

    十三.文件截断

    十四.文件系统

    十五.函数link、linkat、unlink、unlinkat和remove

    十六.函数rename和renameat

    十七.符号链接

    十八.创建和读取符号链接

    十九.文件的时间

    二十.函数futimens、utimensat、utimes

    二十一.函数mkdir、mkdirat和rmdir

    二十二.读目录

    二十三.函数chdir、fchdir和getcwd

    二十四.特殊设备文件

    二十五.文件访问权限位小结

    二十六.总结

  • 相关阅读:
    window.open和window.opener
    dict对象与QueryDict
    BeautifulSoup的一些方法
    ORM分组与聚合
    python-orm
    开发工具IDEA环境安装配置
    Greenplum介绍-table
    对package.json的理解和学习
    javaScript 的 map() reduce() foreach() filter()
    JSON的序列化和反序列化eval()和parse()方法以及stringfy()方法
  • 原文地址:https://www.cnblogs.com/liuwanpeng/p/6295935.html
Copyright © 2011-2022 走看看