2012-04-25 21 views
14

Próbuję napisać mały program w języku C, który emuluje polecenie unix ls -l. Aby to zrobić, korzystam z syscall stat(2) i napotkałem małą czkawkę, pisząc uprawnienia. Mam zmienną mode_t, która przechowuje uprawnienia do plików od st_mode i nie byłoby trudno przetworzyć tę wartość na reprezentację ciągów s, ale zastanawiałem się, czy jest lepszy sposób na zrobienie tego niż to.Drukowanie uprawnień do plików, takich jak "ls -l" przy użyciu stat (2) w C

Odpowiedz

40

przykład z google

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

wynik:

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
+1

Dzięki za odpowiedź. To pomogło tonę. – cheezone

+2

Należy zauważyć, że ponieważ kod używa 'stat()' zamiast 'lstat()', jedyny moment, w którym zgłasza "dowiązanie symboliczne", jest po zerwaniu dowiązania symbolicznego. W przeciwnym razie będzie raportował na pliku na końcu dowiązania symbolicznego. –

14

Podstawy są dość proste; trudnymi bitami są bity SUID i SGID oraz lepki bit, który modyfikuje bity "x". Rozważ podzielenie uprawnień na 3 cyfry ósemkowe dla użytkownika, grupy, właściciela i przy użyciu tych, które będą indeksowane w tablicę 3-znakowych łańcuchów, takich jak rwx i ---. Następnie dostosuj odpowiednie bity x w oparciu o inne bity trybu. Typ pliku będzie musiał zostać potraktowany osobno, ale możesz użyć 12-bitowego przesunięcia w prawo (prawdopodobnie z maskowaniem) i 16 tabeli wpisów, aby poradzić sobie z 16 możliwymi wartościami (nie wszystkie z nich są ważne w danym systemie) . Lub możesz radzić sobie ze znanymi typami, jak pokazano w poniższym kodzie.

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

4 typów bity, trzy bity S (z setuidem, setgid lepkie), a użytkownik, grupa i inne bity.

Jest to kod, którego używam do przekształcania mode_t w ciąg znaków. Został napisany dla ładnie pozbawionego nici programu, więc używa statycznych danych; byłoby trywialne zmodyfikować go do podjęcia ciąg wyjściowy jako parametr wejściowy:

/* 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); 
} 
+0

Doceń głębię dostarczoną przez odpowiedź! Naucz się czegoś nowego każdego dnia! – cheezone

+0

clang '' -Weverything'' dość słusznie narzeka :) –

+0

@ ArranCudbard-Bell: Sprzątałem korpus poleceń z czymś analogicznym do "clang -Weverything" i czasami może być trochę bolesne. Właściwie nie próbowałem bezpośrednio "clang -Weverything"; może być mniej uciążliwy niż opcje, których używam (około 18 '-W *' flag; '-Wconversion' jest największą przyczyną problemów z moim kodem). –

0

Nie lubię składni if/ else if.

Wolę używać oświadczenia switch. Po zmaga się trochę znalazłem sposób możemy to zrobić za pomocą różnych makr, na przykład:

S_ISCHR (mode) 

jest równoznaczne z:

((mode & S_IFMT) == S_IFCHR) 

Pozwala nam to na zbudowanie switch tak:

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

Co, moim zdaniem, jest nieco bardziej eleganckie niż podejście if/else if.

+0

Czy porzuciłeś kod zespołu, aby określić, który jest bardziej wydajny? (np. 'gcc -S -masm = intel -O2 -o filemode.asm filemode.c') –

Powiązane problemy