1. 程式人生 > >獲取檔案狀態 (stat, lstat和fstat)

獲取檔案狀態 (stat, lstat和fstat)

獲取檔案狀態的系統呼叫有三個,分別是stat,fstat和lstat,其實他們的作用是一樣的,都是查詢某個檔案的狀態。如果查詢成功,會把檔案狀態的資訊填充在一個stat結構體中。他們的函式定義分別如下:

int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);

可以看到,這三個系統呼叫用於輸出的引數型別是一樣的,都是struct stat,其中,state與fstat的區別在於,stat()用檔名來指定要查詢的檔案,而fstat()用檔案描述符來指定目標檔案;而stat()與lstat()的區別在於,如果指定的檔案是一個符號連結,那麼stat會解引用,查詢該連結指向的普通檔案的屬性,而lstat()在遇到符號連結檔案是,不去解引用,而是直接返回這個符號連結檔案本身的屬性。要成功獲取到指定檔案的狀態資訊,需要使用者對檔案儲存位置的每一層目錄都具有執行和讀取許可權。

Linux的系統呼叫中還有很多類似的以 f 和 l 開頭的系統呼叫,他們的區別與作用和stat系列函式都是類似的。

返回引數struct stat的結構定義與各欄位的含義分別為:

struct stat {
               dev_t     st_dev;     /* 檔案存放的裝置ID */
               ino_t     st_ino;     /* 索引節點號 */
               mode_t    st_mode;    /* 檔案的屬性掩碼 */
               nlink_t   st_nlink;   /* 硬連結的數量 */
               uid_t     st_uid;     /* 檔案擁有者的使用者ID */
               gid_t     st_gid;     /* 檔案擁有者的組ID */
               dev_t     st_rdev;    /* 裝置ID,僅對部分特殊檔案有效 */
               off_t     st_size;    /* 檔案大小,單位位元組,軟連線檔案的大小是連結名長度 */
               blksize_t st_blksize; /* 檔案使用的儲存塊大小 */
               blkcnt_t  st_blocks;  /* 檔案佔用的儲存塊數量,以512位元組為單位 */
               time_t    st_atime;   /* 最後一次訪問的時間 */
               time_t    st_mtime;   /* 最後一次內容修改的時間 */
               time_t    st_ctime;   /* 最後一次狀態變化的時間 */
           };

對於st_mode欄位,系統定義了一些巨集來檢查檔案的型別:

           S_ISREG(m)  檢查是否是常規檔案

           S_ISDIR(m)  檢查是否是目錄

           S_ISCHR(m)  檢查是否是字元裝置 (如鍵盤)

           S_ISBLK(m)  檢查是否是塊裝置(如硬碟)

           S_ISFIFO(m) 是否是命名管道

           S_ISLNK(m)  是否是符號連結

           S_ISSOCK(m) 是否是套接字

與其他的很多系統呼叫一樣,這幾個系統呼叫都是成功是返回0,否則返回-1,並設定對應的errno,常見的可能出現的errno有:
       EACCES:目標檔案所在的目錄或某個上級目錄沒有查詢許可權

      EBADF:指定的檔案描述符無效.

       EFAULT:無效的檔案地址

       ELOOP:可能遇到了迴圈引用的軟連結檔案

       ENAMETOOLONG:檔案路徑名太長.

       ENOENT:目錄或檔案不存在

       ENOMEM:核心記憶體耗盡

       ENOTDIR:指定的檔案路徑上,某個部分不是目錄

       EOVERFLOW:引用的檔案太大了,或者使用的索引節點太多了,或者佔用的儲存塊太多了。

如下是使用stat()系統呼叫的演示程式:       

#include <sys/types.h>
       #include <sys/stat.h>
       #include <time.h>
       #include <stdio.h>
       #include <stdlib.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 (stat(argv[1], &sb) == -1) {
               perror("stat");
               exit(EXIT_FAILURE);
           }

           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("Link count:               %ld\n", (long) sb.st_nlink);
           printf("Ownership:                UID=%ld   GID=%ld\n",
                   (long) sb.st_uid, (long) sb.st_gid);

           printf("Preferred I/O block size: %ld bytes\n",
                   (long) sb.st_blksize);
           printf("File size:                %lld bytes\n",
                   (long long) sb.st_size);
           printf("Blocks allocated:         %lld\n",
                   (long long) sb.st_blocks);

           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);
       }