码迷,mamicode.com
首页 > 其他好文 > 详细

UNIX高级环境编程(3)Files And Directories - stat函数,文件类型,和各种ID

时间:2015-02-17 23:34:24      阅读:170      评论:0      收藏:0      [点我收藏+]

标签:

    在前面的两篇,我们了解了IO操作的一些基本操作函数,包括open、read和write。

    在本篇我们来学习一下文件系统的其他特性和一个文件的属性,涉及的函数功能包括:

  • 查看文件的所有属性;
  • 改变文件所有者;
  • 改变文件权限;
  • 操作文件夹。

    我们还会了解一些文件系统相关的数据结构和符号链接(symbolic link)。

 

1 函数stat、fstat、fstatat、lsat函数

#include <sys/stat.h>
int stat(const char *restrict pathname, struct stat *restrict buf );
int fstat(int fd, struct stat *buf);
int lstat(const char *restrict pathname, struct stat *restrict buf );
int fstatat(int fd, const char *restrict pathname, struct stat *restrict buf, int flag);
//All four return: 0 if OK, −1 on error

 

函数功能:

stat返回pathname指定文件的信息;

fstat获取在文件描述符fd上打开的文件信息;

lstat的功能和stat函数类似,只是有一种情况有区别,当pathname指定的文件是一个符号链接的时候,lstat函数获取的文件信息是该符号链接的信息,而不是符号链接所指向文件的信息,在后面我们会了解更多关于符号链接的相关内容;

fstatat:文件描述符fd表示一个父目录,pathame指定父目录下得一个子目录,fstatat函数返回该子目录下的文件统计数。flag用来设置是否查询符号链接所指向的文件,如果flag设置为AT_SYMLINK_NOFOLLOW,则fstatat函数只返回该符号链接的相关信息,如果不设置该标志位,则返回该符号链接指向的文件的相关信息。

是一个关键的输出参数,上面的参数将文件属性填充值该结构体内。结构体如下图所示:

技术分享

结构体中得每个成员都代表文件的某一个属性,我们会逐个了解属性的具体含义。

 

2 文件类型(File Type)

目前我们了解了两种文件:常规文件(regular files)和文件夹(directory)。

常见的几种文件类型包括: 

  1. 常规文件(Regular File):包含某种组织形式的数据的文件,Linux并不关心数据是文本或是二进制(有一个例外,就是二进制可执行文件,内核必须知道该类型的格式才能执行),对数据的解析由应用程序负责。
  2. 目录文件(Directory):包含其他文件的名字,和指向这些文件信息的指针。进程可以读取目录文件的内容,但是只有内核才又写该类型文件的权限,进程通过调用本篇介绍的一些函数才操作修改目录文件,本质上仍然是内核修改。
  3. 块文件(Block Special File):如硬盘驱动,提供了固定大小缓存用于IO的文件类型。
  4. 字符文件(Charactor Special File):无缓存的对可变大小单元进行操作的文件类型。操作系统所有的设备都是块设备文件或字符设备文件。
  5. 先入先出队列(FIFO):进程间通信使用的文件类型。
  6. 套接字(Socket):网络或单机中的两个进程之间的通信使用的文件类型。
  7. 符号链接(Symbolic File):指向另一个文件的文件类型。

stat结构体中得st_mode制定了文件的类型。

Example:

#include "apue.h"

int
main(int argc, char *argv[])
{
    int         i;
    struct stat buf;
    char        *ptr; 

    for (i = 1; i < argc; i++) {

        printf("%s: ", argv[i]);

        if (lstat(argv[i], &buf) < 0) {
            err_ret("lstat error");
            continue;
        }

        if (S_ISREG(buf.st_mode))
            ptr = "regular";
        else if (S_ISDIR(buf.st_mode))
            ptr = "directory";
        else if (S_ISCHR(buf.st_mode))
            ptr = "character special";
        else if (S_ISBLK(buf.st_mode))
            ptr = "block special";
        else if (S_ISFIFO(buf.st_mode))
            ptr = "fifo";
        else if (S_ISLNK(buf.st_mode))
            ptr = "symbolic link";
        else if (S_ISSOCK(buf.st_mode))
            ptr = "socket";
        else
            ptr = "** unknown mode **";

        printf("%s\n", ptr);
    }
    exit(0);
}

 

运行截图:

技术分享

 

3 设置用户Id(User ID)和组Id(Group ID)

每个进程6个甚至更多的ID和它关联。如下表所示:

技术分享

简要介绍ID的区别:

  • real user ID和real group ID:当前登陆用户的ID和该用户所属的组ID;
  • effective user ID、effective group ID和supplementary group IDs:文件的访问权限。
  • saved set-user-ID和saved set-group-ID:当一个程序运行时,会拷贝effective user ID和effective group ID到这两个变量中,这两个ID和函数setuid相关。

一般情况下,effective user ID = real user ID,effective group ID = real group ID.

每个文件都由一个所有者,和一个组所有者,分别对应stat数据结构中得字段:st_uid和st_gid。

如果执行程序时,effective user(group) ID != real user(group) ID,可以通过设置mode中两个bit来实现将effective user(group) ID设置为文件所有者(组),这两个bit叫做:set-user-ID位和set-group-ID位。这两个位包含在stat数据结构中得st_mode字段中,可以通过函数S_ISUID和S_ISGID来测试。

 

4 文件访问权限(File Access Permissions)

stat数据结构中的st_mode字段中同样包含文件访问权限位。

每个文件有9中权限位:

技术分享

这些标志位的使用需要注意的事项总结如下:

  • 如果要打开某个文件,则该文件全路径上的所有文件夹都需要有执行权限(execute permission),该文件则需要对应的操作权限。需要可执行权限的原因是可以通过(pass through)它找到下一级目录或者文件;
  • 读权限,允许打开某个已存在文件,标志位:O_RDONLY和O_RDWR;
  • 写权限,允许向某个已存在文件写入数据,标志位:O_WRONLY和O_RDWR;
  • 调用open函数时,如果要指定标志位O_TRUNC,需要写权限;
  • 我们无法在某个文件夹下创建文件,除非我们对该文件夹有写权限(write permission)和执行权限(execute permission);
  • 删除某个文件,需要该文件所在文件夹的写权限和执行权限,而不需要对该文件本身有写权限或读权限;
  • 如果我们希望使用exec类函数执行某个文件,则必须有可执行权限,并且该文件必须为regular file。

文件访问权限检测流程:

  1. 如果进程的effective user ID是0,即超级用户,则允许访问;
  2. 如果进程的effective user ID是文件所有者的ID,即该进程是该文件的所有者(owner),并且相应的访问权限标志位被设置,则允许访问;
  3. 如果进程的effective group ID或者supplementary group IDs之一是文件所有组的ID,并且相应的访问权限标志位被设置,则允许访问;
  4. 如果相应的other访问权限标志位被设置,则允许访问。

简单来说,如果进程是该文件的拥有者,则访问是否允许取决于用户访问权限标志位,忽略组权限标志位;如果进程不是该文件的拥有者,但是该进程属于某个有访问权限的组,访问是否允许取决于组权限标志位的设置,忽略other访问权限标志位。

 

5 新文件和目录的所有权(ownership)

新文件的real user ID为创建该文件的进程的effective user ID。

新文件的real group ID的取值取决于:

  • 新文件的real group ID可以是常见该文件的进程的effective group ID;
  • 新文件的real group ID可以是该文件所在目录的real group ID。(必须保证该目录下面的所有文件的real group ID都是该目录的real group ID)。

 

6 函数access和faccessat

access和faccessat函数用于测试当前用户(real user,当前登录用户)对某一文件是否有某种操作权限。

函数声明:

#include <unistd.h>
int access(const char* pathname, int mode);
int faccessat(int fd, const char* pathname, int mode, int flag);

 

mode的可取值,当为F_OK,测试文件是否存在。

技术分享

faccessat函数的相关细节:

  • 当pathname为绝对路径,或者fd取值为AT_FDCWD并且pathname为相对路径时,功能和access相同。
  • 否则,faccessat函数指定的工作目录为pathname指定的相对目录加上fd描述符指定的父目录。
  • 当flag的值为AT_EACCESS时,测试时使用进程的effective user和group而不是当前real user ID和group。

Example:

#include "apue.h"
#include <fcntl.h>

int
main(int argc, char *argv[])
{
    if (argc != 2)
        err_quit("usage: a.out <pathname>");

    if (access(argv[1], R_OK) < 0)
        err_ret("access error for %s", argv[1]);
    else
        printf("read access OK\n");

    if (open(argv[1], O_RDONLY) < 0)
        err_ret("open error for %s", argv[1]);
    else
        printf("open for reading OK\n");

    exit(0);
}

 

测试:

技术分享

由于本地mac环境搞不定,所以直接截书上的例子吧。

该例中,当切换到root用户,修改了该文件的所有者为root用户,并且设置了set-user-ID,这样当切换到别的用户时,仍可以以RDONLY方式打开文件,但是由于access是测试当前real user的读权限,当切换到其他用户时,access测试不通过,显示Permission denied。

好吧,直接忽略上面这个例子吧,我没能在自己的机器上重现,只是直接随便翻译书上某一段话。如果有某位高手看懂了,请指点。

 

小结

由于这一章内容比较多,所以打算用三篇来写,这是第一篇,主要介绍了stat函数,access函数,文件类型和各种令人糊涂的ID。

 

参考资料:

《Advanced Programming in the UNIX Envinronment 3rd》

UNIX高级环境编程(3)Files And Directories - stat函数,文件类型,和各种ID

标签:

原文地址:http://www.cnblogs.com/suzhou/p/4295535.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!