Linux -- stat, fstat, lstat, fstatat函数

20 篇文章 2 订阅
翻译自Ubuntu 19版本下的Linux Programmer's Manual
  1. 函数名:stat, fstat, lstat, fstatat - 获取文件的状态

  2. 概要:

       #include <sys/types.h>
       #include <sys/stat.h>
       #include <unistd.h>
       
       int stat(const char *pathname, struct stat *statbuf);
       int fstat(int fd, struct stat *statbuf);
       int lstat(const char *pathname, struct stat *statbuf);
       
       #include <fcntl.h>           /* AT_* 常数的定义*/
       #include <sys/stat.h>
       
       int fstatat(int dirfd, const char *pathname, struct stat *statbuf,int flags);
    
       glibc的特性测试宏需求(参考feature_test_macros(7)):
       lstat():
           /* glibc 2.19 and 早些时候的 */ _BSD_SOURCE
               || /* 从glibc 2.20之后 */ _DEFAULT_SOURCE
               || _XOPEN_SOURCE >= 500
               || /* 从2.10:之后 */ _POSIX_C_SOURCE >= 200112L
       fstatat():
          从glibc 2.10之后:
               _POSIX_C_SOURCE >= 200809L
          在 glibc 2.10之前:
               _ATFILE_SOURCE
    
  3. 描述:
      这些函数在statbuf指向的缓冲区中返回关于文件的信息。文件本身不需要任何权限,但是,对于stat()、fstatat()和lstat(),在pathname中指向文件的所有目录上都需要执行(搜索)权限。
      stat()和fstatat()检索路径名指向的文件信息;fstatat()的区别如下所述。
      lstat()与stat()相同,只是pathname如果是一个符号链接,那么它将返回关于链接本身的信息,而不是它所引用的文件。
      fstat()与stat()相同,只是要检索的文件信息由文件描述符fd指定。

    stat结构体
      所有这些系统调用都返回一个stat结构,其中包含以下字段:

    struct stat {
             dev_t     st_dev;         /* 包含该文件的设备ID */
             ino_t     st_ino;         /* 节点(Inode) 号 */
             mode_t    st_mode;        /* 文件类型与权限 */
             nlink_t   st_nlink;       /*硬链接数量 */
             uid_t     st_uid;         /* 拥有者的用户ID */
             gid_t     st_gid;         /* 拥有者的组ID */
             dev_t     st_rdev;        /* 设备号 (如果是特殊的文件) */
             off_t     st_size;        /* 总体大小, 以字节为单位 */
             blksize_t st_blksize;     /* 给文件系统I/O的块大小 */
             blkcnt_t  st_blocks;      /* 已分配的块数量,以512字节为单位 */
    
        /* 从Linux 2.6开始,内核就支持以下时间戳字段的纳秒精度。
    有关Linux 2.6之前的详细信息,请参阅NOTES。*/
    
             struct timespec st_atim;  /*上一次的访问时间戳 */
             struct timespec st_mtim;  /* 上一次修改的时间戳 */
             struct timespec st_ctim;  /* 上一次状态改变的时间戳*/
         #define st_atime st_atim.tv_sec      /*向后兼容*/
         #define st_mtime st_mtim.tv_sec
         #define st_ctime st_ctim.tv_sec
    };
    

    timespec结构体数据类型:

    struct timespec {
            time_t  tv_sec;         /* 秒 */
            long    tv_nsec;        /* 纳秒 */
    };
    

    stat结构体数据类型:

    struct stat {
        unsigned long   st_dev;     /* Device.  */
        unsigned long   st_ino;     /* File serial number.  */
        unsigned int    st_mode;    /* File mode.  */
        unsigned int    st_nlink;   /* Link count.  */
        unsigned int    st_uid;     /* User ID of the file's owner.  */
        unsigned int    st_gid;     /* Group ID of the file's group. */
        unsigned long   st_rdev;    /* Device number, if device.  */
        unsigned long   __pad1;
        long        st_size;    /* Size of file, in bytes.  */
        int     st_blksize; /* Optimal block size for I/O.  */
        int     __pad2;
        long        st_blocks;  /* Number 512-byte blocks allocated. */
        long        st_atime;   /* Time of last access.  */              
        unsigned long   st_atime_nsec;
        long        st_mtime;   /* Time of last modification.  */
        unsigned long   st_mtime_nsec;
        long        st_ctime;   /* Time of last status change.  */
        unsigned long   st_ctime_nsec;
        unsigned int    __unused4;
        unsigned int    __unused5;
    };
    	}
    

    注意:
       在不同的体系结构中,stat结构中字段的顺序有所不同。此外,上面的定义没有显示各种体系结构上某些字段之间可能存在的填充字节。如果需要了解详细信息,请参考glibc和内核源代码。
    注意:
      出于性能和简单性的原因,stat结构中的不同字段可能包含系统调用执行期间不同时刻的状态信息。例如,如果st_mode或st_uid被另一个进程通过调用chmod(2)或chown(2)来更改,stat()可能会返回旧的st_mode和新st_uid,或者旧的st_uid和新st_mode。
    stat结构中的字段如下:
      st_dev:这个字段描述这个文件存放在哪一个设备中。(major(3)和minor(3)宏可能对分解该字段中的设备ID有用。)
      st_ino:这个字段包含文件的节点(inode)号。
      st_mode:这个字段包含文件类型和权限。更多信息请参见inode(7)。
      st_nlink:这个字段包含文件的硬链接数。
      st_uid:这个字段包含该文件拥有者的用户ID。
      st_gid:这个字段包含该文件拥有者的组ID。
      st_rdev:这个字段描述了该文件(inode)所代表的设备。
      st_size:这个字段提供了文件的大小,以字节为单位(如果这是一个常规文件或一个符号链接)。符号链接的大小是它包含的路径名的长度,不包含’\0’这个字节。
      st_blksize:这个字段为有效文件系统I / O提供了“首选”块大小(以字节为单位),一般该值为4096。
      st_blocks:这个字段表示分配给文件块的数量,以512字节为单位。(当文件有空洞时,这个值可能小于st_size/512。)
      st_atime:这是文件上一次的访问时间戳。
      st_mtime:这是文件上一次的修改时间戳。
      st_ctime:这是文件上一次状态改变的时间戳。
      有关上述字段的进一步信息,请参见inode(7)。

    fstatat()
      fstatat()系统调用是访问文件信息的更通用的接口,它仍然可以准确地提供stat()、lstat()和fstat()中的每一个的特性。
      如果pathname中给出的路径名是相对的,那么它将被解释为相对于文件描述符dirfd所引用的目录(而不是相对于调用进程的当前工作目录,就像stat()和lstat()对相对路径名所做的那样)。
      如果pathname是相对的,那么dirfd的值指定为AT_FDCWD,然后pathname被解释为相对于调用进程的当前工作目录(与stat()、lstat()一样)。
    如果路径名是绝对的,则忽略dirfd。
      flags可以是0,也可以包含以下一个或多个标志“或”运算:
    AT_EMPTY_PATH (从 Linux 2.6.39之后)
      如果pathname是一个空字符串,对dirfd引用的文件进行操作(可以使用open(2) O_PATH标志获得该文件)。在这种情况下,dirfd可以引用任何类型的文件,而不仅仅是目录,fstatat()的行为类似于fstat()。如果dirfd 是 AT_FDCWD,在当前工作目录调用操作。这个标志是Linux特有的;定义_GNU_SOURCE以获得它的定义。
    AT_NO_AUTOMOUNT (从 Linux 2.6.38之后)
      如果目录是一个自动挂载点,pathname不要自动挂载终端(“basename”)组件。这允许调用者收集自动挂载点的属性(而不是它将挂载的位置)。从Linux 4.14开始,也不要在按需目录中实例化不存在的名称,比如用于自动加载器间接映射的目录。此标志可用于扫描目录的工具中,以防止自动挂载点目录的大规模自动挂载。如果挂载点已经挂载,则AT_NO_AUTOMOUNT标志无效。这个标志是Linux特有的;定义_GNU_SOURCE以获得它的定义。stat()和lstat()的作用就像设置了AT_NO_AUTOMOUNT一样。
    AT_SYMLINK_NOFOLLOW
      如果pathname是一个符号链接,不要取消对它的引用:而是返回关于链接本身的信息,比如lstat()。(默认情况下,fstatat()取消引用符号链接,比如stat()。)
    有关fstatat()需求的说明,请参见openat(2)。

  4. 返回值:
    成功时,返回零。在出现错误时,返回-1,并适当地设置errno。

  5. ERRORS
    EACCES
      路径名的路径前缀中的一个目录的搜索权限被拒绝。(参见path_resolution (7))。
    EBADF
      fd不是一个有效的打开文件描述符。
    EFAULT
      错误的地址。
    ELOOP
      在遍历路径时遇到太多符号链接。
    ENAMETOOLONG
      路径名太长。
    ENOENT
      pathname的组件不存在,或者pathname是一个空字符串,并且标记中没有指定AT_EMPTY_PATH。
    ENOMEM
      内存不足(也就是内核内存)。
    ENOTDIR
      pathname的路径前缀的组件不是目录。
    EOVERFLOW
      路径名或fd引用文件的大小、inode号或块数不能分别用off_t、ino_t或blkcnt_t类型表示。例如,当在32位平台上编译的应用程序在没有-D_FILE_OFFSET_BITS=64的情况下调用大小超过(1<<31)-1字节的文件的stat()时,可能会发生此错误。
    fstatat()可能会出现以下附加错误:
    EBADF
      dirfd不是一个有效的文件描述符。
    EINVAL
      在flags中指定的无效标志。
    ENOTDIR
      路径名是相对的,dirfd是一个文件描述符,引用的文件不是目录。

  6. st_mode 宏标志位,数值以8进制表示:

    /*文件类型标志*/
    #define S_IFMT  00170000  	/*文件类型判断掩码*/
    #define S_IFSOCK 0140000  	/*套接字文件 s */
    #define S_IFLNK	 0120000    /*符号链接文件l */
    #define S_IFREG  0100000    /*普通文件 - */
    #define S_IFBLK  0060000    /*块设备文件 b */
    #define S_IFDIR  0040000    /*目录文件 d */
    #define S_IFCHR  0020000  	/*字符设备文件 c */
    #define S_IFIFO  0010000    /*管道文件 p */
    /*判断文件类型宏*/
    #define S_ISLNK(m)	(((m) & S_IFMT) == S_IFLNK)  /*判断是否是符号链接文件*/
    #define S_ISREG(m)	(((m) & S_IFMT) == S_IFREG)  /*判断是否是普通文件*/
    #define S_ISDIR(m)	(((m) & S_IFMT) == S_IFDIR)  /*判断是否是目录文件*/
    #define S_ISCHR(m)	(((m) & S_IFMT) == S_IFCHR)  /*判断是否是字符设备*/
    #define S_ISBLK(m)	(((m) & S_IFMT) == S_IFBLK)  /*判断是否是块设备*/
    #define S_ISFIFO(m)	(((m) & S_IFMT) == S_IFIFO)  /*判断是否是管道文件*/
    #define S_ISSOCK(m)	(((m) & S_IFMT) == S_IFSOCK) /*判断是否是套接字文件*/
    /*******************************************************************/
    #define S_ISUID  0004000  /*S位,设置用户ID,使可执行文件在执行阶段具有UID的权限*/
    #define S_ISGID  0002000  /*G位,设置组ID位,使可执行文件在执行阶段具有GID的权限*/
    #define S_ISVTX  0001000  /*T(粘着位),使文件只能被该文件的UID用户或者root用户删除。
    /*权限标志*/
    #define S_IRWXU 00700  /*用户(所有者)具有读、写和执行*/
    #define S_IRUSR 00400  /*用户(所有者)具有读*/
    #define S_IWUSR 00200  /*用户(所有者)具有写*/
    #define S_IXUSR 00100  /*用户(所有者)具有执行*/
    #define S_IRWXG 00070  /*组具有读、写和执行*/
    #define S_IRGRP 00040  /*组具有读*/
    #define S_IWGRP 00020  /*组具有写*/
    #define S_IXGRP 00010  /*组具有执行*/
    #define S_IRWXO 00007  /*其他用户具有读、写和执行*/
    #define S_IROTH 00004  /*其他用户具有读*/
    #define S_IWOTH 00002  /*其他用户具有写*/
    #define S_IXOTH 00001  /*其他用户具有执行*/
    
  7. 例子
    下面的程序调用lstat()并在返回的stat结构中显示选定的字段。

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <time.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/sysmacros.h>
    int main(int argc, char *argv[])
    {
       struct stat sb;
    
       if (argc != 2) {
    	   fprintf(stderr, "Usage: %s <pathname>\n", argv[0]);
    	   exit(EXIT_FAILURE);
       }
    
       if (lstat(argv[1], &sb) == -1) {
    	   perror("lstat");
    	   exit(EXIT_FAILURE);
       }
    
    	printf("ID of containing device:  [%lx,%lx]\n",
    		 (long) major(sb.st_dev), (long) minor(sb.st_dev));
    
    	printf("File type:                ");
    
       switch (sb.st_mode & S_IFMT) {
       case S_IFBLK:  printf("block device\n");            break;
       case S_IFCHR:  printf("character device\n");        break;
       case S_IFDIR:  printf("directory\n");               break;
       case S_IFIFO:  printf("FIFO/pipe\n");               break;
       case S_IFLNK:  printf("symlink\n");                 break;
       case S_IFREG:  printf("regular file\n");            break;
       case S_IFSOCK: printf("socket\n");                  break;
       default:       printf("unknown?\n");                break;
       }
    
       printf("I-node number:            %ld\n", (long) sb.st_ino);
    
       printf("Mode:                     %lo (octal)\n",
    		   (unsigned long) sb.st_mode);
    			printf("Last status change:       %s", ctime(&sb.st_ctime));
       printf("Last file access:         %s", ctime(&sb.st_atime));
       printf("Last file modification:   %s", ctime(&sb.st_mtime));
    
       exit(EXIT_SUCCESS);
    }
    
  8. 扩展:

    #include <sys/types.h>
    #include <pwd.h>
    struct passwd *getpwnam(const char *name);
    struct passwd *getpwuid(uid_t uid);
    

    getpwnam()函数的作用是:返回一个指向passwd结构体指针,该结构体中包含密码数据(例如,本地密码文件/etc/passwd、NIS和LDAP)中与用户名匹配记录的字段。
    getpwuid()函数的作用是: 返回一个指向passwd结构体指针,该结构体中包含密码数据与用户ID号(uid)匹配记录的字段。
    passwd结构在pwd.h中定义如下:

    struct passwd {
      	char   *pw_name;       /* 用户名 */
        char   *pw_passwd;     /* 用户密码*/
        uid_t   pw_uid;        /*用户ID */
        gid_t   pw_gid;        /* 组ID */
        char   *pw_gecos;      /* 用户信息 */
        char   *pw_dir;        /* 家目录 */
        char   *pw_shell;      /* shell程序 */
    };
    

    返回值:getpwnam() 和 getpwuid() 函数返回一个指向passwd结构的指针,如果没有找到匹配的条目或发生错误,则返回NULL。如果发生错误,则适当地设置errno。如果想在调用后检查errno,应该在调用前将errno设置为零。
    举例:

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/sysmacros.h>
    #include <pwd.h>
    
    int main(int argc, char *argv[])
    {
       	struct stat sb;
    	struct passwd *getpwd;
       if (argc != 2) {
    	   fprintf(stderr, "Usage: %s <pathname>\n", argv[0]);
    	   exit(EXIT_FAILURE);
       }	
       if (lstat(argv[1], &sb) == -1) {
    	   perror("lstat");
    	   exit(EXIT_FAILURE);
       }
       if(NULL==(getpwd=getpwuid(sb.st_uid)))
       {
    	   perror("getpwuid");
    	   exit(EXIT_FAILURE);
       }
    	printf("User name: %s\n",getpwd->pw_name);
    	printf("User password: %s\n",getpwd->pw_passwd);
    	printf("User information: %s\n",getpwd->pw_gecos);
    	printf("Home directory: %s\n",getpwd->pw_dir);
    	printf("Shell program: %s\n",getpwd->pw_shell);
       exit(EXIT_SUCCESS);
    }
    
       #include <sys/types.h>
       #include <grp.h>
    
       struct group *getgrnam(const char *name);
       struct group *getgrgid(gid_t gid);
    

    函数的作用是:返回一个指向group结构的指针,该结构包含与组名称相匹配的组数据库中记录的分段字段(例如,本地组文件/etc/group、NIS和LDAP)。
    函数的作用是:返回一个指向group结构的指针,该结构包含与组ID(gid)匹配的组数据库中记录的分段字段。
    gruop结构体在pwd.h中定义如下:

     struct group {
         char   *gr_name;        /* 组名 */
         char   *gr_passwd;      /* 组密码 */
         gid_t   gr_gid;         /* 组ID */
         char  **gr_mem;         /* 指向组成员名称的指针(以'\0'结尾的数组) */
     };
    

    返回值:getgrnam() 和 getgrgid() 函数返回一个指向passwd结构的指针,如果没有找到匹配的条目或发生错误,则返回NULL。如果发生错误,则适当地设置errno。如果想在调用后检查errno,应该在调用前将errno设置为零。
    举例:

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/sysmacros.h>
    #include <grp.h>
    
    int main(int argc, char *argv[])
    {
       struct stat sb;
       struct group *getgr;
    
       if (argc != 2) {
    	   fprintf(stderr, "Usage: %s <pathname>\n", argv[0]);
    	   exit(EXIT_FAILURE);
       }
    
       if (lstat(argv[1], &sb) == -1) {
    	   perror("lstat");
    	   exit(EXIT_FAILURE);
       }
    
       if(NULL==(getgr=getgrgid(sb.st_gid)))
       {
    	   perror("getpwuid");
    	   exit(EXIT_FAILURE);
       }
    
    	printf("Group name: %s\n",getgr->gr_name);
    	printf("Group password: %s\n",getgr->gr_passwd);
    	printf("Group ID: %d\n",getgr->gr_gid);
    	printf("Group member: %s\n",*getgr->gr_mem);
    
       exit(EXIT_SUCCESS);
    }
    
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值