stat () дает неверную информацию - PullRequest
0 голосов
/ 02 марта 2020

Я использую al oop для печати информации о каждом файле в каталоге, чтобы воссоздать функцию оболочки ls как программу 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);
}

Вот результаты команды оболочки (этот каталог не имеет ничего общего с этой программой): вывод оболочки

Вот результаты запуска программы:

program output

Ответы [ 3 ]

2 голосов
/ 02 марта 2020

Если ваша платформа имеет достаточную поддержку POSIX 2008, вы можете использовать функции fstatat() и dirfd() для хорошего эффекта. Это будет работать на текущих версиях BSD, macOS, Linux, AIX, HP-UX, Solaris - как минимум.

Этот код похож на код в вопросе, но он не пытается повторить способ декодирования типа файла и т. д. c.

#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() позволяет указать «интерпретировать имя файла относительно заданного каталога», где каталог идентифицируется дескриптором файла.

При наличии подходящего пакета сообщений об ошибках (поэтому название программы будет включается в ошибки автоматически), я бы упаковал тело l oop как функцию. Однако ссылки argv[0] в сообщениях об ошибках делают это неудобным. Для отчетов об ошибках я бы использовал код, который доступен в моем репозитории SOQ (вопросы о переполнении стека) на GitHub в виде файлов stderr.c и stderr.h в подпункте src / libsoq -directory.

Пример вывода (от Ma c - номера инодов 10-ди git больше, чем в большинстве систем):

$ 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) и добавили dir_close(fd) до или после closedir(dp).

#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_RDONLY (O_SEARCH недоступен в MacOS Mojave 10.14.6, например, где я проводил тестирование). Если ваша система не имеет O_DIRECTORY, пропустите этот термин. Это лишь некоторые детали, которые усложняют настройку этого беспорядка.

В целом, если у вас есть fstatat(), у вас, вероятно, тоже будет dirfd().

1 голос
/ 02 марта 2020

stat(2) возвращает 0 в случае успеха, и все же вы проверяете, есть ли stat(dir->d_name, &fileStats) < 0. Таким образом, ваш код печатает что-то всякий раз, когда stat(2) не может фактически определить файл. Кроме того, при связывании условий fileStats.st_ino > 0 - это выражение, содержащее неинициализированную переменную в первой итерации.

Наиболее жизнеспособная причина сбоя stat(2) заключается в том, что вы не перечислите текущий каталог. Элемент d_name структуры dirent - это имя файла в каталоге, которое представляет собой не полный путь, а путь относительно пути к каталогу. Когда вы передаете его в stat(2), он будет работать, только если файл находится в текущем рабочем каталоге (cwd) процесса. Если это не так, как в вашем случае, вызов stat(2) не удастся. Что вам нужно сделать, это построить полный путь путем объединения filepath и dir->d_name с разделителем пути / между:

char fullpath[PATH_MAX];

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

Или, альтернативно, используйте chdir(2) для изменения рабочего каталог до l oop:

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) будет корректно работать с относительными путями, то есть только с именами файлов.

0 голосов
/ 02 марта 2020
stat(dir->d_name, &fileStats) < 0

должно быть

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

Вы печатаете содержимое только при наличии ошибки.

stat возвращает:

Вкл. успех, ноль возвращается. В случае ошибки возвращается -1 и значение errno устанавливается соответствующим образом.

...