Imprimir permisos de archivos como ‘ls -l’ usando stat(2) en C

6 minutos de lectura

Estoy tratando de escribir un pequeño programa en C que emule el comando de Unix
ls -l. Para hacerlo, estoy usando el stat(2) syscall y me encontré con un pequeño contratiempo al escribir los permisos. tengo un mode_t variable que contiene los permisos de archivo de st_modey no sería difícil analizar ese valor en una representación de cadena s, pero me preguntaba si hay una mejor manera de hacerlo que esa.

avatar de usuario
preguntar

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

  • Gracias por la respuesta. Esto ha ayudado a una tonelada.

    – zona de queso

    25/04/2012 a las 20:45

  • Tenga en cuenta que debido a que el código usa stat() en vez de lstat(), la única vez que informará ‘enlace simbólico’ es cuando el enlace simbólico está roto. De lo contrario, informará sobre el archivo al final del enlace simbólico.

    –Jonathan Leffler

    26 de enero de 2015 a las 13:37

  • agradable, pero los espacios después de las comas lo hacen más legible en lugar de todo amontonado.

    – luz clara

    31 de agosto de 2018 a las 12:09

  • ¿Cómo saber el nombre del propietario y del grupo, puedo saberlo? por favor

    – Krishna Acharya

    13 de enero de 2021 a las 13:03

avatar de usuario
jonathan leffler

Los conceptos básicos son bastante simples; los bits complicados son los bits SUID y SGID y el bit pegajoso, 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 ---. A continuación, ajuste el adecuado x bits 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 16 entradas para tratar con los 16 valores posibles (no todos los cuales son válidos en un sistema dado) . O puede tratar con tipos conocidos como se muestra en el código a continuación.

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

Los 4 tipos de bits, los tres S-bits (setuid, setgid, sticky) 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 buen programa sin subprocesos, por lo que usa datos estáticos; sería trivial modificarlo 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);
}

  • ¡Aprecio la profundidad proporcionada por tu respuesta! ¡Aprenda algo nuevo cada día!

    – zona de queso

    25/04/2012 a las 20:46

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

    –Jonathan Leffler

    26 de enero de 2015 a las 15:04

  • @JonathanLeffler -Weverything entonces -Wno- fuera de las advertencias realmente estúpidas. Creo que hay uno en el que le advierte si usa un caso predeterminado en un cambio sobre un tipo de enumeración. Nunca los arreglarás a todos. Consejo profesional para los constantes. Póngalo a la derecha, luego lea de derecha a izquierda. const entonces siempre se aplica a la cosa directamente a su izquierda. Ayuda con matrices constantes de constantes.

    – Arran Cudbard-Bell

    26 de enero de 2015 a las 19:10


  • @Jonathan Leffler: parece haber un pequeño problema con el bit adhesivo: su código me muestra drwx-wx--tdonde ls espectáculos drwx-wx--T. Por ejemplo en caso de /var/spool/cron/crontabs/.

    – Martín Vegter

    17 dic 2016 a las 20:56

  • @MartinVegter: sí, parece que la secuencia S_IXUSR, S_IXGRP, S_IXUSR debería ser S_IXUSR, S_IXGRP, S_IXOTH. Lo arreglaré.

    –Jonathan Leffler

    17 dic 2016 a las 20:58

no me gusta el if/ else if sintaxis.

prefiero usar el switch declaración. Después de luchar un poco, encontré la forma en que podemos hacerlo usando diferentes macros, por ejemplo:

S_ISCHR (mode)

Es equivalente a:

((mode & S_IFMT) == S_IFCHR)

Esto nos permite construir una declaración de cambio como esta:

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 if/else if Acercarse.

  • ¿Has volcado el código ensamblador para determinar cuál es más eficiente? (p.ej gcc -S -masm=intel -O2 -o filemode.asm filemode.c)

    –David C. Rankin

    16 de junio de 2017 a las 5:23

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad