Я пытаюсь написать небольшую программу на C, которая эмулирует команду unix ls -l
. Для этого я использую системный вызов stat(2)
и столкнулся с небольшой заминкой при записи разрешений. У меня есть переменная mode_t
, которая содержит права доступа к файлу из st_mode
, и было бы несложно разобрать это значение в строковое представление s, но мне просто интересно, есть ли лучший способ сделать это, чем этот.
Печать прав доступа к файлам, таких как «ls -l», с использованием stat (2) в C
Ответы (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
stat()
вместо lstat()
, единственный раз, когда он сообщит о «символической ссылке», это когда символическая ссылка не работает. В противном случае он сообщит о файле в конце символической ссылки.
- person Jonathan Leffler; 26.01.2015
Основы достаточно просты; хитрые биты — это биты 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);
}
clang -Weverything
, и временами это может быть немного болезненно. На самом деле я не пробовал clang -Weverything
напрямую; это может быть менее обременительно, чем варианты, которые я использую (около 18 флагов -W*
; -Wconversion
- самая большая проблема для моего кода).
- person Jonathan Leffler; 26.01.2015
drwx-wx--t
, где ls
показывает drwx-wx--T
. Например, в случае /var/spool/cron/crontabs/
.
- person Martin Vegter; 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
.
gcc -S -masm=intel -O2 -o filemode.asm filemode.c
)
- person David C. Rankin; 16.06.2017