文件和目录(1)
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[i]);
if(lstat(argv[i], &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标志。