Printing file permissions like 'ls -l' using stat(2) in C

后端 未结 3 1707
梦谈多话
梦谈多话 2021-02-01 20:53

I am trying to write a small C program that emulates the unix command ls -l. To do so, I am using the stat(2) syscall and have ran into a small hi

相关标签:
3条回答
  • 2021-02-01 21:34

    I don't like the if/ else if syntax.

    I prefer use the switch statement. After struggling a bit I found the way we can do it using different macros, for example:

    S_ISCHR (mode)
    

    Is equivalent to:

    ((mode & S_IFMT) == S_IFCHR)
    

    This allows us to build a switch statement like this:

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

    Which in my opinion it's a bit more elegant than the if/else if approach.

    0 讨论(0)
  • 2021-02-01 21:36

    The basics are simple enough; the tricky bits are the SUID and SGID bits and the sticky bit, which modify the 'x' bits. Consider splitting the permissions into 3 octal digits for user, group, owner, and using those to index into an array of 3-character strings such as rwx and ---. Then adjust the appropriate x bits based on the other mode bits. The file type will have to be dealt with separately, but you could use a 12-bit shift right (possibly with masking) and a 16 entry table to deal with the 16 possible values (not all of which are valid on any given system). Or you can deal with known types as shown in the code below.

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

    The 4 types bits, the three S-bits (setuid, setgid, sticky) and the user, group and other bits.

    This is code I use for converting mode_t into a string. It was written for a nicely threadless program, so it uses static data; it would be trivial to modify it to take the output string as an input parameter:

    /* 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 讨论(0)
  • 2021-02-01 21:46

    example from 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;
    }
    

    result:

    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
    0 讨论(0)
提交回复
热议问题