web-dev-qa-db-fra.com

Imprimer des permissions de fichiers comme 'ls -l' en utilisant stat (2) en C

J'essaie d'écrire un petit programme en C qui émule la commande unix ls -l. Pour ce faire, j'utilise l'appel système stat(2) et je me suis heurté à un petit problème en écrivant les autorisations. J'ai une variable mode_t qui contient les autorisations de fichier de st_mode, et il ne serait pas difficile d'analyser cette valeur dans la représentation de chaîne s, mais je me demandais simplement s'il existait un meilleur moyen de le faire.

15
cheezone

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

résultat:

Information for 2.c 
 --------------------------- .__ Taille du fichier: 1223 octets 
 Nombre de liens: 1 
 Fichier inode: 39977236 
 Autorisations de fichier: -rw-r - r --

 Le fichier n'est pas un lien symbolique
43
askovpen

Les bases sont assez simples; les bits difficiles sont les bits SUID et SGID et le bit collant, qui modifient les bits "x". Envisagez de fractionner les autorisations en trois chiffres octaux pour l'utilisateur, le groupe, le propriétaire et pour les indexer dans un tableau de chaînes de trois caractères, telles que rwx et ---. Puis ajustez les bits x appropriés en fonction des autres bits de mode. Le type de fichier devra être traité séparément, mais vous pouvez utiliser un décalage de 12 bits à droite (éventuellement avec masquage) et une table de 16 entrées pour traiter les 16 valeurs possibles (qui ne sont pas toutes valables sur un système donné). . Vous pouvez également utiliser des types connus, comme indiqué dans le code ci-dessous.

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

Les 4 types de bits, les trois S-bits (setuid, setgid, sticky) et les bits d’utilisateur, de groupe et autres.

C'est le code que j'utilise pour convertir mode_t en chaîne. Il a été écrit pour un programme bien sans thread, il utilise donc des données statiques; il serait trivial de le modifier pour prendre la chaîne de sortie en tant que paramètre d'entrée:

/* 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);
}
16
Jonathan Leffler

Je n'aime pas la syntaxe if/ else if.

Je préfère utiliser l'instruction switch. Après avoir lutté un peu, j'ai trouvé comment nous pouvons utiliser différentes macros, par exemple:

S_ISCHR (mode)

Est équivalent à:

((mode & S_IFMT) == S_IFCHR)

Cela nous permet de construire une instruction switch comme ceci:

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

Ce qui, à mon avis, est un peu plus élégant que l'approche if/else if.

0