C4: 文件和目录
本章主要讨论stat函数及其返回信息,通过修改stat结构字段,了解文件属性。
struct stat结构定义如下:
1 struct stat 2 { 3 __dev_t st_dev; /* Device. */ 4 #ifndef __x86_64__ 5 unsigned short int __pad1; 6 #endif 7 #if defined __x86_64__ || !defined __USE_FILE_OFFSET64 8 __ino_t st_ino; /* File serial number. */ 9 #else 10 __ino_t __st_ino; /* 32bit file serial number. */ 11 #endif 12 #ifndef __x86_64__ 13 __mode_t st_mode; /* File mode. */ 14 __nlink_t st_nlink; /* Link count. */ 15 #else 16 __nlink_t st_nlink; /* Link count. */ 17 __mode_t st_mode; /* File mode. */ 18 #endif 19 __uid_t st_uid; /* User ID of the file's owner. */ 20 __gid_t st_gid; /* Group ID of the file's group.*/ 21 #ifdef __x86_64__ 22 int __pad0; 23 #endif 24 __dev_t st_rdev; /* Device number, if device. */ 25 #ifndef __x86_64__ 26 unsigned short int __pad2; 27 #endif 28 #if defined __x86_64__ || !defined __USE_FILE_OFFSET64 29 __off_t st_size; /* Size of file, in bytes. */ 30 #else 31 __off64_t st_size; /* Size of file, in bytes. */ 32 #endif 33 __blksize_t st_blksize; /* Optimal block size for I/O. */ 34 #if defined __x86_64__ || !defined __USE_FILE_OFFSET64 35 __blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */ 36 #else 37 __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ 38 #endif 39 #ifdef __USE_XOPEN2K8 40 /* Nanosecond resolution timestamps are stored in a format 41 equivalent to 'struct timespec'. This is the type used 42 whenever possible but the Unix namespace rules do not allow the 43 identifier 'timespec' to appear in the <sys/stat.h> header. 44 Therefore we have to handle the use of this header in strictly 45 standard-compliant sources special. */ 46 struct timespec st_atim; /* Time of last access. */ 47 struct timespec st_mtim; /* Time of last modification. */ 48 struct timespec st_ctim; /* Time of last status change. */ 49 # define st_atime st_atim.tv_sec /* Backward compatibility. */ 50 # define st_mtime st_mtim.tv_sec 51 # define st_ctime st_ctim.tv_sec 52 #else 53 __time_t st_atime; /* Time of last access. */ 54 __syscall_ulong_t st_atimensec; /* Nscecs of last access. */ 55 __time_t st_mtime; /* Time of last modification. */ 56 __syscall_ulong_t st_mtimensec; /* Nsecs of last modification. */ 57 __time_t st_ctime; /* Time of last status change. */ 58 __syscall_ulong_t st_ctimensec; /* Nsecs of last status change. */ 59 #endif 60 #ifdef __x86_64__ 61 __syscall_slong_t __glibc_reserved[3]; 62 #else 63 # ifndef __USE_FILE_OFFSET64 64 unsigned long int __glibc_reserved4; 65 unsigned long int __glibc_reserved5; 66 # else 67 __ino64_t st_ino; /* File serial number. */ 68 # endif 69 #endif 70 };
1 函数stat、fstat、fstatat、lstat
获取文件属性,头文件 sys/stat.h。成功返回0,失败返回-1。
1 /* Get file attributes for FILE and put them in BUF. */ 2 extern int stat (const char *__restrict __file, 3 struct stat *__restrict __buf) __THROW __nonnull ((1, 2)); 4 5 /* Get file attributes for the file, device, pipe, or socket 6 that file descriptor FD is open on and put them in BUF. */ 7 extern int fstat (int __fd, struct stat *__buf) __THROW __nonnull ((2)); 8 9 /* Get file attributes about FILE and put them in BUF. 10 If FILE is a symbolic link, do not follow it. */ 11 extern int lstat (const char *__restrict __file, 12 struct stat *__restrict __buf) __THROW __nonnull ((1, 2)); 13 14 /* Similar to stat, get the attributes for FILE and put them in BUF. 15 Relative path names are interpreted relative to FD unless FD is 16 AT_FDCWD. */ 17 # ifndef __USE_FILE_OFFSET64 18 extern int fstatat (int __fd, const char *__restrict __file, 19 struct stat *__restrict __buf, int __flag) 20 __THROW __nonnull ((2, 3));
以上4个函数区别:
- stat返回文件名相关的文件属性
- fstat返回文件描述符相关的文件属性
- lastat与符号链接有关,如果输入参数文件名是一个文件链接,则返回符号链接的信息,而非文件信息
- fstatat为相对一个打开目录,flag参数控制是否跟随一个符号链接
2 文件类型
对应stat结构的字段 __mode_t st_mode; /* File mode. */
UNIX的文件类型有以下几种:
类型 | 类型识别宏 | 释义 |
普通文件 | S_ISREG() | 文本或者二进制文件 |
目录文件 | S_ISDIR() | 目录 |
块特殊文件 | S_ISBLK() | 此类型文件提供对设备带缓冲的访问 |
字符特殊文件 | S_ISCHR() | 此类型文件提供对设备不带缓冲的访问 |
FIFO | S_ISFIFO() | 进程间通信,也称管道 |
套接字 | S_ISSOCK() | 网络通信 |
符号链接 | S_ISLINK() | 此类型文件指向另一个文件 |
用法为S_ISREG(stat.st_mode)
3 设置用户ID和组ID
注意,此为进程属性。
通常,进程的有效用户ID就是实际用户ID,有效组ID就是实际组ID。stat中的两个ID是文件拥有者ID和组ID
19 __uid_t st_uid; /* User ID of the file's owner. */
20 __gid_t st_gid; /* Group ID of the file's group.*/
4 文件访问权限
stat结构的st_mode也包含了文件的访问权限。文件都有9个访问权限位,stat.h中定义为9个宏,如下图:
上图前3行,用户指文件所有者。用u表示用户,g表示组,o表示其他,与chmod命令保持一致。
文件权限规则:
- 执行权限:名字路径中的任一目录,包括它可能隐含的当前工作目录都应有执行权限。其中,目录读权限指的是访问目录内容(读目录文件列表),目录执行权限指的是搜索(通过该目录)
- 读权限:是否能够打开文件进行读操作
- 写权限:是否能够打开文件进行写操作。如果open中对一个文件制定O_TRUNK,则必须对该文件具有写权限
- 如需创建一个新文件,则必须对文件所在目录具有写权限和执行权限
- 如需删除一个文件,则必须对文件所在目录具有写权限和执行权限,对文件本身不需要有读写权限
文件有拥有者ID st_uid和拥有者组ID st_gid,此为文件属性。
进程有有效ID和组ID。
访问文件时,两组ID进行判断,觉得进程是否拥有文件使用权。规则为:
- 若进程的有效用户ID是0(超级用户),则允许访问
- 若进程的有效ID等于文件所有者ID,则允许访问
- 若进程的组ID等于文件所有者组ID,则允许访问
- 其他用户的访问权限位被设置,则允许访问,否则拒绝
创建新文件时
- 新文件的用户ID设置为进程有效用户ID
- 新文件的组ID可以设置为进程的有效组ID,或者是所在目录的组ID,依赖系统
5 函数access和faccessat
按照进程实际用户ID和实际组ID进行访问权限测试文件权限,头文件 unistd.h。成功返回0,失败返回-1
1 /* Test for access to NAME using the real UID and real GID. */ 2 extern int access (const char *__name, int __type) __THROW __nonnull ((1)); 3 4 /* Test for access to FILE relative to the directory FD is open on. 5 If AT_EACCESS is set in FLAG, then use effective IDs like `eaccess', 6 otherwise use real IDs like `access'. */ 7 extern int faccessat (int __fd, const char *__file, int __type, int __flag)
操作类型为:
操作类型 | 说明 |
F_OK | 测试文件是否存在 |
R_OK | 测试读权限 |
W_OK | 测试写权限 |
X_OK | 测试执行权限 |
faccessat函数,如果flag设置为AT_EACCESS,访问检查用的是调用有效用户ID和有效组ID,而不是实际用户ID和实际组ID。
6 函数umask
为进程设置文件模式创建屏蔽字,并返回之前的值。头文件sys/stat.h,返回之前的文件屏蔽字
1 /* Set the file creation mask of the current process to MASK, 2 and return the old creation mask. */ 3 4 extern __mode_t umask (__mode_t __mask) __THROW;
参数mask为表4-4按位或运算得来。
7 函数chmod、fchmod、fchmodat
更改现有文件的访问权限,头文件sys/stat.h。成功返回0,失败返回-1。
1 /* Set file access permissions for FILE to MODE. 2 If FILE is a symbolic link, this affects its target instead. */ 3 extern int chmod (const char *__file, __mode_t __mode) 4 __THROW __nonnull ((1)); 5 6 /* Set file access permissions of the file FD is open on to MODE. */ 7 #if defined __USE_POSIX199309 || defined __USE_XOPEN_EXTENDED 8 extern int fchmod (int __fd, __mode_t __mode) __THROW; 9 #endif 10 11 /* Set file access permissions of FILE relative to 12 the directory FD is open on. */ 13 extern int fchmodat (int __fd, const char *__file, __mode_t __mode, 14 int __flag) 15 __THROW __nonnull ((2)) __wur;
参数mode是下图所示常量的按位或。
8 函数chown、fchown、fchownat、lchown
用于改变文件的用户ID和组ID,头文件 unistd.h。成功返回0,失败返回-1。
1 /* Change the owner and group of FILE. */ 2 extern int chown (const char *__file, __uid_t __owner, __gid_t __group) 3 __THROW __nonnull ((1)) __wur; 4 5 /* Change the owner and group of the file that FD is open on. */ 6 extern int fchown (int __fd, __uid_t __owner, __gid_t __group) __THROW __wur; 7 8 /* Change the owner and group of FILE relative to the directory FD is open 9 on. */ 10 extern int fchownat (int __fd, const char *__file, __uid_t __owner, 11 __gid_t __group, int __flag) 12 __THROW __nonnull ((2)) __wur; 13 14 /* Change owner and group of FILE, if it is a symbolic 15 link the ownership of the symbolic link is changed. */ 16 extern int lchown (const char *__file, __uid_t __owner, __gid_t __group) 17 __THROW __nonnull ((1)) __wur;
如果两个参数中任意一个是-1,则对于ID不变。
9 文件长度
stat结构成员st_size表示以字节为单位的文件长度,此字段对普通文件、目录文件、符号链接有效。
10 函数truncate文件截断
可以改变文件长度,头文件unistd.h。成功返回0,失败返回-1。
1 /* Truncate FILE to LENGTH bytes. */ 2 # ifndef __USE_FILE_OFFSET64 3 extern int truncate (const char *__file, __off_t __length) 4 __THROW __nonnull ((1)) __wur;
11 函数link、linkat、unlink、unlinkat、remove
创建一个指向现有文件i节点的链接,头文件unistd.h。成功返回0,失败返回-1。
这种链接方式,直接指向文件i节点,使得文件i节点上的链接计数__nlink_t st_nlink; /* Link count. */增加。
1 /* Make a link to FROM named TO. */ 2 extern int link (const char *__from, const char *__to) __THROW __nonnull ((1, 2)) __wur; 3 4 /* Like link but relative paths in TO and FROM are interpreted relative 5 to FROMFD and TOFD respectively. */ 6 extern int linkat (int __fromfd, const char *__from, int __tofd, const char *__to, int __flags) 7 __THROW __nonnull ((2, 4)) __wu
unlink可以删除一个现有目录项,将所引文件的链接计数减1。成功返回0,失败返回-1。
1 /* Remove the link NAME. */ 2 extern int unlink (const char *__name) __THROW __nonnull ((1)); 3 4 /* Remove the link NAME relative to FD. */ 5 extern int unlinkat (int __fd, const char *__name, int __flag) 6 __THROW __nonnull ((2));
只有文件的链接计数达到0,文件的内容才可以被删除。
如果unlinkat的参数flag被设置为AT_REMOVEDIR时,unlinkat函数可以类似rmdir一样删除目录。
remove函数解除对一个文件或者目录的链接,头文件stdio.h。成功返回0,失败返回-1。
对于文件,remove的功能与unlink类似。对于目录,remove的功能与rmdir类似。
1 /* Remove file FILENAME. */ 2 3 extern int remove (const char *__filename) __THROW;
opencreat创建新临时文件后,立即调用unlink,可以保证临时文件在程序奔溃时也可以被删除。
12 函数rename、renameat
rename、renameat可用于对文件或者目录重命名,头文件stdio.h。成功返回0,失败返回-1。
1 /* Rename file OLD to NEW. */ 2 extern int rename (const char *__old, const char *__new) __THROW; 3 4 /* Rename file OLD relative to OLDFD to NEW relative to NEWFD. */ 5 extern int renameat (int __oldfd, const char *__old, int __newfd, const char *__new) __THROW;
13 符号链接
符号链接比i节点的链接更加灵活,有以下两个原因:
- i节点链接通常要求链接和文件在同一文件系统
- 通常只有超级用户才能创建i节点的链接
可以用symlink或symlinkat函数创建一个符号链接,头文件unistd.h。成功返回0,失败返回-1。
1 /* Make a symbolic link to FROM named TO. */ 2 extern int symlink (const char *__from, const char *__to) 3 __THROW __nonnull ((1, 2)) __wur; 4 5 /* Like symlink but a relative path in TO is interpreted relative to TOFD. */ 6 extern int symlinkat (const char *__from, int __tofd, 7 const char *__to) __THROW __nonnull ((1, 3)) __wur;
函数创建一个指向from文件路径的新目录项to。
由于open方法访问符号链接时,将访问到符号链接引用的文件。为了访问符号链接本身,需要使用readlink和readlinkat函数,头文件unistd.h。成功返回读取的字节数,失败返回-1。
1 /* Read the contents of the symbolic link PATH into no more than 2 LEN bytes of BUF. The contents are not null-terminated. 3 Returns the number of characters read, or -1 for errors. */ 4 extern ssize_t readlink (const char *__restrict __path, 5 char *__restrict __buf, size_t __len) 6 __THROW __nonnull ((1, 2)) __wur; 7 8 /* Like readlink but a relative PATH is interpreted relative to FD. */ 9 extern ssize_t readlinkat (int __fd, const char *__restrict __path, 10 char *__restrict __buf, size_t __len) 11 __THROW __nonnull ((2, 3)) __wur;
14 文件时间
stat结构有3个时间:
- struct timespec st_atim; /* Time of last access. */ 文件访问时间
- struct timespec st_mtim; /* Time of last modification. */ 文件修改时间,指的是文件内容被修改
- struct timespec st_ctim; /* Time of last status change. */ i节点状态变更时间,节点最后修改时间,如访问权限、用户ID、链接数等
文件访问时间和修改时间可以用futimens和utimensat函数更改,头文件sys/stat.h。成功返回0,失败返回-1。
1 /* Set file access and modification times of the file associated with FD. */ 2 extern int futimens (int __fd, const struct timespec __times[2]) __THROW; 3 4 /* Set file access and modification times relative to directory file 5 descriptor. */ 6 extern int utimensat (int __fd, const char *__path, const struct timespec __times[2], int __flags) __THROW __nonnull ((2));
timespec结构定义为:
1 struct timespec 2 { 3 __time_t tv_sec; /* Seconds. */ 4 __syscall_slong_t tv_nsec; /* Nanoseconds. */ 5 };
函数futimens数组参数的第一个元素包含访问时间,第二个参数包含修改时间。
- 如果times是空指针,则设置访问时间和修改时间为当前时间
- 如果times数组的任一元素tv_nsec为UTIME_NOW,则忽略tv_sec字段,时间戳设置为当前时间
- 如果times数组的任一元素tv_nsec为UTIME_OMIT,则忽略tv_sec字段,时间戳保持不变
- 如果times数组的任一元素tv_nsec不是UTIME_OMIT和TIME_NOW,则设置时间戳为tv_sec和tv_nsec
15 目录操作:创建、删除、读取、切换
使用函数mkdir、mkdirat、rmdir可以创建或者删除目录,头文件sys/stat.h。成功返回0,失败返回-1。
1 /* Create a new directory named PATH, with permission bits MODE. */ 2 extern int mkdir (const char *__path, __mode_t __mode) 3 __THROW __nonnull ((1)); 4 5 /* Like mkdir, create a new directory with permission bits MODE. But 6 interpret relative PATH names relative to the directory associated 7 with FD. */ 8 extern int mkdirat (int __fd, const char *__path, __mode_t __mode) 9 __THROW __nonnull ((2)); 10 11 /* Remove the directory PATH. */ 12 extern int rmdir (const char *__path) __THROW __nonnull ((1));
注意:
- 创建目录至少应该指定一个执行权限位
- rmdir只能删除空目录
对某一目录具有访问权限的任一用户都可以读目录,但是只有内核才可以写目录。目录操作头文件dirent.h。
1 /* Open a directory stream on NAME. 2 Return a DIR stream on the directory, or NULL if it could not be opened. 3 This function is a possible cancellation point and therefore not 4 marked with __THROW. */ 5 extern DIR *opendir (const char *__name) __nonnull ((1)); 6 7 8 /* Same as opendir, but open the stream on the file descriptor FD. 9 This function is a possible cancellation point and therefore not 10 marked with __THROW. */ 11 extern DIR *fdopendir (int __fd); 12 13 14 /* Read a directory entry from DIRP. Return a pointer to a `struct 15 dirent' describing the entry, or NULL for EOF or error. The 16 storage returned may be overwritten by a later readdir call on the 17 same DIR stream. 18 If the Large File Support API is selected we have to use the 19 appropriate interface. 20 This function is a possible cancellation point and therefore not 21 marked with __THROW. */ 22 extern struct dirent *readdir (DIR *__dirp) __nonnull ((1)); 23 24 25 /* Rewind DIRP to the beginning of the directory. */ 26 extern void rewinddir (DIR *__dirp) __THROW __nonnull ((1)); 27 28 29 /* Close the directory stream DIRP. 30 Return 0 if successful, -1 if not. 31 This function is a possible cancellation point and therefore not 32 marked with __THROW. */ 33 extern int closedir (DIR *__dirp) __nonnull ((1)); 34 35 36 /* Return the current position of DIRP. */ 37 extern long int telldir (DIR *__dirp) __THROW __nonnull ((1)); 38 39 40 /* Seek to position POS on DIRP. */ 41 extern void seekdir (DIR *__dirp, long int __pos) __THROW __nonnull ((1));
每个进程都有一个初始工作目录。起始目录是登录名的属性,而当前工作目录是进程的属性。
使用chdir可以切换当前工作目录,头文件unistd.h。成功返回0,失败返回-1。
1 /* Change the process's working directory to PATH. */ 2 extern int chdir (const char *__path) __THROW __nonnull ((1)) __wur; 3 4 /* Change the process's working directory to the one FD is open on. */ 5 extern int fchdir (int __fd) __THROW __wur;
使用getcwd可以获取当前工作目录,头文件unistd.h。成功返回0,失败返回-1。
1 /* Get the pathname of the current working directory, 2 and put it in SIZE bytes of BUF. Returns NULL if the 3 directory couldn't be determined or SIZE was too small. 4 If successful, returns BUF. In GNU, if BUF is NULL, 5 an array is allocated with `malloc'; the array is SIZE 6 bytes long, unless SIZE == 0, in which case it is as 7 big as necessary. */ 8 extern char *getcwd (char *__buf, size_t __size) __THROW __wur;