2012-04-25 10 views
14

Estoy tratando de escribir un pequeño programa en C que emula el comando de unix ls -l. Para hacerlo, estoy usando el syscall stat(2) y me he encontrado con un pequeño inconveniente al escribir los permisos. Tengo una variable mode_t que contiene los permisos de archivo de st_mode, y no sería difícil analizar ese valor en s representación de cadena, pero me preguntaba si hay una mejor manera de hacerlo que eso.Impresión de permisos de archivo como 'ls -l' usando stat (2) en C

Respuesta

40

ejemplo de 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; 
} 

resultado:

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

Gracias por la respuesta. Esto ha ayudado mucho. – cheezone

+2

Tenga en cuenta que debido a que el código usa 'stat()' en lugar de 'lstat()', la única vez que informará 'symlink' es cuando se rompe el enlace simbólico. De lo contrario, informará sobre el archivo al final del enlace simbólico. –

14

Los fundamentos son bastante simple; los bits difíciles son los bits SUID y SGID y el bit adhesivo, que modifican los bits 'x'. Considere dividir los permisos en 3 dígitos octales para usuario, grupo, propietario y usarlos para indexar en una matriz de cadenas de 3 caracteres como rwx y ---. Luego ajuste los bits apropiados x basados ​​en los otros bits de modo. El tipo de archivo tendrá que tratarse por separado, pero podría usar un desplazamiento a la derecha de 12 bits (posiblemente con enmascaramiento) y una tabla de entrada de 16 para tratar los 16 valores posibles (no todos son válidos en un sistema dado) . O puede tratar con tipos conocidos como se muestra en el siguiente código.

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

los 4 tipos bits, en los tres bits S (setuid, setgid, pegajosas) y el usuario, grupo y otros bits.

Este es el código que uso para convertir mode_t en una cadena. Fue escrito para un programa muy bien sin hilos, por lo que utiliza datos estáticos; sería trivial modificarla para tomar la cadena de salida como parámetro de entrada:

/* 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

¡Aprecie la profundidad proporcionada por su respuesta! ¡Aprenda algo nuevo cada día! – cheezone

+0

sonido metálico '' -Weverything'' quejó con razón :) –

+0

@ ArranCudbard-Bell: He estado limpiando un corpus de comandos con algo análogo a 'sonido metálico -Weverything' y puede ser un poco doloroso a veces. En realidad, no he intentado 'clang -Weverything' directamente; podría ser menos oneroso que las opciones que estoy usando (alrededor de 18 '-W *' flags; '-Wconversion' es la mayor causa de problemas para mi código). –

0

no me gusta la sintaxis if/ else if.

prefiero utilizar la instrucción switch. Después de luchar un poco encontré la manera en que podemos hacerlo utilizando diferentes macros, por ejemplo:

S_ISCHR (mode) 

es equivalente a:

((mode & S_IFMT) == S_IFCHR) 

Esto nos permite construir una sentencia switch como esto:

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

Que en mi opinión es un poco más elegante que el enfoque if/else if.

+0

¿Ha descartado el código de ensamblado para determinar cuál es más eficiente? (por ejemplo, 'gcc -S-masm = intel -O2 -o filemode.asm filemode.c') –