Печать прав доступа к файлам, таких как «ls -l», с использованием stat (2) в C

Я пытаюсь написать небольшую программу на C, которая эмулирует команду unix ls -l. Для этого я использую системный вызов stat(2) и столкнулся с небольшой заминкой при записи разрешений. У меня есть переменная mode_t, которая содержит права доступа к файлу из st_mode, и было бы несложно разобрать это значение в строковое представление s, но мне просто интересно, есть ли лучший способ сделать это, чем этот.


person cheezone    schedule 25.04.2012    source источник


Ответы (3)


пример из гугла

#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>

int main(int argc, char **argv)
{
    if(argc != 2)    
        return 1;

    struct stat fileStat;
    if(stat(argv[1], &fileStat) < 0)    
        return 1;

    printf("Information for %s\n", argv[1]);
    printf("---------------------------\n");
    printf("File Size: \t\t%d bytes\n", fileStat.st_size);
    printf("Number of Links: \t%d\n", fileStat.st_nlink);
    printf("File inode: \t\t%d\n", fileStat.st_ino);

    printf("File Permissions: \t");
    printf( (S_ISDIR(fileStat.st_mode)) ? "d" : "-");
    printf( (fileStat.st_mode & S_IRUSR) ? "r" : "-");
    printf( (fileStat.st_mode & S_IWUSR) ? "w" : "-");
    printf( (fileStat.st_mode & S_IXUSR) ? "x" : "-");
    printf( (fileStat.st_mode & S_IRGRP) ? "r" : "-");
    printf( (fileStat.st_mode & S_IWGRP) ? "w" : "-");
    printf( (fileStat.st_mode & S_IXGRP) ? "x" : "-");
    printf( (fileStat.st_mode & S_IROTH) ? "r" : "-");
    printf( (fileStat.st_mode & S_IWOTH) ? "w" : "-");
    printf( (fileStat.st_mode & S_IXOTH) ? "x" : "-");
    printf("\n\n");

    printf("The file %s a symbolic link\n", (S_ISLNK(fileStat.st_mode)) ? "is" : "is not");

    return 0;
}

результат:

Information for 2.c
---------------------------
File Size:              1223 bytes
Number of Links:        1
File inode:             39977236
File Permissions:       -rw-r--r--

The file is not a symbolic link
person askovpen    schedule 25.04.2012
comment
Спасибо за ответ. Это очень помогло. - person cheezone; 26.04.2012
comment
Обратите внимание, что поскольку код использует stat() вместо lstat(), единственный раз, когда он сообщит о «символической ссылке», это когда символическая ссылка не работает. В противном случае он сообщит о файле в конце символической ссылки. - person Jonathan Leffler; 26.01.2015
comment
хорошо, но пробелы после запятых делают его более читабельным, чем все, что сбито вместе. - person clearlight; 31.08.2018
comment
Как узнать имя владельца и группы, можно узнать? пожалуйста - person Krishna Acharya; 13.01.2021

Основы достаточно просты; хитрые биты — это биты SUID и SGID, а также липкий бит, который изменяет биты «x». Подумайте о том, чтобы разделить разрешения на 3 восьмеричных цифры для пользователя, группы и владельца и использовать их для индексирования массива трехсимвольных строк, таких как rwx и ---. Затем отрегулируйте соответствующие биты x на основе других битов режима. С типом файла нужно будет работать отдельно, но вы можете использовать 12-битный сдвиг вправо (возможно, с маскированием) и таблицу из 16 записей для работы с 16 возможными значениями (не все из которых допустимы в любой данной системе) . Или вы можете работать с известными типами, как показано в коде ниже.

+----+---+---+---+---+
|type|SSS|USR|GRP|OTH|
+----+---+---+---+---+

4 бита типа, три S-бита (setuid, setgid, sticky) и пользовательские, групповые и другие биты.

Это код, который я использую для преобразования mode_t в строку. Он был написан для красивой программы без потоков, поэтому использует статические данные; было бы тривиально изменить его, чтобы получить выходную строку в качестве входного параметра:

/* Convert a mode field into "ls -l" type perms field. */
static char *lsperms(int mode)
{
    static const char *rwx[] = {"---", "--x", "-w-", "-wx",
    "r--", "r-x", "rw-", "rwx"};
    static char bits[11];

    bits[0] = filetypeletter(mode);
    strcpy(&bits[1], rwx[(mode >> 6)& 7]);
    strcpy(&bits[4], rwx[(mode >> 3)& 7]);
    strcpy(&bits[7], rwx[(mode & 7)]);
    if (mode & S_ISUID)
        bits[3] = (mode & S_IXUSR) ? 's' : 'S';
    if (mode & S_ISGID)
        bits[6] = (mode & S_IXGRP) ? 's' : 'l';
    if (mode & S_ISVTX)
        bits[9] = (mode & S_IXOTH) ? 't' : 'T';
    bits[10] = '\0';
    return(bits);
}

static int filetypeletter(int mode)
{
    char    c;

    if (S_ISREG(mode))
        c = '-';
    else if (S_ISDIR(mode))
        c = 'd';
    else if (S_ISBLK(mode))
        c = 'b';
    else if (S_ISCHR(mode))
        c = 'c';
#ifdef S_ISFIFO
    else if (S_ISFIFO(mode))
        c = 'p';
#endif  /* S_ISFIFO */
#ifdef S_ISLNK
    else if (S_ISLNK(mode))
        c = 'l';
#endif  /* S_ISLNK */
#ifdef S_ISSOCK
    else if (S_ISSOCK(mode))
        c = 's';
#endif  /* S_ISSOCK */
#ifdef S_ISDOOR
    /* Solaris 2.6, etc. */
    else if (S_ISDOOR(mode))
        c = 'D';
#endif  /* S_ISDOOR */
    else
    {
        /* Unknown type -- possibly a regular file? */
        c = '?';
    }
    return(c);
}
person Jonathan Leffler    schedule 25.04.2012
comment
Оцените глубину вашего ответа! Узнавайте что-то новое каждый день! - person cheezone; 26.04.2012
comment
@ArranCudbard-Bell: я очищал корпус команд с помощью чего-то, аналогичного clang -Weverything, и временами это может быть немного болезненно. На самом деле я не пробовал clang -Weverything напрямую; это может быть менее обременительно, чем варианты, которые я использую (около 18 флагов -W*; -Wconversion - самая большая проблема для моего кода). - person Jonathan Leffler; 26.01.2015
comment
@JonathanLeffler -Weverthing, тогда -Wno- из действительно глупых предупреждений. Я думаю, что есть один, где он предупреждает вас, если вы используете случай по умолчанию в переключении на тип перечисления. Вы никогда не исправите их все. Профессиональный совет для констант. Поставь справа, а потом читай справа налево. const всегда применяется к объекту, находящемуся непосредственно слева от него. Помогает с константными массивами констант. - person Arran Cudbard-Bell; 26.01.2015
comment
@Jonathan Leffler - кажется, есть небольшая проблема с липким битом: ваш код показывает мне drwx-wx--t, где ls показывает drwx-wx--T. Например, в случае /var/spool/cron/crontabs/. - person Martin Vegter; 17.12.2016
comment
@MartinVegter: да, похоже, последовательность S_IXUSR, S_IXGRP, S_IXUSR должна быть S_IXUSR, S_IXGRP, S_IXOTH. Я исправлю это. - person Jonathan Leffler; 17.12.2016

Мне не нравится синтаксис if/ else if.

Я предпочитаю использовать оператор switch. Немного помучившись, я нашел способ сделать это с помощью разных макросов, например:

S_ISCHR (mode)

Эквивалентно:

((mode & S_IFMT) == S_IFCHR)

Это позволяет нам построить оператор switch следующим образом:

char f_type(mode_t mode)
{
    char c;

    switch (mode & S_IFMT)
    {
    case S_IFBLK:
        c = 'b';
        break;
    case S_IFCHR:
        c = 'c';
        break;
    case S_IFDIR:
        c = 'd';
        break;
    case S_IFIFO:
        c = 'p';
        break;
    case S_IFLNK:
        c = 'l';
        break;
    case S_IFREG:
        c = '-';
        break;
    case S_IFSOCK:
        c = 's';
        break;
    default:
        c = '?';
        break;
    }
    return (c);
}

Что, на мой взгляд, немного более элегантно, чем подход if/else if.

person Alexandro de Oliveira    schedule 16.06.2017
comment
Вы сбросили ассемблерный код, чтобы определить, какой из них более эффективен? (например, gcc -S -masm=intel -O2 -o filemode.asm filemode.c) - person David C. Rankin; 16.06.2017