stat() 提供错误信息

stat() giving wrong information

我正在使用循环打印目录中每个文件的信息,以将 ls shell 函数重新创建为 C 程序。当将来自程序的信息与来自 ls 命令的正确信息进行比较时,来自程序(使用 stat())的结果是非常错误的。

这是我的程序的所有代码:

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char* params[])
{
  void printTable(char filepath[], int s, int b);

    // If no modifiers, send error
    if(argc == 1) {
      printf("Error: no directory specified. \n");
      exit(1);
      return 0;
    }
    // If only a directory is provided
    if(argc ==2) {
      printTable(params[1], 0, 0);
    } // end of argc == 2

    // If there are 4 modifiers
    else if(argc == 4) {
    }
    return 0;
}

void printTable (char filepath[], int s, int b) {
  DIR *dp;
  struct dirent *dir;
  struct stat fileStats;
  if((dp = opendir(filepath)) == NULL) {
    fprintf(stderr, "Cannot open directory. \n");
    exit(1);
  }
  printf("Processing files in the directory: %s\n", filepath);
  printf("inode \t Type \t UID \t GID \t SIZE \t       Filename \t                 Modification date \n");
  while((dir = readdir(dp)) != NULL ) {
    if(dir->d_ino != 0 && fileStats.st_ino > 0 && stat(dir->d_name, &fileStats) < 0) {
      // Print the inode
      printf("%d \t ", fileStats.st_ino);
      // Print type
      if(dir->d_type == DT_BLK)
        printf("BLK \t ");
      else if(dir->d_type == DT_CHR)
        printf("CHR \t ");
      else if(dir->d_type == DT_DIR)
        printf("DIR \t ");
      else if(dir->d_type == DT_FIFO)
        printf("FIFO \t ");
      else if(dir->d_type == DT_LNK)
        printf("LNK \t ");
      else if(dir->d_type == DT_SOCK)
        printf("SOCK \t ");
      else if(dir->d_type == DT_REG)
        printf("REG \t ");
      else
        printf("UNKOWN \t ");
      // Print UID
      printf("%d \t ", fileStats.st_uid);
      // Print GID
      printf("%d \t ", fileStats.st_gid);
      // Print SIZE
      printf("%jd bytes \t ", fileStats.st_size);
      // Print file name
      printf("%25s \t ", dir->d_name);
      // Print date modified
      printf("%20s \n", ctime(&fileStats.st_mtime));
    }
  }
  struct tm *lt = localtime(&fileStats.st_mtime);
  int diff = difftime(time(NULL), mktime(lt));
  printf("%d \n", diff);
  closedir(dp);
}

下面是shell命令的结果(该目录与本程序无关): shell output

这是 运行 程序的结果:

stat(2) returns 0 成功,但您正在测试是否 stat(dir->d_name, &fileStats) < 0。因此,只要 stat(2) 未能 实际统计文件,您的代码就会打印一些内容。此外,条件链接的方式,fileStats.st_ino > 0 是一个在第一次迭代中涉及未初始化变量的表达式。

stat(2) 失败的最可能原因是您没有列出当前目录。 dirent结构中的d_name成员是目录中的文件名,不是完整路径,而是相对于目录路径的路径。当您将它传递给 stat(2) 时,只有当文件位于进程的当前工作目录 (cwd) 中时,它才会起作用。如果不是,就像您的情况一样,stat(2) 调用将失败。您需要做的是通过将 filepathdir->d_name 与路径分隔符 / 连接起来来构建完整路径:

char fullpath[PATH_MAX];

sprintf(fullpath, "%s/%s", filepath, dir->d_name);
stat(fullpath, &fileStats);

或者,也可以使用 chdir(2) 在循环之前更改工作目录:

char olddir[PATH_MAX];

// Save current working directory
getcwd(olddir, PATH_MAX);
// Change working directory to `filepath`
chdir(filepath);

// Do your loop here

// Restore old working directory
chdir(olddir);

现在 stat(2) 可以正确使用相对路径,即仅文件名。

stat(dir->d_name, &fileStats) < 0

应该是

stat(dir->d_name, &fileStats) == 0

只有在出现错误时才打印内容。

stat returns:

On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

如果你的平台对POSIX 2008有足够的支持,你可以使用fstatat() and dirfd()功能,效果不错。这至少适用于当前版本的 BSD、macOS、Linux、AIX、HP-UX、Solaris。

此代码与问题中的代码类似,但它不会尝试复制解码文件类型等的方式。

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    for (int i = 1; i < argc; i++)
    {
        const char *dir = argv[i];
        DIR *dp = opendir(dir);
        if (dp == 0)
        {
            fprintf(stderr, "%s: failed to open directory %s: %d %s\n",
                    argv[0], dir, errno, strerror(errno));
            continue;
        }
        /* dirfd(): POSIX 2008 - OK on BSD, macOS, Linux, AIX, HP-UX, Solaris */
        int fd = dirfd(dp);
        struct dirent *file;
        while ((file = readdir(dp)) != 0)
        {
            struct stat sb;
            /* 0 argument could be AT_SYMLINK_NOFOLLOW */
            if (fstatat(fd, file->d_name, &sb, 0) == 0)
            {
                /* Some of the conversion specifiers may be incorrect for some systems */
                /* Inode number, size, time in particular */
                printf("%10llu  %5d  %5d  %7o  %3d  %9lld  %10ld  %10ld  %10ld  %s/%s\n",
                       sb.st_ino, sb.st_uid, sb.st_gid, sb.st_mode, sb.st_nlink, sb.st_size,
                       sb.st_mtime, sb.st_atime, sb.st_ctime, dir, file->d_name);
            }
            else
                fprintf(stderr, "%s: failed to stat() %s/%s: %d %s\n",
                    argv[0], dir, file->d_name, errno, strerror(errno));
        }
        closedir(dp);
    }

    return 0;
}

请注意,这根本不需要(因此不需要)使用 chdir()。使用 fstatat() 允许您指定 "interpret the file name relative to the given directory",其中目录由文件描述符标识。

如果有合适的错误报告包(这样程序的名称将自动包含在错误中),我会将循环体打包为一个函数。但是,错误消息中的 argv[0] 引用会带来不便。对于错误报告,我会使用 SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c and stderr.h in the src/libsoq 子目录中可用的代码。

示例输出(来自 Mac — 10 位 inode 编号比大多数系统上的要大):

$ at67 bin ~/src/ule
4296808809    501     20    40755   33       1056  1577029162  1583165629  1577029162  bin/.
4296808746    501     20    40755  208       6656  1583164216  1583165629  1583164216  bin/..
4296811200    501     20   100755    1       1266  1515986057  1582224384  1582216636  bin/rfn
4296811205    501     20   100755    1       1266  1515986057  1583164235  1582216636  bin/rfn-c
4305347192    501     20   100755    1        246  1524096284  1582224384  1582216636  bin/soqvg
4297537255    501     20   100755    1       3813  1579639563  1582830967  1582216636  bin/pipe-rot
4296811199    501     20   100755    1        233  1515695843  1582224384  1582216636  bin/sow
4298720660    501     20   100755    1        627  1517875149  1582224384  1582216636  bin/so-getchar
4296811201    501     20   100755    1        218  1515695843  1582224384  1582216636  bin/ddpr
4296811210    501     20   100755    1       1266  1515986057  1582224384  1582216636  bin/rfn-pl
4296808811    501     20   100644    1        490  1510874880  1578595253  1510874880  bin/README.md
4296811204    501     20   100755    1       2278  1515695843  1582224384  1582216636  bin/fixin
4296811203    501     20   100755    1       2835  1576997332  1582224384  1582216636  bin/so-books
4296811196    501     20   100755    1        617  1515695843  1582224388  1582216636  bin/wso
4296811197    501     20   100755    1         85  1515695843  1583165629  1582216636  bin/so
4296808810    501     20   100644    1         92  1510874880  1579561480  1510874880  bin/.gitignore
4296811193    501     20   100755    1        200  1515695843  1582224388  1582216636  bin/posixcmd
4296811206    501     20   100755    1       1266  1515986057  1582224384  1582216636  bin/rfn-h
4451766334    501     20   100755    1        507  1576997332  1582224384  1582216636  bin/so-esql
4297012073    501     20   100755    1        937  1582216633  1583164235  1582216636  bin/sscce
4296811202    501     20   100755    1        522  1515695843  1582224384  1582216636  bin/so-late
4296811209    501     20   100755    1       1266  1515986057  1582224384  1582216636  bin/rfn-sql
4297507309    501     20   100755    1        848  1526264352  1582224384  1582216636  bin/so-stderr
4296811194    501     20   100755    1        206  1515695843  1582224388  1582216636  bin/posixfun
4342190418    501     20   100755    1       1227  1541833786  1582224384  1582216636  bin/so-quotes
4298078558    501     20   100755    1        722  1515695843  1582224384  1582216636  bin/soa
4296811198    501     20   100755    1         92  1515695843  1582224384  1582216636  bin/sops
4356366344    501     20   100755    1        454  1545845134  1582644937  1582216636  bin/so-reformat-c
4296811208    501     20   100755    1       1266  1515986057  1582224384  1582216636  bin/rfn-cpp
4298720661    501     20   100755    1        700  1576997332  1582224384  1582216636  bin/so-c-reserved
4296811207    501     20   100755    1       1266  1515986057  1582656633  1582216636  bin/rfn-sh
4296811195    501     20   100755    1        255  1515695843  1582224388  1582216636  bin/posixhdr
4451855327    501     20   100755    1        780  1577029658  1582224384  1582216636  bin/so-dotarrow
4296916142    501     20    40755   15        480  1574117045  1583165629  1574117045  /Users/jonathanleffler/src/ule/.
4296713088    501     20    40755   97       3104  1575746582  1583165539  1575746582  /Users/jonathanleffler/src/ule/..
4296917945    501     20   100444    1          7  1473056744  1578608259  1510993969  /Users/jonathanleffler/src/ule/ule-test-nnl
4296917947    501     20   100644    1       6148  1418098863  1578608259  1510994007  /Users/jonathanleffler/src/ule/.DS_Store
4296917957    501     20   100755    1      44824  1473056806  1578608437  1513032846  /Users/jonathanleffler/src/ule/ule-v1.4
4296917948    501     20   100444    1         15  1473056679  1578608259  1510993969  /Users/jonathanleffler/src/ule/ule-test-nul
4296917949    501     20   100640    1         43  1418023230  1578608259  1510993969  /Users/jonathanleffler/src/ule/ule-test-mix
4296917951    501     20   100644    1        745  1436058130  1578608259  1510993969  /Users/jonathanleffler/src/ule/ule.notes
4296917952    501     20   100640    1         33  1418023230  1578608259  1510993969  /Users/jonathanleffler/src/ule/ule-test-unx
4296917953    501     20   100640    1         33  1418023230  1578608259  1510993969  /Users/jonathanleffler/src/ule/ule-test-dos
4296917954    501     20    40755   11        352  1541802114  1578608839  1541802114  /Users/jonathanleffler/src/ule/RCS
4441180506    501     20   100444    1       6726  1574116649  1578608259  1574117045  /Users/jonathanleffler/src/ule/ule.c
4296917956    501     20    40755    3         96  1437532230  1578611808  1510994007  /Users/jonathanleffler/src/ule/ule.dSYM
4297282884    501     20   100755    1      60160  1513033250  1582552763  1513033250  /Users/jonathanleffler/src/ule/ule
4296917958    501     20   100640    1         30  1425237329  1578608259  1510993969  /Users/jonathanleffler/src/ule/ule-test-mac
$

如果您的系统没有 dirfd() 但有 fstatat(),您或许可以使用这些函数来打开和关闭目录。 (上面代码的早期修订版使用了此代码的变体——但配置它很快就会变得混乱,dirfd() 使这一切都变得不必要了。)。您可以使用 dir_open(dir) 代替 dirfd(dp),并在 closedir(dp) 之前或之后添加 dir_close(fd)

#include <fcntl.h>
#include <unistd.h>

int dir_open(const char *name)
{
    return open(name, O_RDONLY | O_DIRECTORY);
}

int dir_close(int fd)
{
    return close(fd);
}

如果您的系统有 O_SEARCH,请使用它代替 O_RDONLYO_SEARCH 在 macOS Mojave 10.14.6 上不可用,例如,我在其中进行了测试) .如果您的系统没有 O_DIRECTORY,请忽略该术语。这些只是使配置变得混乱的一些细节。

总的来说,如果你有 fstatat(),你很可能也会有 dirfd()