本文主要总结Unix文件类型与访问权限
函数stat、fstat、fstatat、lstat
stat函数通过文件名pathname获取文件信息,并保存在buf所指的结构体stat中
fstat函数获得已在描述符fd上打开文件的有关信息
lstat函数与stat类似,但是当命名的文件是一个符号链接时,lstat返回该符号链接的有关信息
fstatat函数为一个相对于当前打开目录的路径名返回文件统计信息
#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
buf是一个指针,指向一个我们必须提供的结构,函数来填充由buf指向的结构,基本形式是:
struct stat { mode_t st_mode; /* file type & mode (permissions) */ ino_t st_ino; /* i-node number (serial number) */ dev_t st_dev; /* device number (file system) */ dev_t st_rdev; /* device number for special files */ nlink_t st_nlink; /* number of links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ off_t st_size; /* size in bytes, for regular files */ struct timespec st_atim; /* time of last access */ struct timespec st_mtim; /* time of last modification */ struct timespec st_ctim; /* time of last file status change */ blksize_t st_blksize; /* best I/O block size */ blkcnt_t st_blocks; /* number of disk blocks allocated */ };
文件类型
我们平时最常接触的文件类型有普通文件(regular file)和目录(di-rectory file),但是 UNIX 系统提供了多种文件类型:
(1) 普通文件(regular file)
这种文件包含了某种形式的数据,这些数据无论是文件还是二进制对于 UNIX 内核而言都是一样的。对普通文件内容的解释有处理该文件的应用程序进行。
(2) 目录文件(directory file)
目录文件包含了其他文件的名字以及指向与这些文件有关信息的指针。对一个目录文件具有读权限的任一进程都可以读取该目录的内容,但是只有内核才能直接写目录文件。
(3) 块特殊文件(block special file)
这种文件类型提供对设备带缓冲的访问,每次访问以固定长度为单位进行。
(4) 字符特殊文件(character special file)
这种文件类型提供对设备不带缓冲的访问,每次访问长度可变。系统中的所有设备要么是字符特殊文件,要么是块特殊文件。
(5) FIFO
这种类型文件用于进程间通信。也称为命名管道(namedpipe)。
(6) 套接字(socket)
这种文件类型用于进程间的网络通信。
(7) 符号链接(symbolic link)
这种文件类型指向另一个文件。
文件类型信息包含在 stat 结构的st_mode 成员中。可以使用下表中的宏来确定文件类型。这些宏的参数都是 stat 结构的st_mode 成员。
宏 | 文件类型 |
S_ISREG() | 普通文件 |
S_ISDIR() | 目录文件 |
S_ISCHR() | 字符特殊文件 |
S_ISBLK() | 块特殊文件 |
S_ISFIFO() | 管道或 FIFO |
S_ISLNK() | 符号链接 |
S_ISSOCK() | 套接字 |
下面程序取其命令行参数,然后针对每个命令行参数打印其文件类型:
#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); }
运行结果如下:
wu@ubuntu:~/opt/Cproject/apue/cp1$ ./a.out /etc/passwd /etc /dev/log /dev/tty /var/lib/oprofile/opd_pipe /dev/sr0 /dev/cdrom
/etc/passwd: regular
/etc: directory
/dev/log: socket
/dev/tty: character special
/var/lib/oprofile/opd_pipe: lstat error: No such file or directory
/dev/sr0: block special
/dev/cdrom: symbolic link
文件的设置用户 ID位 和设置组 ID位
实际用户 ID | 我们实际上是谁 |
实际组 ID | |
有效用户 ID | 用于文件访问权限检查 |
有效组 ID | |
附加组 ID | |
保存的设置用户 ID | 由 exec 函数保存 |
保存的设置组 ID |
一个进程的实际用户 ID 和实际组 ID 即为当前登录会话使用的用户ID 和该用户所在的组 ID。当执行一个程序文件时,有效用户 ID 通常就是实际用户 ID,有效组 ID 通常就是实际组 ID。但是,程序文件的文件模式字可以通过设置特殊标志(设置用户 ID 位或设置组 ID 位)来将进程的有效用户 ID 或有效组 ID 设置为程序文件的所有者 ID 或所有组 ID。设置用户 ID(set-user-ID)位的含义是“当执行此文件时,将进程的有效用户 ID设置为文件所有者的用户 ID”。与此相似,设置组(set-group-ID)位的含义是“当执行此文件时,将进程的有效组 ID 设置为文件所有组 ID”。
文件设置用户 ID 位及设置组 ID 位都包含在 stat 结构的st_mode 值中,这两位可用常量S_ISUID 和S_ISGID 进行测试。
文件访问权限
stat 结构的st_mode 值中包含了针对文件的访问权限位。所有文件类型都具有访问权限。每个文件有 9 个访问权限位
st_mode 屏蔽 | 意义 |
S_IRUSR | 用户 -读 |
S_IWUSR | 用户 -写 |
S_IXUSR | 用户 -执行 |
S_IRGRP | 组 -读 |
S_IWGRP | 组 -写 |
S_IXGRP | 组 -执行 |
S_IROTH | 其他 -读 |
S_IWOTH | 其他 -写 |
S_IXOTH | 其他 -执行 |
关于文件访问权限位的使用方式如下:
• 用名字打开任一类型的文件时,对该名字中包含的每一个目录,包括它可能隐含的当前工作目录都应具有执行权限。例如,
为了打开文件/usr/include/stdio.h,需要对目录/、/usr 和/usr/include具有执行权限。然后,需要具有对该文件本身的适当权限。如果当前工作目录是/usr/include,那么为了打开文件 stdio.h,则需要有对该工作目录的执行权限。
注意:对于目录的读权限和执行权限的意义是不相同的。读权限允许我们读目录,获取在该目录中所有文件名的列表。当一个目录是我们要访问文件的路径名的一个组成部分时,对该目录的执行权限使我们可通过该目录(也就是搜索该目录,寻找一个特定的文件名。)
• 文件的读权限决定了我们是否能够打开该文件进行读操作。
• 文件的写权限决定了我们是否能够打开该文件进行写操作。
• 为了要在一个目录中创建一个新文件,必须对该目录具有写权限和执行权限。
• 为了删除一个现有的文件,必须对包含该文件的目录具有写权限和执行权限。对该文件本身则不需要有读、写权限。
• 如果用 6 个 exec 函数中的任何一个执行某个文件,都必须对该文件具有执行权限。该文件还必须是一个普通文件。
新文件和目录的所有权
新文件的用户 ID 设置为进程的有效用户 ID。关于组 ID,POSIX.1 允许实现选择下列之一作为新文件的组 ID。
1. 新文件的组 ID 可以是进程有效组 ID。
2. 新文件的组 ID 可以是它所在目录的组 ID。
对于 Linux 2.4.22,新文件的组 ID 取决于它所在目录的设置组 ID 为是否被设置。如果该目录的这一位被设置,则新文件的组 ID 设置为目录的组 ID;否则,将新文件的组 ID 设置为进程的有效组 ID。
Comments