stat、fstat和lstat函数 #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int stat(const char *path, struct stat*buf); int fstat(int fd, struct stat *buf); int lstat(const char *path, struct stat*buf); 一旦给出path,stat函数返回与词文件相关的信息结构。fstat获取已在描述符fd上打开的有关信息。lstat类似于stat,但当命名的文件是一个符号连接时,lstat返回该符号链接的有关信息,而不是该符号链接引用文件的信息。 第二个参数buf是一个结构指针,该结构返回文件有关信息,其基本形式如下: struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /*blocksize for file system I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ }; 文件类型文件类型包括以下几种: 普通文件、目录文件、块特殊文件、字符特殊文件、FIFO、套接字和符号链接。 文件类型信息包含在stat结构的st_mode成员中,可用下面的宏来确定文件类型: S_ISREG(): 普通文件 S_ISDIR(): 目录文件 S_ISCHR(): 字符特殊文件 S_ISBLK(): 块特殊文件 S_ISFIFO(): 管道或FIFO S_ISLINK(): 符号链接 S_ISSOCK(): 套接字 如下程序取其命令参数,然后针对每一个命令参数打印其文件类型: #include<unistd.h> #include<stdio.h> #include<sys/stat.h> int main(int argc, char *argv[]) { int i; structstat buf; char *ptr;
for(i = 1; i < argc; i++) { printf("%s:", argv); if(lstat(argv, &buf) < 0) { perror("lstaterror"); continue; } if(S_ISREG(buf.st_mode)) ptr= "regular"; elseif (S_ISDIR(buf.st_mode)) ptr= "directory"; elseif (S_ISCHR(buf.st_mode)) ptr= "character special"; elseif (S_ISBLK(buf.st_mode)) ptr= "block special"; elseif (S_ISFIFO(buf.st_mode)) ptr = "fifo"; elseif (S_ISLNK(buf.st_mode)) ptr= "symbolic link"; elseif (S_ISSOCK(buf.st_mode)) ptr= "socket"; else ptr= "** unknown mode **"; printf("%s\n",ptr); } exit(0); } 设置用户ID和设置组ID每个进程相关联的ID有6个或更多,如下: 实际用户ID、实际组ID:我们实际上是谁 有效用户ID、有效组ID、附加组ID:用于文件访问权限的检查 保存的设置用户ID、保存的设置组ID:由exec函数保存 每个文件都有一个所有者和组所有者,所有者由stat结果中的st_uid成员表示,组所有者则由st_gid成员表示。 当执行一个程序文件时,进程的有效用户ID通常是实际用户ID,有效组ID通常是实际组ID。但可以在文件模式(st_mode)中可以通过设置用户ID位(S_ISUID)和设置组ID位(S_ISGID)将进程的有效用户ID设置为文件所有者的用户ID,将有效组ID设置为文件的组所有者的ID。 文件访问权限st_mode值也包含了针对文件的访问权限位。 每个文件有9个访问权限,分为三类。chmod命令用于修改这9个权限值,改名了允许我们用u表示用户,用g表示组,用o表示其他。以下是9个访问权限位: S_IRUSR 用户读 S_IWUSR 用户写 S_IXUSR 用户执行 S_IRGRP 用户读 S_IWGRP 用户写 S_IXGRP 用户执行 S_IROTH 用户读 S_IWOTH 用户写 S_IXOTH 用户执行 目录的读权限允许我们读目录,获得在该目录中所有文件名的列表。 当一个目录是我们要访问文件路径的一个组成部分时,对该目录的执行权限使我们可通过该目录。 当需要创建或删除有一个目录中的文件时,必须对包含该文件的目录具有写权限和执行权限。 进程每次打开、创建或删除一个文件时,内核就进行文件访问权限测试,这种测试可能涉及文件的所有者、进程的有效ID以及进程的附加组ID。两个所有者ID是文件的性质,而两个有效组ID和附加组ID则是进程的性质。内核测试如下:
1.若进程有效用户ID是0(超级用户),则允许访问 2.若进程有效用户ID等于文件所有者ID,那么可通过所有者设置适当的访问权限访问文件,否则拒绝访问。 3.若进程有效组ID或附加组ID等于文件组ID,那么可通过文件组设置的适当访问权限访问文件,否则拒绝访问。 4.若其他用户适当的访问权限被设置,则允许访问;否则拒绝访问。
创建新文件时,新文件的用户ID设置为进程的有效用户ID。关于组ID,POSIX.1允许实现选择下列之一作为新文件的组ID。 1) 新文件的组ID可以是进程的有效组ID 2) 新文件的组ID可以是它所在目录的组ID access函数#include <unistd.h> int access(const char *pathname, int mode); 该函数按实际用户ID和实际组ID进行文件访问权限测试。成功返回0,出错返回-1。 其中第一个参数为测试文件名,第二个参数是以下所列常量的按位或 R_OK 测试读权限 W_OK 测试写权限 X_OK 测试执行权限 F_OK 测试文件是否存在 以下显示了access函数的使用方法: #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); } 下面是该程序的示例会话: chen123@ubuntu:~/user/apue.2e$ ls -l a.out -rwxrwxr-x 1 chen123 chen123 78392014-04-18 19:59 a.out chen123@ubuntu:~/user/apue.2e$ ./a.out a.out read access OK open for reading OK chen123@ubuntu:~/user/apue.2e$ ls -l /etc/shadow -rw-r----- 1 root shadow 1047 2014-04-1506:03 /etc/shadow chen123@ubuntu:~/user/apue.2e$ ./a.out /etc/shadow access error for /etc/shadow: Permissiondenied open error for /etc/shadow: Permissiondenied chen123@ubuntu:~/user/apue.2e$ su Password: root@ubuntu:/home/chen123/user/apue.2e# chown root a.out root@ubuntu:/home/chen123/user/apue.2e# chmod u+s a.out root@ubuntu:/home/chen123/user/apue.2e# ls -l a.out -rwsrwxr-x 1 root chen123 7839 2014-04-1819:59 a.out root@ubuntu:/home/chen123/user/apue.2e# exit exit chen123@ubuntu:~/user/apue.2e$ ./a.out /etc/shadow access error for /etc/shadow: Permissiondenied open for reading OK 这里的测试首先显示a.out的文件信息和/etc/shadow的文件信息。当执行./a.out /etc/shadow命令时,access不能通过,因为/etc/shadow的文件用户为root,而进程实际用户是chen123,所以没有读权限。open也不能通过,因为测试的是用户也是实际用户。但将a.out的用户ID改变为root,并打开设置用户ID位后,当执行a.out程序时,可以认为执行这个程序的进程有效用户ID变成了a.out文件用户ID root。但access函数测试的实际用户ID仍然是chen123。所以access不能通过,而open可以通过。但若没有打开设置用户ID位,open也不能通过。 umask函数#include <sys/types.h> #include <sys/stat.h> mode_t umask(mode_t mask); umask函数为进程设置文件模式创建屏蔽字,并返回以前的值。 参数mask是由文件访问权限中的9个常量中的若干个按位或构成的。在进程创建文件或新目录时,一定会使用文件模式创建屏蔽字。对于任何在文件模式创建屏蔽字中为1的为,在文件mode中的相应位一定被关闭。 chmod和fchmod函数#include <sys/stat.h> int chmod(const char *path, mode_t mode); int fchmod(int fd, mode_t mode); 这两个函数可以更改现有的文件访问权限。 chown、fchown和lchown函数#include <unistd.h> int chown(const char *path, uid_t owner,gid_t group); int fchown(int fd, uid_t owner, gid_tgroup); int lchown(const char *path, uid_t owner,gid_t group); 以上函数可用于更改文件的用户ID和组ID。 在符号链接的情况下,lchown更改符号链接本身的所有者,而不是符号链接所指向的文件。 如若两个参数owner和group中的任意一个是-1,则个的ID不变。 文件长度stat结构成员st_size表示一字节为单位的文件长度。此字段只对普通文件、目录文件和符号链接有意义。 对普通文件,其文件长度可以是0,在读这种文件时,将得到文件结束指示。 对于目录,文件长度通常是一个数(例如16或512)的倍数。 对于链接,文件长度是文件名(包括路径)中的实际字节数。 UNIX提供st_blksize字段,当我们将该字段用于读操作时,读一个文件的时间量最少。 文件截短#include <unistd.h> #include <sys/types.h> int truncate(const char *path, off_tlength); int ftruncate(int fd, off_t length); 这两个函数可以把现有的文件长度截短为length字节。如果该文件以前的长度大于length,则超过length以外的数据就不在能访问。如果以前的长度短语length,则扩张该文件,在旧文件尾端与新文件尾端填充0(也就是创建了空洞)。 讲一个文件清空为0可以在打开文件时使用O——TRUNC标志。 |