APUE学习笔记——文件和目录

函数statfstatfstatatlstat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#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 lstat(int fd, const char *restrict pathname, struct stat *restrict buf, int flag);

/* 所有4个函数的返回值:若成功,返回0;若出错,返回-1 */

sturct 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 links */
gid_t st_gid; /* group ID of owner */
off_t st_size; /* size in bytes, for regular files */
struct timespec st_atime; /* time of last access */
struct timespec st_mtime; /* time of last modification */
struct timespec st_ctime; /* time of last file status change */
blksize_t st_blksize; /* best I/O block size */
blkcnt_t st_blocks; /* number of disk blocks allocated */
}
  • 函数stat:返回pathname命名文件有关的信息结构;
  • 函数fstat:返回在描述符fd上打开文件的有关信息;
  • 函数lstat:类似于函数stat,但当命名的文件是一个符号链接时,lstat返回该符号连接的有关信息;
  • 函数fstatat:为一个相对于当前打开目录(由fd参数指向)的路径名返回文件统计信息。

文件信息包含stat结构的st_mode成员中。可以下表中的宏文件确定文件的类型。

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

文件类型

文件类型包括如下几类:

  1. 普通文件(regular file):包含了某种形式的数据;
  2. 目录文件(directory file):包含了其他文件的名字以及指向与这些文件有关信息的指针;
  3. 块特殊文件(block special file):该类型文件提供对设备(如磁盘)带缓冲的访问,每次访问以固定长度为单位进行;
  4. 字符特殊文件(character special file):该文件提供对设备不带缓冲的访问,每次访问长度不变。系统中的所有设备要么是字符特殊文件,要么是块特殊文件。
  5. FIFO:该文件用于进程通信,也称为命名管道(named pipe);
  6. 套接字(socket):该文件用于进程间的网络通信,也可以用在一台宿主机上进程间的非网络通信。
  7. 符号链接(symbolic link):该文件指向另一个文件。

设置用户ID和设置组ID

  • 实际用户ID和实际组ID标识我们究竟是谁。
  • 有效用户ID、有效组ID以及附属组ID决定了我们的文件访问权限。
  • 保存的设置用户ID和保存的设置组ID在执行一个程序时包含了有效用户ID和有效组ID的副本。

每个文件有一个所有者和组所有者,所有者由stat结构中的st_uid指定,组所有者则由st_gid指定。

文件访问权限

所有文件类型(目录、字符特别文件等)都有访问权限(access permisiion)。

st_mode屏蔽 含义
S_IRUSR 用户都
S_IWUSR 用户写
S_IXUSR 用户执行
S_IRGRP 组读
S_IWGRP 组写
S_IXGRP 组执行
S_IROTH 其他读
S_IWOTH 其他写
S_IXOTH 其他执行

函数accessfaccessat

1
2
3
4
#include <unistd.h>
int access(const char *pathname, int mode);
int faccessat(int fd, const char *pathname, int mode, int flag);
/* 两个函数的返回值:若成功,返回0;若出错,返回-1 */

accessfaccessat函数是按实际用户ID和实际组ID进行访问权限测试的。

mode 说明
R_OK 测试读权限
W_OK 测试写权限
X_OK 测试执行权限

函数umask

umask函数为进程设置文件模式创建屏蔽字(mask for the process),并返回之前的值。(这是少数几个没有出错返回函数中的一个。)

1
2
3
4
#include <sys/stat.h>

mode_t umask(mode_t cmask);
/* 返回值:之前的文件模式穿件屏蔽字 */

参数cmask是由上上个表列出的9个常量(S_IRUSR、S_IWUSR等)中若干个按位“或”构成的。

屏蔽位 含义
0400 用户读
0200 用户写
0100 用户执行
0040 组读
0020 组写
0010 组执行
0004 其他读
0002 其他写
0001 其他执行

函数chmodfchmodfchmodat

1
2
3
4
5
6
#include <sys/stat.h>

int chomd(const char *pathname, mode_t mode);
int fchome(int fd, mode_t mode);
int fchomdat(int fd, const char *pathname, mode_t mode, int flag);
/* 3个函数返回值:若成功,返回0;若出错,返回-1 */

chmod函数在指定的文件上进行操作,而fchmod函数则对已打开的文件进行操作。
fchomdat函数与chmod函数在下面两种情况下是相同的:一种是pathname参数为绝对路径,另一种是fd参数取值为AT_FDCWDpathname参数为相对路径。否则,fchmodat计算相对于打开目录(由fd参数指向)的pathname。flag参数可以用于改变fchmodat的行为,当设置了AT_SYMLIN_NOFOLLOW标志时,fchmodat并不会跟随符号链接。

mode 说明
S_ISUID 执行时设置用户ID
S_ISGID 执行时设置组ID
S_ISVTX 保存正文(粘着位)
S_IRWXU 用户(所有者)读、写和执行
S_IRUSR 用户(所有者)读
S_IWUSR 用户(所有者)写
S_IXUSR 用户(所有者)执行
S_IRWXG 组读、写和执行
S_IRGRP 组读
S_IWGRP 组写
S_IXGRP 组执行
S_IRWXO 其他读、写和执行
S_IROTH 其他读
S_IWOTH 其他写
S_IXOTH 其他执行

粘着位(sticky bit)

S_ISVTX位被称为粘着位:如果一个可执行的程序文件的这一位被设置了,那么当程序第一次被执行,在其终止时,程序正文的一个副本仍被保存在交换区。
后来的UNIX版本称为它为保存正文位(saved-text bit),因此也就有了常量S_ISVTX

函数chownfchownfchownatlchown

下面几个chown函数可用于更改文件的用户ID和组ID。如果两个参数ownergroup中的任意一个是-1,则对应的ID不变。

1
2
3
4
5
6
7
8
9
#include <unistd.h>

int chown(const char *pathname, uid_t owner, git_t group);
int fchown(int fd, uid_t owner, gid_t group);
int fchownat(int fd, const char *pathname, uid_t owner, gid_t group, int flag);
int lchown(const char *pathname, uid_t owner, gid_t group);
/*
* 4个函数的返回值:若辰宫,返回0;若出错,返回-1
*/

文件长度

stat结构成员st_size表示以字节为单位的单位长度。只对普通文件、目录文件和符号连接有效。

文件中的空洞

空洞是由所设置的偏移量超过文件端尾,并写入了某些数据后造成的。

文件截断

1
2
3
4
5
6
7
#include <unistd.h>

int truncate(const char *pathname, off_t length);
int ftruncate(int fd, off_t length);
/*
* 两个函数的返回值:若成功,返回0;若失败,返回-1
*/

如果该文件以前的长度大于length,则超过length以外的数据就不能再访问。如果以前的长度小于length,文件长度将增加,在以前的文件尾端和新的文件尾端之间的数据将读作0。

文件系统

函数linklinkatunlinkunlinkatremove

1
2
3
4
5
#include <unistd.h>

int link(const char *existingpath, const char *newpath);
int linkat(int efd, const char *existingpath, int nfd, const char *newpath, int flag);
/* 两个函数的返回值:若成功,返回0;若出错,返回-1 */

这两个函数创建一个新目录项newpath,它引用现有文件existingpath。如果newpath已存在,则返回出错。
对于linkat函数,现有文件是通过efdexistingpath指定,新的路径名是通过nfdnewpath指定的。

1
2
3
4
5
#include <unistd.h>

int unlink(const char *pathname);
int unlinkat(int fd, const char *pathname, int flag);
/* 两个函数的返回值:若成功,返回0;若出错,返回-1 */

这两个函数删除目录项,并将由pathname所引用的链接数减1。

1
2
3
4
#include <stdio.h>

int remove(const char *pathname);
/* 返回值:若成功,返回0;若失败,返回-1 */

函数renamerenameat

1
2
3
4
#include <stdio.h>

int rename(const char *oldname, const char *newname);
int renameat(int oldfd, const char *oldname, int newfd, const char *newname);
  1. 如果oldname指的是一个文件而不是目录,那么为该文件或符号连接重命名。
  2. 如果oldname指的是一个空目录,那么为该目录重命名。
  3. 如果oldnamenewname引用符号连接,则处理的是符号链接本身,而不是它引用的文件。
  4. 不能对...重命名。
  5. 作为一个特例,如果oldnamenewname引用同意文件,则函数不做任何更改而成功返回。

符号链接

符号链接是对一个文件的间接指针。符号链接以及它指向何种对象并无任何文件系统限制,任何用户都可以创建指向目录的符号连接。

创建和读取符号连接

1
2
3
4
5
#include <unistd.h>

int symlink(const char *actualpath, const char *sympath);
int symlinat(const char *acutalpath, int fd, const char *symptah);
/* 两个函数的返回值:若成功,返回0;若出错,返回-1 */
1
2
3
4
5
#include <unistd.h>

ssize_t readlink(const char *actualpath, const char *symptah);
sszie_t readlinkat(int fd, const char* restrict pathname, char *restrict buf, size_t bufsize);
/* 两个函数的返回值:若成功,返回读取的字节数;若出错,返回-1 */

文件时间

字段 说明 例子 ls(1)选项
st_atim 文件数据的最后访问时间 read -u
st_mtim 文件数据的最后修改时间 write 默认
st_ctim i节点状态的最后更改时间 chomd、chown -c

函数futimensutimensatutimes

1
2
3
4
5
#include <sys/stat.h>

int futimens(int fd, const struct timespec times[2]);
int utimensat(int fd, const char *path, const struct timespec times[2], int flag);
/* 两个函数返回值:若成功,返回0;若出错,返回-1 */

函数mkdirmkdiratrmdir

两个函数创建一个新目录。

1
2
3
4
5
#include <sys/stat.h>

int mkdir(const char *pathname, mode_t mode);
int mkdir(int fd, const char *pathname, mode_t mode);
/* 两个函数返回值:若成功,返回0;若出错,返回-1 */

rmdir函数可以创建一个空目录。

1
2
3
4
#include <unistd.h>

int rmdir(const char *pathname);
/* 返回值:若成功,返回0;若失败,返回-1 */

读目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <dirent.h>

DIR *opendir(const char *pathname);
DIR *fdopendir(int fd);
/* 两个函数返回值:若成功,返回指针;若出错,返回NULL */

struct dirent *readdir(DIR *dp);
/* 返回值:若成功,返回指针;若在目录尾或出错,返回NULL */

void rewinddir(DIR *dp);
int closeddir(DIR *dp);
/* 返回值:若成功,返回0;若失败,返回-1 */

long telldir(DIR *dp);
/* 返回值:与dp关联的相关位置 */

void seekdir(DIR *dp, long loc);

函数chdirfchdirgetcwd

1
2
3
4
5
#include <unistd.h>

int chdir(const char *pathname);
int fchdir(int fd);

进程调用chdirfchdir函数更改当前工作目录。

1
2
3
#include <unistd.h>

char *getcwd(char *buf, size_t size);

逐层上移,直到遇到根,得到当前工作目录完整的绝对路径。
必须像此函数传递两个函数,一个是缓冲区地址buf(要有足够的长度容纳绝对路径再加上一个终止null字节),另一个是缓冲区的长度size(以字节为单位)。