C4 文件和目录:APUE 笔记

时间:2023-03-10 04:19:43
C4 文件和目录:APUE 笔记

C4: 文件和目录

  本章主要讨论stat函数及其返回信息,通过修改stat结构字段,了解文件属性。

  struct stat结构定义如下:

 struct stat
{
__dev_t st_dev; /* Device. */
#ifndef __x86_64__
unsigned short int __pad1;
#endif
#if defined __x86_64__ || !defined __USE_FILE_OFFSET64
__ino_t st_ino; /* File serial number. */
#else
__ino_t __st_ino; /* 32bit file serial number. */
#endif
#ifndef __x86_64__
__mode_t st_mode; /* File mode. */
__nlink_t st_nlink; /* Link count. */
#else
__nlink_t st_nlink; /* Link count. */
__mode_t st_mode; /* File mode. */
#endif
__uid_t st_uid; /* User ID of the file's owner. */
__gid_t st_gid; /* Group ID of the file's group.*/
#ifdef __x86_64__
int __pad0;
#endif
__dev_t st_rdev; /* Device number, if device. */
#ifndef __x86_64__
unsigned short int __pad2;
#endif
#if defined __x86_64__ || !defined __USE_FILE_OFFSET64
__off_t st_size; /* Size of file, in bytes. */
#else
__off64_t st_size; /* Size of file, in bytes. */
#endif
__blksize_t st_blksize; /* Optimal block size for I/O. */
#if defined __x86_64__ || !defined __USE_FILE_OFFSET64
__blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */
#else
__blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */
#endif
#ifdef __USE_XOPEN2K8
/* Nanosecond resolution timestamps are stored in a format
equivalent to 'struct timespec'. This is the type used
whenever possible but the Unix namespace rules do not allow the
identifier 'timespec' to appear in the <sys/stat.h> header.
Therefore we have to handle the use of this header in strictly
standard-compliant sources special. */
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. */
# define st_atime st_atim.tv_sec /* Backward compatibility. */
# define st_mtime st_mtim.tv_sec
# define st_ctime st_ctim.tv_sec
#else
__time_t st_atime; /* Time of last access. */
__syscall_ulong_t st_atimensec; /* Nscecs of last access. */
__time_t st_mtime; /* Time of last modification. */
__syscall_ulong_t st_mtimensec; /* Nsecs of last modification. */
__time_t st_ctime; /* Time of last status change. */
__syscall_ulong_t st_ctimensec; /* Nsecs of last status change. */
#endif
#ifdef __x86_64__
__syscall_slong_t __glibc_reserved[];
#else
# ifndef __USE_FILE_OFFSET64
unsigned long int __glibc_reserved4;
unsigned long int __glibc_reserved5;
# else
__ino64_t st_ino; /* File serial number. */
# endif
#endif
};

1 函数stat、fstat、fstatat、lstat

  获取文件属性,头文件 sys/stat.h。成功返回0,失败返回-1。

 /* Get file attributes for FILE and put them in BUF.  */
extern int stat (const char *__restrict __file,
struct stat *__restrict __buf) __THROW __nonnull ((, )); /* Get file attributes for the file, device, pipe, or socket
that file descriptor FD is open on and put them in BUF. */
extern int fstat (int __fd, struct stat *__buf) __THROW __nonnull (()); /* Get file attributes about FILE and put them in BUF.
If FILE is a symbolic link, do not follow it. */
extern int lstat (const char *__restrict __file,
struct stat *__restrict __buf) __THROW __nonnull ((, )); /* Similar to stat, get the attributes for FILE and put them in BUF.
Relative path names are interpreted relative to FD unless FD is
AT_FDCWD. */
# ifndef __USE_FILE_OFFSET64
extern int fstatat (int __fd, const char *__restrict __file,
struct stat *__restrict __buf, int __flag)
__THROW __nonnull ((, ));

  以上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

  注意,此为进程属性。

C4 文件和目录:APUE 笔记

  通常,进程的有效用户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个宏,如下图:

C4 文件和目录:APUE 笔记

  上图前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

 /* Test for access to NAME using the real UID and real GID.  */
extern int access (const char *__name, int __type) __THROW __nonnull (()); /* Test for access to FILE relative to the directory FD is open on.
If AT_EACCESS is set in FLAG, then use effective IDs like `eaccess',
otherwise use real IDs like `access'. */
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,返回之前的文件屏蔽字

 /* Set the file creation mask of the current process to MASK,
and return the old creation mask. */ extern __mode_t umask (__mode_t __mask) __THROW;

  参数mask为表4-4按位或运算得来。

C4 文件和目录:APUE 笔记

7 函数chmod、fchmod、fchmodat

  更改现有文件的访问权限,头文件sys/stat.h。成功返回0,失败返回-1。

 /* Set file access permissions for FILE to MODE.
If FILE is a symbolic link, this affects its target instead. */
extern int chmod (const char *__file, __mode_t __mode)
__THROW __nonnull (()); /* Set file access permissions of the file FD is open on to MODE. */
#if defined __USE_POSIX199309 || defined __USE_XOPEN_EXTENDED
extern int fchmod (int __fd, __mode_t __mode) __THROW;
#endif /* Set file access permissions of FILE relative to
the directory FD is open on. */
extern int fchmodat (int __fd, const char *__file, __mode_t __mode,
int __flag)
__THROW __nonnull (()) __wur;

  参数mode是下图所示常量的按位或。

C4 文件和目录:APUE 笔记

C4 文件和目录:APUE 笔记

8 函数chown、fchown、fchownat、lchown

  用于改变文件的用户ID和组ID,头文件 unistd.h。成功返回0,失败返回-1。

 /* Change the owner and group of FILE.  */
extern int chown (const char *__file, __uid_t __owner, __gid_t __group)
__THROW __nonnull (()) __wur; /* Change the owner and group of the file that FD is open on. */
extern int fchown (int __fd, __uid_t __owner, __gid_t __group) __THROW __wur; /* Change the owner and group of FILE relative to the directory FD is open
on. */
extern int fchownat (int __fd, const char *__file, __uid_t __owner,
__gid_t __group, int __flag)
__THROW __nonnull (()) __wur; /* Change owner and group of FILE, if it is a symbolic
link the ownership of the symbolic link is changed. */
extern int lchown (const char *__file, __uid_t __owner, __gid_t __group)
__THROW __nonnull (()) __wur;

  如果两个参数中任意一个是-1,则对于ID不变。

9 文件长度

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

10 函数truncate文件截断

  可以改变文件长度,头文件unistd.h。成功返回0,失败返回-1。

 /* Truncate FILE to LENGTH bytes.  */
# ifndef __USE_FILE_OFFSET64
extern int truncate (const char *__file, __off_t __length)
__THROW __nonnull (()) __wur;

11 函数link、linkat、unlink、unlinkat、remove

  创建一个指向现有文件i节点的链接,头文件unistd.h。成功返回0,失败返回-1。

  这种链接方式,直接指向文件i节点,使得文件i节点上的链接计数__nlink_t st_nlink; /* Link count. */增加。

 /* Make a link to FROM named TO.  */
extern int link (const char *__from, const char *__to) __THROW __nonnull ((, )) __wur; /* Like link but relative paths in TO and FROM are interpreted relative
to FROMFD and TOFD respectively. */
extern int linkat (int __fromfd, const char *__from, int __tofd, const char *__to, int __flags)
__THROW __nonnull ((, )) __wu  

  unlink可以删除一个现有目录项,将所引文件的链接计数减1。成功返回0,失败返回-1。

 /* Remove the link NAME.  */
extern int unlink (const char *__name) __THROW __nonnull (()); /* Remove the link NAME relative to FD. */
extern int unlinkat (int __fd, const char *__name, int __flag)
__THROW __nonnull (());

  只有文件的链接计数达到0,文件的内容才可以被删除。

  如果unlinkat的参数flag被设置为AT_REMOVEDIR时,unlinkat函数可以类似rmdir一样删除目录。

  remove函数解除对一个文件或者目录的链接,头文件stdio.h。成功返回0,失败返回-1。

  对于文件,remove的功能与unlink类似。对于目录,remove的功能与rmdir类似。

 /* Remove file FILENAME.  */

 extern int remove (const char *__filename) __THROW;

  open\creat创建新临时文件后,立即调用unlink,可以保证临时文件在程序奔溃时也可以被删除。

12 函数rename、renameat

  rename、renameat可用于对文件或者目录重命名,头文件stdio.h。成功返回0,失败返回-1。

 /* Rename file OLD to NEW.  */
extern int rename (const char *__old, const char *__new) __THROW; /* Rename file OLD relative to OLDFD to NEW relative to NEWFD. */
extern int renameat (int __oldfd, const char *__old, int __newfd, const char *__new) __THROW;

13 符号链接

  符号链接比i节点的链接更加灵活,有以下两个原因:

  •  i节点链接通常要求链接和文件在同一文件系统
  •  通常只有超级用户才能创建i节点的链接  

  可以用symlink或symlinkat函数创建一个符号链接,头文件unistd.h。成功返回0,失败返回-1。

 /* Make a symbolic link to FROM named TO.  */
extern int symlink (const char *__from, const char *__to)
__THROW __nonnull ((, )) __wur; /* Like symlink but a relative path in TO is interpreted relative to TOFD. */
extern int symlinkat (const char *__from, int __tofd,
const char *__to) __THROW __nonnull ((, )) __wur;

  函数创建一个指向from文件路径的新目录项to。

  由于open方法访问符号链接时,将访问到符号链接引用的文件。为了访问符号链接本身,需要使用readlink和readlinkat函数,头文件unistd.h。成功返回读取的字节数,失败返回-1。

 /* Read the contents of the symbolic link PATH into no more than
LEN bytes of BUF. The contents are not null-terminated.
Returns the number of characters read, or -1 for errors. */
extern ssize_t readlink (const char *__restrict __path,
char *__restrict __buf, size_t __len)
__THROW __nonnull ((, )) __wur; /* Like readlink but a relative PATH is interpreted relative to FD. */
extern ssize_t readlinkat (int __fd, const char *__restrict __path,
char *__restrict __buf, size_t __len)
__THROW __nonnull ((, )) __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。

 /* Set file access and modification times of the file associated with FD.  */
extern int futimens (int __fd, const struct timespec __times[]) __THROW; /* Set file access and modification times relative to directory file
descriptor. */
extern int utimensat (int __fd, const char *__path, const struct timespec __times[], int __flags) __THROW __nonnull (());

  timespec结构定义为:

 struct timespec
{
__time_t tv_sec; /* Seconds. */
__syscall_slong_t tv_nsec; /* Nanoseconds. */
};

  函数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。

 /* Create a new directory named PATH, with permission bits MODE.  */
extern int mkdir (const char *__path, __mode_t __mode)
__THROW __nonnull (()); /* Like mkdir, create a new directory with permission bits MODE. But
interpret relative PATH names relative to the directory associated
with FD. */
extern int mkdirat (int __fd, const char *__path, __mode_t __mode)
__THROW __nonnull (()); /* Remove the directory PATH. */
extern int rmdir (const char *__path) __THROW __nonnull (());

  注意:

  •   创建目录至少应该指定一个执行权限位
  •   rmdir只能删除空目录

  对某一目录具有访问权限的任一用户都可以读目录,但是只有内核才可以写目录。目录操作头文件dirent.h。

 /* Open a directory stream on NAME.
Return a DIR stream on the directory, or NULL if it could not be opened.
This function is a possible cancellation point and therefore not
marked with __THROW. */
extern DIR *opendir (const char *__name) __nonnull (()); /* Same as opendir, but open the stream on the file descriptor FD.
This function is a possible cancellation point and therefore not
marked with __THROW. */
extern DIR *fdopendir (int __fd); /* Read a directory entry from DIRP. Return a pointer to a `struct
dirent' describing the entry, or NULL for EOF or error. The
storage returned may be overwritten by a later readdir call on the
same DIR stream.
If the Large File Support API is selected we have to use the
appropriate interface.
This function is a possible cancellation point and therefore not
marked with __THROW. */
extern struct dirent *readdir (DIR *__dirp) __nonnull (()); /* Rewind DIRP to the beginning of the directory. */
extern void rewinddir (DIR *__dirp) __THROW __nonnull (()); /* Close the directory stream DIRP.
Return 0 if successful, -1 if not.
This function is a possible cancellation point and therefore not
marked with __THROW. */
extern int closedir (DIR *__dirp) __nonnull (()); /* Return the current position of DIRP. */
extern long int telldir (DIR *__dirp) __THROW __nonnull (()); /* Seek to position POS on DIRP. */
extern void seekdir (DIR *__dirp, long int __pos) __THROW __nonnull (());

  每个进程都有一个初始工作目录。起始目录是登录名的属性,而当前工作目录是进程的属性。

  使用chdir可以切换当前工作目录,头文件unistd.h。成功返回0,失败返回-1。

 /* Change the process's working directory to PATH.  */
extern int chdir (const char *__path) __THROW __nonnull (()) __wur; /* Change the process's working directory to the one FD is open on. */
extern int fchdir (int __fd) __THROW __wur;

  使用getcwd可以获取当前工作目录,头文件unistd.h。成功返回0,失败返回-1。

 /* Get the pathname of the current working directory,
and put it in SIZE bytes of BUF. Returns NULL if the
directory couldn't be determined or SIZE was too small.
If successful, returns BUF. In GNU, if BUF is NULL,
an array is allocated with `malloc'; the array is SIZE
bytes long, unless SIZE == 0, in which case it is as
big as necessary. */
extern char *getcwd (char *__buf, size_t __size) __THROW __wur;