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

  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:這個字段提供了文件的大小,以字節爲單位(如果這是一個常規文件或一個符號鏈接)。符號鏈接的大小是它包含的路徑名的長度,不包含NULL這個字節。
      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);
    }
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章