函数stat、fstat、fstatat和lstat
#include <sys/stat.h>
int stat(const char *restrict pathname, struct stat *restrict
buf);
int fstat(int fd, struct stat *buf);
int lstat(const chat *restrict pathname, struct stat *restrict
buf);
int fstatat(int fd, const char *restrict pathname, struct stat
*restrict buf, int flag);
//所有4个函数的返回值:若成功,返回0;若出错,返回-1
一旦给出pathname,stat函数将返回与此命名文件有关的信息结构。fstat函数获得已在描述符fd上打开文件的有关信息。lstat函数类似于stat,但是当命名函数是一个符号连接时,lstat返回该符号链接的有关信息,而不是由该符号引用的文件的信息。
fstatat函数为一个相对于当前打开目录(有fd参数指向)的路径名返回文件统计信息。flag参数控制着是否跟随着一个符号链接。当AT_SYMLINK_NOFOLLOW标志被设置时,fstatat不会跟随符号链接,而是返回符号链接本身的信息。否则,在默认情况下,返回的是符号链接所指向的实际文件的信息。如果fd参数的值是AT_FDCWD,并且pathname参数是一个相对路径名,fstatat会计算相对于当前目录的pathname参数。如果pathname是一个绝对路径,fd参数就会被忽略。这两种情况下,根据flag的取值,fstatat的作用就跟stat或lstat一样。
struct stat{
mode_t st_mode; /*file type or mode*/
ino_t st_ino; /*i-node number*/
dev_t st_dev; /*device number*/
dev_t st_rdev; /*device number for special files*/
nlink_t st_nlink; /*number of link*/
uid_t st_uid; /*user ID of owner*/
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*/
}
文件类型
文件类型包括如下几种:
1. 普通文件(regular file)。
2. 目录文件(directory file)。
3. 块特殊文件(block special file)。这种类型的文件提供对设备带缓冲的访问,每次访问以固定长度为单位。
4. 字符特殊文件(character special file)。这种类型的文件提供对设备不带缓冲的访问,每次访问长度可变。系统中的所有设备要么是字符特殊文件,要么是块特殊文件。
5. FIFO。这种类型的文件用于进程间通信,有时也称为命名管道。
6. 套接字(socket)
7. 符号链接(symbolic link)。这种类型的文件指向另一个文件。
设置用户ID和设置组ID
与一个进程相关联的ID有6个或更多
实际用户ID 实际组ID | 我们实际上是谁 |
---|---|
有效用户ID 有效组ID 附属组ID | 用于文件访问权限检查 |
保存的设置用户ID 保存的设置组ID | 由exec函数保存 |
实际用户ID和实际组ID在登录时取自口令文件中的登录项。通常,在一个登录会话期间这些值并不改变,但是超级用户进程有方法改变他们。
有效用户ID、有效组ID以及附属组ID决定了我们的文件访问权限。
保存的设置用户ID和保存的设置组ID在执行一个程序时包含了有效用户ID和有效组ID的副本。
文件访问权限
st_mode 屏蔽 | 含义 |
---|---|
S_IRUSR | 用户读 |
S_IWUSR | 用户写 |
S_IXUSR | 用户执行 |
S_IRGRP | 组读 |
S_IWGRP | 组写 |
S_IXGRP | 组执行 |
S_IROTH | 其他读 |
S_IWOTH | 其他写 |
S_IXOTH | 其他执行 |
新文件的目录的所有权
新用户ID设置为进程的有效用户ID。
1. 新文件的组ID可以是进程的有效组ID。
2. 新文件的组ID可以是它所在目录的组ID
函数access和faccessat
access和faccessat函数是按实际用户ID和实际组ID进行访问权限测试的。
#include <unistd.h>
int access(const char *pathname, int mode);
int faccessat(int fd, const char *pathname, int mode, int flag);
//两个函数的返回值:若成功,返回0;若出错,返回-1
其中,如果测试文件是否已经存在,mode就为F_OK否则mode是下表中所列常量的按位或。
mode | 说明 |
---|---|
R_OK | 测试读权限 |
W_OK | 测试写权限 |
X_OK | 测试执行权限 |
faccessat函数与access函数在下面两种情况下是相同的:一种是pathname参数为绝对路径,另一种是fd参数取值为AT_FDCWD而pathname参数为相对路径。否则,faccessat计算相对于打开目录(由fd参数指向)的pathname。
flag参数可以用于改变faccessat的行为,如果flag设置为AT_EACCESS,访问检查用的是调用进程的有效用户ID和有效组ID,而不是实际用户ID和实际组ID。
函数umask
umask函数为进程设置文件模式创建屏蔽字,并返回之前的值
#include <sys/stat.h>
mode_t umask(mode_t cmask);
//返回值:之前的文件模式创建屏蔽字
在文件模式创建屏蔽字中为1的位,在文件mode中的相应位一定被关闭。
函数chmod、fchmod和fchmodat
这三个函数使我们可以更改现有文件的访问权限。
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
int fchmodat(int fd, const char *pathname, mode_t mode,int
flag);
//3个函数的返回值:若成功,返回0;若出错,返回-1
chmod函数在指定的文件上进行操作,而fchmod函数则对已打开的文件进行操作。fchmodat函数与chmod函数在下面两种情况下是相同的:一种是pathname参数为绝对路径,另一种是fd参数取值为AT_FDCWD而pathname参数为相对路径。否则,fchmodat计算相对于打开目录的pathname。flag参数可以用于改变fchmodat的行为,当设置了AT_SYMLINK_NOFOLLOW标志时,fchmodat并不会跟随符号链接。
为了改变一个文件的权限位,进程的有效用户ID必须等于文件的所有者ID,或者该进程必须具有超级用户权限。
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 | 其他执行 |
粘着位
如果一个可执行程序文件的这一位被设置了,那么当该程序第一次被执行,在其终止时,程序正文部分的一个副本仍被保存在交换区。这使得下次执行该程序时能较快地将其装载入内存。
如果对一个目录设置了粘着位,只有对该目录具有写权限的用户并且满足下列条件之一,才能删除或重命名该目录下的文件:
1. 拥有此文件
2. 拥有此目录
3. 是超级用户
chown、fchown、fchownat和lchown
下面几个chown函数可用于更改文件的用户ID个组ID。如果两个参数owner和group中的任意一个是-1,则对应的ID不变。
#include <unistd.h>
int chown(const char *pathname, uid_t owner, gid_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表示以字节为单位的文件的长度。此字段只对普通文件、目录文件和符号链接有意义。
文件中的空洞
空洞是由所设置的便宜量超过文件尾端,并写入了某些数据后造成的。
文件截断
为了截断文件可以调用函数truncate和ftruncate
#include <unistd.h>
int truncate(const char *pathname, off_t length);
int ftruncate(int fd, off_t length);
//两个函数的返回值:若成功,返回0;若出错,返回-1
这两个函数将一个现有文件长度截断为length。如果该文件以前的长度大于length,则超过length以外的数据就不再能访问。
文件系统
。。。。自行百度
函数link、linkat、unlink、unlinkat和remove
任何文件都可以有多个目录项指向其i节点。创建一个指向现有文件的链接的方法是使用link函数或linkat函数
#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已经存在,则返回出错。只创建newpath中的最后一个分量,路径中的其他部分应当已经存在。
对于linkat函数,现有文件是通过efd和existingpath参数指定的,新的路径名是通过nfd和newpath参数指定的。默认情况下,如果两个路径名中的任一个是相对路径,那么它需要通过相对于对应的文件描述符进行计算。如果两个文件描述符中的任一个设置为AT_FDCWD,那么相应的路径名就通过相对于当前目录进行计算。如果任一路径名是绝对路径,相应的文件描述符参数就会被忽略。
当现有文件是符号连接时,由flag参数来控制linkat函数是创建指向现有符号链接的链接还是创建指向现有符号链接所指向的文件的链接。如果在flag参数中设置了AT_SYMLINK_FOLLOW标志,就创建指向符号链接目标的链接。如果这个标志被清除了,则创建一个指向符号链接本身的链接。
为了删除一个现有的目录项,可以调用unlink函数
#include <unistd.h>
int unlink(const char *pathname);
int unlink(int fd, const char *pathname, int flag);
//两个函数的返回值:若成功,返回0;若出错,返回-1
这两个函数删除目录项,并将有pathname所引用文件的链接计数减1。其他链接仍是可以访问的。
只有当链接计数达到0时,该文件内容才可被删除。
我们也可以用remove函数解除对一个文件或目录的链接。对于文件,remove的功能与unlink相同。对于目录,remove的功能与rmdir相同。
#include <stdio.h>
int remove(const char *pathname);
//返回值:若成功,返回0;若出错,返回-1
函数rename和renameat
文件或目录可以用rename函数或者renameat函数进行重命名。
#include <stdio.h>
int rename(const char *oldname, const char *newname);
int renameat(int oldfd, const char *oldname, int newfd, const char *newname);
//两个函数的返回值:若成功,返回0;若出错,返回-1
符号链接
引入符号链接的原因是为了避开硬件链接的一些限制。
…
创建和读取符号链接
可以用symlink或symlinkat函数创建一个符号链接
#include <unistd.h>
int symlink(const char *actualpath, const char *sympath);
int symlinkat(const char *actualpath, int fd, const char
sympath);
//两个函数的返回值:若成功,返回0;若出错,返回-1
函数创建了一个指向actualpath的新目录项sympath。在创建此符号链接时,并不要求actualpath已经存在。并且,actualpath和sympath并不需要位于同一文件系统中。
因为open函数跟随符号链接,所以需要有一种方法打开该链接本身,并读该链接中的名字。readlink和readlinkat函数提供了这种功能。
#include <unistd.h>
ssize_t readlink(const char *restrict pathname, char *restrict buf, size_t bufsize);
ssize_t readlinkat(int fd, const char *restrict pathname, char *restrict buf, size_t bufsize);
//两个函数的返回值:若成功,返回读取的字节数;若出错,返回-1
两个函数组合了open、read和close的所有操作。如果函数成功执行,则返回读入buf的字节数。在buf中返回的符号链接的内容不以NULL字节终止。
当pathname参数指定的是绝对路径名或者fd参数的值为AT_FDCWD,readlinkat函数的行为与readlink相同。但是,如果fd参数是一个打开目录的有效文件描述符并且pathname参数是相对路径名,则readlinkat计算相对于由fd代表的打开目录的路径名。
函数futimens、utimensat和utimes
一个文件的访问和修改时间可以用一下几个函数更改。futimens和utimensat函数可以指定纳秒级精度的时间戳。用到的数据结构是与stat函数族相同的timespec结构
#include <sys/stat.h>
int futimens(int fd, const struct timespec time[2]);
int utimensat(int fd, const char *path, const struct timespec time[2], int flag);
//两个函数返回值:若成功,返回0;若出错,返回-1
这两个函数的times数组参数的第一个元素包含访问时间,第二元素包含修改时间。这两个时间值是日历时间。
时间戳可以按下列4中方式之一进行指定。
1. 如果times参数是一个空指针,则访问时间和修改时间两者都设置为当前时间。
2. 如果times参数指向两个timespec结构的数组,任一数组元素的tv_nsec字段的值为UTIME_NOW,相应的时间戳就设置为当前时间,忽略相应的tv_sec字段。
3. 如果times参数指向两个timespec结构的数组,任一数组元素的tv_nsec字段的值为UTIME_OMIT,相应的时间戳保持不变,忽略相应的tv_sec字段。
4. 如果times参数指向两个timespec结构的数组,且tv_nsec字段的值为既不是UTIME_NOW也不是UTIME_OMIT,在这种情况下,相应的时间戳设置为相应的tv_sec和tv_nsec字段的值。
#include <sys/time.h>
int utimes(const char *pathname, const struct timeval time[2]);
//函数返回值:若成功,返回0;若出错,返回-1
utimes函数对路径名进行操作。times参数是指向包含两个时间戳元素的数组的指针,两个时间戳是用秒和微妙表示的。
下面的程序使用带O_TRUNC选项的open函数将文件长度截断为0,但并不更改其访问时间及修改时间。
#include "apue.h"
#include <fcntl.h>
int main(int argc, char *argv[])
{
int i,fd;
struct stat statbuf;
struct timespec time[2];
for(i = 1; i < argc ; i++)
{
if(stat(argv[i], &statbuf) < 0){
err_ret("%s:stat error",argv[i]);
continue;
}
if((fd = open(argv[i], O_RDWR|O_TRUNC)) < 0){
err_ret("%s:open error",argv[i]);
continue;
}
times[0] = statbuf.st_atim;
times[1] = statbuf.st_mtim;
if(futimens(fd, times) < 0)
err_ret("%s:futimens error",argv[i]);
close(fd);
}
exit(0);
}
函数makdir、makdirat和rmdir
用mkdir和mkdirat函数创建目录,用rmdir函数删除目录。
#include <sys/stat.h>
int mkdir(const char *pathname, mode_t mode);
int mkdirat(int fd, const char *pathname, mode_t mode);
//两个函数返回值:若成功,返回0;若出错,返回-1
这两个函数创建一个新的空目录。其中,. 和 . . 目录项是自动创建的。所指定的文件访问权限mode由进程的文件模式创建屏蔽字修改。
#include <unistd.h>
int rmdir(const char *pathname);
//返回值:若成功,返回0;若失败,返回-1
读目录
#include <dirent.h>
DIR *opendir(const char *pathname);
DIR *fdopendir(int fd);
//两个函数的返回值:若成功,返回指针;若出错,返回NULL
struct dirent *readdir(DIR *dp);
//返回值:若成功,返回指针;若在目录尾或出错,返回NULL
void rewinddir(DIR *dp);
int closedir(DIR *dp);
//返回值:若成功,返回0;若出错,返回-1
long telldir(DIR *dp);
//返回值:与dp关联的目录中的当前位置
void seekdir(DIR *dp, long loc);
函数chdir、fchdir和getcwd
#include <unistd.h>
int chdir(const char *pathname);
int fchdir(int fd);
//两个函数的返回值:若成功,返回0;若出错,返回-1
在这个两个函数中,分别用pathname或打开文件描述符来指定新的当前工作目录。
得到当前工作目录完整的绝对路径名
#include <unistd.h>
char *getcwd(char *buf,size_t size);
//返回值:若成功,返回buf;若出错,返回NULL
必须向此函数传递两个参数,一个是缓冲区地址buf,另一个是缓冲区的长度size。
设备特殊文件
- 系统中与每个文件名关联的st_dev值是文件系统的设备号
- 只有字符特殊文件和块特殊文件才有st_rdev值。