2012-04-25 62 views
14

我正在尝试编写一个模拟unix命令的小型C程序 ls -l。为此,我正在使用系统调用stat(2),并且遇到了写入权限的小问题。我有一个mode_t变量,它拥有st_mode的文件许可权,并且将该值解析为s字符串表示形式并不困难,但我只是想知道是否有更好的方法可以做到这一点。从谷歌打印文件权限像'ls -l'使用stat(2)中的C

回答

40

例如

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

结果:

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

感谢您的回答。这帮了一大笔钱。 – cheezone 2012-04-25 20:45:46

+2

请注意,因为代码使用'stat()'而不是'lstat()',所以只会在符号链接断开时报告'符号链接'。否则,它将在符号链接的末尾报告该文件。 – 2015-01-26 13:37:13

14

基础知识足够简单;棘手的位是SUID和SGID位以及修改'x'位的粘性位。考虑将权限分为用户,组,所有者的三个八进制数字,并使用它们索引到一个由3个字符组成的数组,例如rwx---。然后根据其他模式位调整相应的x位。文件类型必须单独处理,但您可以使用12位右移(可能带有掩码)和16个条目表来处理16个可能值(并非所有值在任何给定系统上都是有效的) 。或者你可以处理已知的类型,如下面的代码所示。

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

的4个类型的位,的三个S位(的setuid,setgid的,粘性的)和用户,组等的位。

这是我用于将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); 
} 
+0

欣赏您的答案提供的深度!每天学些新东西! – cheezone 2012-04-25 20:46:05

+0

铿锵''-Weverything''完全正确地抱怨:) – 2015-01-26 15:01:28

+0

@ ArranCudbard-Bell:我一直在用'clang -Weverything'类似的东西来清理一个命令语料库,有时可能会有点痛苦。我实际上没有直接尝试'clang -Weverything';它可能比我使用的选项更麻烦(大约18'-W *'标志;'-Wconversion'是我的代码出现问题的最大原因)。 – 2015-01-26 15:04:53

0

我不喜欢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方法更优雅。

+0

你倾销了汇编代码以确定哪一个更高效? (例如'gcc -S -masm = intel -O2 -o filemode.asm filemode.c') – 2017-06-16 05:23:07