UNIX环境高级编程(7):文件和目录(1)

stat、fstat、lstat函数:

本章讨论的中心是三个stat函数以及它们返回的信息:

#include <sys/stat.h>

int stat(const char *restrict pathname,struct stat *restrict buf);

int fstat(int filedes, struct stat *buf);

int lstat(const char *restrict pathname, struct stat *restrict buf);

三个函数的返回值:若成功返回0,若出错则返回-1.

stat函数返回pathname所指定文件的有关的信息结构,fstat函数获取已在描述符filedes上打开文件的有关信息,lstat函数类似于stat,但是当命名的文件是一个符号链接时,lstat返回该符号链接的有关信息,而不是由符号链接所指向文件的信息。

第二个参数buf是个指向struct stat结构体的指针,struct stat的实际定义可能随实现有所不同,但其基本形式是:

struct stat {

mode_t st_mode;  /* file type & mode(permissions) */
info_t st_info;
dev_t st_dev;
dev_t st_rdev;
nlinK_t st_nlink;
uid_t st_uid;
gid_t st_gid;
off_t st_size;
time_t st_atime;
time_t st_mtime;
time_t st_ctime;
blksize_t st_blksize;
blkcnt_t st_blocks;

};

该结构的每一个成员都是基本系统数据类型,我们将说明此结构的每一个成员以了解文件的属性。使用stat函数最多的可能是ls -l命令,用其可以获得有关一个文件的所有信息。

文件类型:

UNIX系统的大多数文件是普通文件或目录,但也有另外的一些文件类型,文件类型包括以下几种:

(1)普通文件:这是最常用的文件类型,这种文件包含了某种形式的数据,这种数据是文本还是二进制数据对于UNIX内核而言并无区别。值得注意是,对于二进制可执行文件,为了执行程序,内核必须理解其格式,所有二进制可执行文件都遵循一种格式,这种格式使内核能够确定程序文本和数据加载的位置。

(2)目录文件:这种文件包含了其他文件的名字以及指向与这些文件有关信息的指针。

(3)块特殊文件:这种文件类型提供对设备(例如磁盘)带缓冲的访问,每次访问以固定长度为单位进行。

(4)字符特殊文件:这种文件类型提供对设备不带缓冲的访问,每次访问长度可变。系统中的所有设备要么是字符特殊文件,要么是块特殊文件。

(5)FIFO:这种文件类型用于进程间通信,有时也将其称为命名管道。

(6)套接字(socket):这种文件类型用于进程间的网络通信。套接字也可用于在一台宿主机上进程之间的非网络通信。

(7)符号链接:这种文件类型指向另一个文件。

文件类型信息包含在stat结构体的st_mode成员中,可用下列宏确定文件类型,这些宏的参数都是stat结构中的st_mode成员:

  • S_ISREG() 普通文件;
  • S_ISDIR() 目录文件;
  • S_ISBLK() 块特殊文件;
  • S_ISCHR() 字符特殊文件;
  • S_ISFIFO() 管道或FIFO
  • S_ISSOCK() 套接字;
  • S_ISLNK() 符号链接;

POSIX.1允许实现将进程间通信(IPC)对象(例如,消息队列和信号量)表示为文件。可用如下宏来确定IPC对象的类型,这些宏的参数是指向stat结构的指针:

  • S_TYPEISMQ() 消息队列;
  • S_TYPEISSEM() 信号量;
  • S_TYPEISSHM  共享存储对象;
下列程序接收命令行参数,并针对每一个命令行参数打印其所对应文件的文件类型:
/*
 * Copyright (C) [email protected]
 */


#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>


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

	for (i = 1; i < argc; i++) {
		printf("%s: ", argv[i]);
		if (lstat(argv[i], &buf) < 0) {
			printf("lstat error\n");
			continue;
		}

		if (S_ISREG(buf.st_mode)) {
			pt = "regular";
		} else if (S_ISDIR(buf.st_mode)) {
			pt = "directory";
		} else if (S_ISBLK(buf.st_mode)) {
			pt = "block special";
		} else if (S_ISCHR(buf.st_mode)) {
			pt = "character special";
		} else if (S_ISFIFO(buf.st_mode)) {
			pt = "FIFO";
		} else if (S_ISSOCK(buf.st_mode)) {
			pt = "socket";
		} else if (S_ISLNK(buf.st_mode)) {
			pt = "symbol link";
		} else {
			pt = "unknown mode";
		}
		printf ("%s\n", pt);
	}

	exit(0);
}

以下是该程序的执行结果:

UNIX环境高级编程(7):文件和目录(1)_第1张图片

该程序特地使用了lstat函数而不是stat函数以便检测符号链接,如果使用了stat函数,则不会观察到符号链接。

设置用户ID和设置组ID:

与一个进程相关联的ID有6个或更多:

  • 实际用户ID、实际组ID:标识我们实际是谁,这两个字段是在登录时取自口令文件中的登录项;
  • 有效用户ID、有效组ID、附加组ID:决定了我们的文件访问权限;
  • 保存的设置用户ID、保存的设置组ID:在执行一个程序时保存了有效用户ID和有效组ID的副本。

通常,有效用户ID等于实际用户ID,有效组ID等于实际组ID。

每个文件都有一个所有者和组所有者,所有者由stat结构中的st_uid成员表示,组所有者由st_gid成员表示。当执行一个程序文件时,进程的有效用户ID通常就是实际用户ID,有效组ID通常是实际组ID。但是可以在文件模式字st_mode中设置一个特殊标志,其含义是“当执行此文件时,将进程的有效用户ID设置为文件所有者的用户ID(st_uid)”。与此类似,在文件模式字中可以设置另一位,它使得将执行此文件的进程的有效组ID设置为文件的组所有者ID(st_gid)。在文件模式字中的这两位被称为设置用户ID位(set-user-ID)和设置组ID位(set-group-ID)。设置用户ID位及设置组ID位都保存在st_mode值中,这两位可用常量S_ISUID和S_ISGID测试。

UNIX程序passwd就是一个设置了设置用户ID的可执行程序。

文件访问权限:

st_mode值中也包含了针对文件的访问权限位。所有文件都有访问权限。每个文件有9个访问权限位,chmod命令可用于修改这9个权限位。

  • S_IRUSR文件所有者-读;
  • S_IWUSR:文件所有者-写;
  • S_IXUSR:文件所有者-执行;
  • S_IRGRP:文件所属用户组-读;
  • S_IWGRP:文件所属用户组-写;  
  • S_IXGRP:文件所属用户组-执行;
  • S_IROTH:其他-读;
  • S_IWOTH:其他-写;
  • S_IXOTH:其他-执行;

关于文件的权限,有如下注意事项:

  • 我们用名字打开任意类型的文件时,对该名字中包含的每一个目录,包括它可能隐含的当前工作目录都应具有可执行权限;
  • 对于一个文件的读权限决定了我们是否能够打开该文件进行读操作;
  • 对于一个文件的写权限决定了我们是否能够打开该文件进行写操作;
  • 为了在一个目录中创建一个新文件,必须对该目录具有写权限和可执行权限;
  • 为了删除一个文件,必须对包含该文件的目录具有写权限和可执行权限;
  • 如果用6个exec函数中的任何一个执行某个文件,必须对该文件具有可执行权限,该文件还必须是个普通文件;

进程每次打开、创建、删除文件时,内核就进行文件访问权限测试,这种测试可能涉及文件的所有者(st_uid和st_gid),进程的有效ID(有效用户ID,有效组ID)以及进程的附加组ID(如果支持的话)。两个所有者ID是文件的性质,而两个有效ID和和附加组ID则是进程的性质。

文件访问权限测试的过程为(按顺序执行这四步)

  • 若进程的有效用户ID是0(超级用户),则允许访问;
  • 若进程的有效用户ID等于文件所有者ID(也就是该进程拥有此文件),那么:若所有者适当的访问权限位被设置,则允许访问,否则拒绝访问;
  • 若进程的有效组ID或进程的附加组ID之一等于文件的组ID,那么,若组适当的访问权限位被设置,则允许访问,否则拒绝访问;
  • 若其他用户适当的访问权限位被设置,则允许访问,否则拒绝访问;

新文件或目录的所有权:

新文件的用户ID设置为进程的有效用户ID,关于组ID,POSIX.1允许实现选择下列之一作为新文件的组ID:

  • 新文件的组ID可以是进程的有效组ID;
  • 新文件的组ID可以是它所在目录的组ID;

对于Linux系统,新文件的组ID取决于它所在目录的设置组ID位是否被设置,如果该目录的这一位已经设置,则将新文件的组ID设置为目录的组ID,否则将新文件的组ID设置为进程的有效组ID。


你可能感兴趣的:(apue)