【问题标题】:Printing file permissions like 'ls -l' using stat(2) in C在 C 中使用 stat(2) 打印文件权限,如“ls -l”
【发布时间】:2012-05-06 13:46:54
【问题描述】:

我正在尝试编写一个模拟 unix 命令的小型 C 程序 ls -l。为此,我正在使用stat(2) 系统调用,并且在编写权限时遇到了一个小问题。我有一个mode_t 变量,它保存来自st_mode 的文件权限,并且将该值解析为s 字符串表示并不难,但我只是想知道是否有比这更好的方法.

【问题讨论】:

    标签: c stat


    【解决方案1】:

    来自谷歌的示例

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

    结果:

    2.c 的信息
    --------------------------
    文件大小:1223 字节
    链接数:1
    文件索引节点:39977236
    文件权限:-rw-r--r--
    
    该文件不是符号链接

    【讨论】:

    • 感谢您的回答。这帮了很多忙。
    • 请注意,由于代码使用stat() 而不是lstat(),因此它报告“符号链接”的唯一时间是符号链接损坏时。否则,它将报告符号链接末尾的文件。
    • 不错,但逗号后的空格使其更具可读性,而不是所有内容都挤在一起。
    • 如何知道所有者和组的名称,我可以知道吗?请
    【解决方案2】:

    基础很简单;棘手的位是 SUID 和 SGID 位以及修改“x”位的粘性位。考虑将权限拆分为用户、组、所有者的 3 个八进制数字,并使用这些数字来索引 3 个字符的字符串数组,例如 rwx---。然后根据其他模式位调整适当的x 位。文件类型必须单独处理,但您可以使用 12 位右移(可能带有屏蔽)和 16 条目表来处理 16 个可能的值(并非所有这些值在任何给定系统上都有效) .或者您可以处理已知类型,如下面的代码所示。

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

    4 种类型的位,三个 S 位(setuid、setgid、sticky)和用户、组和其他位。

    这是我用于将mode_t 转换为字符串的代码。它是为一个很好的无线程程序编写的,所以它使用静态数据;修改它以将输出字符串作为输入参数将是微不足道的:

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

    【讨论】:

    • 欣赏您的回答所提供的深度!每天学习新东西!
    • @ArranCudbard-Bell:我一直在用类似于clang -Weverything 的东西清理命令语料库,有时会有点痛苦。我实际上并没有直接尝试过clang -Weverything;它可能没有我使用的选项那么繁琐(大约 18 个 -W* 标志;-Wconversion 是我的代码遇到问题的最大原因)。
    • @JonathanLeffler -Weverything 然后 -Wno- 发出真正愚蠢的警告。我认为如果您在切换枚举类型时使用默认情况,它会警告您。你永远无法修复它们。 consts 的专业提示。把它放在右边,然后从右到左读。 const 然后总是直接应用于它左边的东西。有助于 const 的 const 数组。
    • @Jonathan Leffler - 粘性位似乎存在小问题:您的代码显示drwx-wx--t,其中ls 显示drwx-wx--T。例如/var/spool/cron/crontabs/.
    • @MartinVegter:是的,看起来序列 S_IXUSR、S_IXGRP、S_IXUSR 应该是 S_IXUSR、S_IXGRP、S_IXOTH。我会解决的。
    【解决方案3】:

    我不喜欢 if/ else if 语法。

    我更喜欢使用switch 语句。经过一番挣扎,我找到了使用不同宏的方法,例如:

    S_ISCHR (mode)
    

    相当于:

    ((mode & S_IFMT) == S_IFCHR)
    

    这允许我们像这样构建一个 switch 语句:

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

    在我看来,这比 if/else if 方法更优雅一些。

    【讨论】:

    • 您是否已转储汇编代码以确定哪个更有效? (例如gcc -S -masm=intel -O2 -o filemode.asm filemode.c
    猜你喜欢
    • 1970-01-01
    • 2019-12-02
    • 1970-01-01
    • 2013-12-25
    • 1970-01-01
    • 1970-01-01
    • 2011-01-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多