IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> Files and Directories 文件和目录 -> 正文阅读

[C++知识库]Files and Directories 文件和目录

4.1介绍 Introduction

?

前边一章介绍了?基础的?I/O 函数。 这些讨论都聚焦在?常规文件的I/O, 如?打开,读,写一个文件。 现在我们将看看??文件系统的其他功能, 和?文件的属性。我们将?以stat?函数开始,来过一遍?stat结构体的每一个成员,来看看文件的所有属性。 在这个过程, 我们将描述?每一个函数都修改那些属性: 改变owner, 改变?权限,等等。 这章的最后,看看操作目录的函数,并且我们开发一个函数,它往下(descends)遍历一个目录的结构。

4.2??stat, fstat, fstatat, and 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 char *restrict pathname, struct stat *restrict buf );

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

????????????????????????????????????????????????????????????????????????????????All four return: 0 if OK, -1 on error

lstat?函数?和?stat?很类似,区别是?文件名是个?符号链接(symbolic link), lstat 返回的是symbolic link的信息,而不是?被符号链接引用的文件。(4.22, 4.17 又会描述到)

fstatat 函数提供了一种方式,?返回?pathname 相对于?fd?的文件的信息。 flag参数控制?symbolic links 有如下的:

AT_SYMLINK_NOFOLLOW ?flag ,fstatat 将不会跟随?symbolic links, 而是返回?link本身的信息。

其他,默认的是?跟随symbolic link ,返回?symbolic link 指向的文件。

如果fd?是?AT_FDCWD, 并且?pathname 是相对的路径名, fstatat 会使用?pathname 参数相对于当前目录。 如果pathname 是绝对路径名,那么?fd?参数会被忽略。

在flag 的这两种cases, fstatat 的行为?要么像?stat??,要么像?lstat。

buf参数是一个指针,指向一个结构体,结构体的定义,在每个实现之间都是不同的,但是它可能看起来如下:

struct 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 owner */

gid_t? ? ? ? ? ? ? ? ? ? ??st_gid; ???????????????????????????????????????/* group ID of owner */

off_t ???????????????????????? st_size; ??????????????????????????????????/* size in bytes, for regular files */

struct timespec ????????st_atim; ???????????????????????????????? /* time of last access */

struct timespec ????????st_mtim; ???????????????????????????????? /* time of last modification */

struct timespec ????????st_ctim; ???????????????????????????????? /* time of last file status change */

blksize_t ???????????????? st_blksize; ???????? ??????????????????????????????????????/* best I/O block size */

blkcnt_t ???????????????? st_blocks; ???????????????????????????????? /* number of disk blocks allocated */

};

timespec 结构体,它至少包含如下字段。

time_t ??tv_sec;

long tv_nsec;

4.3 ?File Types 文件类型

  • 1.? 常规文件?(Regular file)。对于unix 内核来说,数据是?text?还是?binary 都是没有?区分的。对于常规文件的内容的解释?留给了?应用。
  • ?????注意: 一个值得注意的例外情况是?binary executable files。 为了执行一个程序, 内核必须理解它的格式。所有的二进制可执行文件?的格式?都能让内核知道到哪个位置?去加载程序的?text?和?data。
  • 2. 目录文件?(Directory?file)。一个file?,它包含了?其他文件的?名字,并且指向了?这些文件。 任何进程(process), 有?对目录的读权限,就可以读取目录的内容, 但仅仅只有内核能直接?写?一个目录文件。 进程(processes) 必须使用这章描述的函数来改变目录。
  • 3. 块特殊?文件(Block special file)。 ?提供了?有缓冲的?I/O 访问, 访问?固定大小的单元。例如?disk drives。
  • 4. 字符特殊?文件(Character special file)。提供?无缓冲的?I/O 访问, 访问?devices上?可变大小的单元。 在系统上的所有?设备(devices) 要么是block special files?要么是?character special files。
  • 5.? FIFO. 用于进程间通信,它有时也叫做?pipe。(15.5 中会描述)
  • 6. Socket。 用于多进程间的?网络通信。socket也可以用于?一个?host?上的?多个进程间的?非网络通信。 (16章会提到)
  • 7.??Symbolic link 。 一种文件类型,它指向?另一个文件。 (4.17)

文件的类型?被编码在?stat?结构体的?st_mode 成员中。我们可以使用?如下4.1图中的?宏来检测?file?type。 这些宏?的参数?是??stat?结构体中的?st_mode 成员。

Macro

Type of file

S_ISREG()

regular file

S_ISDIR()

directory file

S_ISCHR()

character special file

S_ISBLK()

block special file

S_ISFIFO()

pipe or FIFO

S_ISLNK()

symbolic link

S_ISSOCK()

socket

Figure 4.1 File type macros in <sys/stat.h>

POSIX.1 允许?实现?去?将这些??进程间通信(IPC) 对象,例如?message?queues, semaphores. 视作?文件(file)

在下面图4.2的图中的宏,允许我们?去检测?IPC对象的类型。 不是传stat结构体的st_mode 成员作为参数,而是传一个?指针(它指向?stat?结构体)。

Macro

Type of object

S_TYPEISMQ()

message queue

S_TYPEISSEM()

semaphore

S_TYPEISSHM()

shared memory object

Figure 4.2 IPC type macros in <sys/stat.h>

然而,在这本书中讨论的?各种unix 系统的实现,还没有把些对象?视作文件。

4.3 打印文件类型,对于命令行中给出的参数

filedir/filetype.c

1.#include?"apue.h"??
2.??
3.int??
4.main(int?argc,?char?*argv[])??
5.{??
6.????int?????????i;??
7.????struct?stat?buf;??
8.????char????????*ptr;??
9.??
10.????for?(i?=?1;?i?<?argc;?i++)?{??
11.????????printf("%s:?",?argv[i]);??
12.????????if?(lstat(argv[i],?&buf)?<?0)?{??
13.????????????err_ret("lstat?error");??
14.????????????continue;??
15.????????}??
16.????????if?(S_ISREG(buf.st_mode))??
17.????????????ptr?=?"regular";??
18.????????else?if?(S_ISDIR(buf.st_mode))??
19.????????????ptr?=?"directory";??
20.????????else?if?(S_ISCHR(buf.st_mode))??
21.????????????ptr?=?"character?special";??
22.????????else?if?(S_ISBLK(buf.st_mode))??
23.????????????ptr?=?"block?special";??
24.????????else?if?(S_ISFIFO(buf.st_mode))??
25.????????????ptr?=?"fifo";??
26.????????else?if?(S_ISLNK(buf.st_mode))??
27.????????????ptr?=?"symbolic?link";??
28.????????else?if?(S_ISSOCK(buf.st_mode))??
29.????????????ptr?=?"socket";??
30.????????else??
31.????????????ptr?=?"**?unknown?mode?**";??
32.????????printf("%s\n",?ptr);??
33.????}??
34.????exit(0);??
35.}??
36.??
37./**?
38.?*?
39.[xingqiji@work78?filedir]$?./filetype?/etc/passwd?/etc??/dev/log??/dev/tty?/var/lib/oprofile/opd_pipe??/dev/sr0??/dev/cdrom?
40./etc/passwd:?regular?
41./etc:?directory?
42./dev/log:?socket?
43./dev/tty:?character?special?
44./var/lib/oprofile/opd_pipe:?lstat?error:?No?such?file?or?directory?
45./dev/sr0:?block?special?
46./dev/cdrom:?symbolic?link?
47.[xingqiji@work78?filedir]$??
48.?
49.?*??
50.?*/?

说明:我们尤其使用?lstat?而不是stat?来?侦测symbolic link, 如果我们使用?stat函数,我们将从不会看到?symbolic links。

历史地, 早期的unix system 版本不提供?S_ISXXX 宏, 替代地?,我们不得不?,在<sys/stat.h> 我们检查file, 我将发现这个?S_ISDIR 宏?定义?像如下:

#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)

?4.4 Set-User-ID ?and ?Set-Group-ID

每个进程?有六个或更多的ID??和它关联。这些展示在图4.5 中。

real user ID

who we really are

real group ID

effective user ID

used for file access permission checks

effective group ID

supplementary group IDs

saved set-user-ID

saved by exec functions

saved set-group-ID

????????????????????????????????????????4.5 每个进程?关联的?User?IDs , group IDs

  1. real user ID, real group ID 鉴定了?我们真实是谁。 这两个字段?从?当我们登录时,用到的password file 文件中?获取。正常地,这些值?在我们整个?login?session都不会改变, 尽管超级用户有办法改变它们(8.11 会描述)
  2. ?effective user ID, effective group ID, supplementary group IDs 决定了我们的文件访问权限,就像下个部分描述的那样。
  3. ?saved?set-user-ID ?和??saved?set-group-ID ?包含了??effective user ID, effective group ID 的一个备份, ?生成的时机就是?当程序被执行后。

saved?IDs 在2001 POSIX.1 才是必要的。在更老的POSIX, 这是可选的。 一个应用可以在?编译时?测试?_POSIX_SAVED_IDS 或?在运行时?调用?sysconf , 参数是?_SC_SAVED_IDS , 来看实现?是否支持此功能。

通常地, effective user ID 等效于?real user ID, ?effective group ID 等效于?real?group ID。

每个文件都有?一个?owner?和?group owner 。 这个owner?是?stat结构体中的?st_uid 成员, group owner 是?stat结构体中的st_gid 成员。

当我们执行一个程序文件使, 进程的?the effective user ID ?通常就是??real?user ID, effective?group ID 通常是??real?group ID. ??然而我们?可以??在文件的?mode?(stat 的st_mode) 设置?一个?special flag 。含义就是说: 当这个文件被执行, 进程的effective user ID ?设置为?和?file的?owner?一样(stat?的st_uid)。 类似地,我们可以在file?的mode?word 设置另一个bit, 它将引起?进程的?effective group ID 设置为?和?file的?group?owner(即?stat的?st_gid) 一样。这两个bit?在file?mode?word 中被叫做?set-user-ID 位?和?set-group-ID 位。

例如,file的?owner?是superuser 并且?file的?set-user-ID 位被设置了,那么当程序(这个file)被执行?作为进程, ?这个进程?就有了?superuser privileges 。这种情况会发生,且?它?忽视不管??执行这个文件的?产生的进程的?real?user ID 是什么。 举个例子,unix 系统?有个程序(passwod(1)) 允许?任何人?可以改变?他或她的密码?, ?这个程序就是?一个set-user-ID 的程序。有需求去?write??/etc/passwd 或?/etc/shadow 文件, 正常地这些文件只能被?superuser 去write。( 这样的程序,第8章还会更详细的讨论)。

回到stat?函数的讨论, set-user-ID bit 和?set-group-ID 位?被包含在?file的st_mode 值中。这两个bits?可以依据?常量S_ISUID 和?S_ISGID?来测试。

4.5 File?Access Permissions ?文件访问权限

st_mode 的值?也编码了?文件的?access?permission bits。 这里说的file?指的可以是是前面提到的任何文件类型。 所有的文件类型?,目录、character?special files,等等?都有权限.

每个文件有9个权限位,被分成了3类。它们如图4.6:

st_mode mask

Meaning

S_IRUSR

user-read

S_IWUSR

user-write

S_IXUSR

user-execute

S_IRGRP

group-read

S_IWGRP

group-write

S_IXGRP

group-execute

S_IROTH

other-read

S_IWOTH

other-write

S_IXOTH

other-execute

Figure 4.6 The nine fifile access permission bits, from <sys/stat.h>

chmod(1) 命令可以修改这9种权限bits。

a.??首先规则是, 不管任何时候,我们想通过name去open任何类型的文件,我们必须有?对name中提到的每个目录?有执行权限,包括当前目录(如果它被含有)。这就是为什么对于目录的?execute permission bit 又常常被叫做?search?bit。

例如: ?为了去?打开?/usr/include/stdio.h, 我们需要?目录?/ 的?执行权限, 目录?/usr?上的执行权限, 目录?/usr/include 上的执行权限, 对文件本身也要有合适的权限(依赖于我们如何open文件是?read-only,read-write,等等.

如果当前目录是?/usr/include ,那么我们需要当前目录的执行权限。这有个当前目录的被包含(不明显的提及)例子。就是?./stdio.h 这样写法。

注意?一个目录的??读权限?和?执行权限?是很不同的。read 权限让我们?读目录,获取在目录下的所有文件名(filenames)。 执行权限?让我们?通过目录,当它是?一个pathname 的一个组成部分(我们需要去search目录来寻找特定的filename)

另一个不明显的隐含的目录引用是?PATH 环境变量(8.10 描述),指定了一个目录,但没有执行权限,在这种情况,在这个目录下就不能找到任何的?可执行文件。

b. 一个文件的?读权限?决定我们是否可以打开一个已存在的文件,来读。open函数O_RDONLY, 或?O_RDWR?flags

c. ?一个文件的写?权限?决定我们是否可以打开一个已存在的文件, 来写。open函数O_WRONLY 或?O_RDWR flags

d. 如果在open?中指定了?O_TRUNC flag ,那么我们必须对这个文件有?write?权限

e. ?如果想在一个目录下?创建一个新文件,那么我们必须对这个目录?有写?和?执行?权限。

f. ?为了去删除一个已存在的文件,我们需要?对文件所在的目录?有?写?和?执行权限。我们对文件本身不需要有?读?或?写?权限。

g. ?如果我们想用?7个?exec函数来执行?一个文件,那么文件的执行权限必须开启,并且这个文件要是一个?regular?file。

每次进程?open, create, 或delete 一个文件?,内核就会执行?file?access 检查,这个检查依据是: 文件的属主(st_uid, st_gid) , 进程的?effective IDs (包括?effective user ID 和?effective group ID), ?进程的补充group IDs (如果支持的话)。 两个owner?IDs 是文件的属性, 两个effective IDs 和?补充的group IDs ?是进程的属性, ?内核是按如下来检查的:

1. 如果进程的?effective user ID 是?0 ( 超级用户?superuser) , access 就是允许的。 这个使得超级用户?能?自由的?进入?整个文件系统。

2.? 如果?进程的?effective user ID 等于?文件的?owner ID ( 例如?进程?拥有这个文件); 并且?文件?的?合适的?用户访问权限位?被设置。 则?access?就是允许的, 其他情况下,access是被禁止的。 “合适的?访问权限bit” 含义是: 如果进程?要打开这个文件来读,那么?user-read bit 必须被打开;如果进程要打开这个文件来写, 那么user-write bit 必须被打开; 如果进程要执行这个文件,那么?user-execute bit 必须被打开。

3.? 如果进程的?effective?group ID ?或者?进程的补充group IDs中的一个??和文件的group ID 相等。 并且?合适的group 访问权限bit?被设置,那么?access 就是?允许的。否则access就是被禁止的。

4.? 如果?合适的?other access 权限bit?被设置, access?是允许的,否则access就是被禁止的。

这个四个步骤,是被尝试着按顺序来的。 注意如果?进程拥有文件(step?2) , ?access?被授权或?拒绝?取决于?user?access permissions bit, 就从来不看?group permissions bit了。类似地,如果进程?没有拥有文件,但是属于一个?合适的?group, access 是?被授权还是拒绝?取决于文件的?group access permissions bit , 就从来不看?other permissions bit?了

4. 6 ?ownership of new file and Directories ??新文件??新目录的属主

新文件的?user?ID ?被设置为?和进程的?effective user ID 一样。POSIX.1 允许一个实现?去选择?如下的选项来?决定?一个新文件的?group ID:

  1. ??新文件的?group ID 可以?和?进程的?effective group ID 一样。
  2. ??新文件的?group ID 可以?和?这个文件所在目录的?group ID ?一样。

Linux3.2.0 的默认行为,来决定新文件group ID ?取决于?文件所在的目录的?set-group ID bit 是否被设置。如果这个bit?被设置了,新文件的?group ID 就从?目录上?copy; ?否则新文件的?group ID 就设置的?和??进程的?effective group ID 一样。

4.7 access ?and?faccessat 函数

有些时候一个进程?想去?基于?real?user ?group IDs 来测试?可访问性access??faccessat 函数?来检查?是基于?real user ?group IDs

#include <unistd.h>

int access(const char *pathname, int mode);

int faccessat(int fd, const char *pathname, int mode, int flag);

????????????????????????????????????????????????????????????????????????????????????????Both return: 0 if OK, -1 on error

mode??要么是?F_OK (测试一个文件是否存在) ,那么是?如下?flags ?OR?组合??4.7:

mode

Description

R_OK

test for read ?permission

W_OK

test for write ?permission

X_OK

test for execute permission

faccessat 函数的行为和?access?是一样的,特别是当: pathname 是绝对的?或?fd 是AT_FDCWD 并且?pathname 是相对的。 否则??,faccessat 在计算pathname时?就会?相对着?这个目录(fd指向的目录)来。

flag?参数?可被?用于来?改变?facccessat 的行为。 如果?AT_EACCESS flag 被设置, access 检查就会使用?调用进程的?effective user ID , effective group ID 来检查?(而不是使用?进程的real user 和?group IDs)。

例子4.8

access?函数的使用。

#include "apue.h"  
#include <fcntl.h>  
  
int  
main(int argc, char *argv[])  
{  
    if (argc != 2)  
        err_quit("usage: a.out <pathname>");  
    if (access(argv[1], R_OK) < 0)  
        err_ret("access error for %s", argv[1]);  
    else  
        printf("read access OK\n");  
    if (open(argv[1], O_RDONLY) < 0)  
        err_ret("open error for %s", argv[1]);  
    else  
        printf("open for reading OK\n");  
    exit(0);  
}  
  
/** 
[xingqiji@work78 filedir]$ ./access /etc/passwd 
read access OK 
open for reading OK 
*/ 

4.8 umask 函数

umask 函数?设置??进程的??file?mode creation mask , 并且?返回?先前的?value。

(这个函数?是少有的?没有?error返回的函数?之一)

#include <sys/stat.h>

mode_t umask(mode_t cmask);

????????????????????????????????????????????????????????????????????????Returns: previous file mode creation mask

cmask参数的格式是: 9个?常量(S_IRUSR, S_IWUSR 等)的?OR?组合。

file?mode creation mask 通常?在进程创建?文件?或目录时?使用。 ?在file mode creation mask 中?被关闭的,在file的mode中?都被?打开。

例子:4.9

这个程序?创建两个文件: ?一个?带着?umask 0 来创建, 一个?带着?umask 禁用?所有group?和?other?permission bits 来创建。

filedir/umask.c

1.#include?"apue.h"??
2.#include?<fcntl.h>??
3.??
4.#define?RWRWRW?(S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)??
5.??
6.int??
7.main(void)??
8.{??
9.????umask(0);??
10.????if?(creat("foo",?RWRWRW)?<?0)??
11.????????err_sys("creat?error?for?foo");??
12.????umask(S_IRGRP?|?S_IWGRP?|?S_IROTH?|?S_IWOTH);??
13.????if?(creat("bar",?RWRWRW)?<?0)??
14.????????err_sys("creat?error?for?bar");??
15.????exit(0);??
16.}??
17.??
18./**?
19.[xingqiji@work78?filedir]$?ls?-l?foo?bar?
20.-rw-------?1?xingqiji?xingqiji?0?2月??23?10:18?bar?
21.-rw-rw-rw-?1?xingqiji?xingqiji?0?2月??23?10:18?foo?
22.*/?

通常在登录的时候?shell的?start-up file 会设置一次?(使用umask) mask?, 并且不再改变。 当?程序在创建文件时,想确保指定的?access permission bit 被启用, 我们必须修改?正在运行着的进程的?umask value?。

在先前的例子,我们使用?shell的umask 命令?在程序运行前和?完成后去打印?file?mode creation?mask ?。 这向我们展示了??进程改变了?file?mode creation mask ?不会影响?parent?(常常是?shell) 的mask。 所有的shell 都有?一个内建的命令?来使我们可以使用它去打印或设置当前的?file?mode creation mask。

设置umask value 用?十六进制表示。 图4.10 ?通过设置相应位就可以?禁用/关闭?对应的?permissions bit (要创建文件的) , 例如??027 ?表示?阻止?group members 写, 阻止other 读,写, 执行你的文件。

Mask?bit

Meaning

0400

user-read

0200

user-write

0100

user-execute

0040

group-read

0020

group-write

0010

group-execute

0004

other-read

0002

other-write

0001

other-execute

????????????????????????????????????????4.10 ?the umask ??file access permission bits

UNIX 规范?要求??umask 命令支持?符号表示?umask , 如下:

$ umask ???????????????????????? first print the current file mode creation mask

002

$ umask -S ???????????????????????? print the symbolic form

u=rwx,g=rwx,o=rx

$ umask 027 ???????????????????????? change the file mode creation mask

$ umask -S ???????????????????????????????? print the symbolic form

u=rwx,g=rx,o=

4.9 chmod , fchmod, and 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);

????????????????????????????????????????????????????????????????????????????????All three return: 0 if OK, -1 on error

flag 参数通常用于改变?fchmodat 行为, 当?AT_SYMLINK_NOFOLLOW flag 被设置, fchmodat 不会?跟随?symbolic links。

为了改变?文件的?permission bits ?, 进程的?effective user ID 必须?等于?文件的?owner?ID, 或者进程?有?超级用户权限。

mode?参数?被指定?为??下面常量的?OR??4.11 :

mode

描述

S_ISUID

set-user-ID on execution

S_ISGID

set-group-ID on execution

S_ISVTX

saved-text (sticky bit)

S_IRWXU

read,write, execute (owner)

S_IRUSR

read by user(owner)

S_IWUSR

write by user(owner)

S_IXUSR

execute by user (owner)

S_IRWXG

read,write, and execute by group

S_IRGRP

read by group

S_IWGRP

write by group

s_IXGRP

execute by group

S_IRWXO

read, write, execute (world)

S_IROTH

read by other (world)

S_IWOTH

write by other (world)

S_IXOTH

execute by other (world)

4.11 The mode constants for chmod functions, from <sys/stat.h>

注意: 4.11 中的9个??和?4.6 的9个file?access permission bits 是一样的。这里?添加了?2个?set-ID ?常量(S_ISUID ?和?S_ISGID) ?, saved-text 常量(S_ISVTX), ?和?3个?合并常量( S_IRWXU, S_IRWXG, S_IRWXO).

4.12 例子:

filedir/changemod.c

1.#include?"apue.h"??
2.??
3.int??
4.main(void)??
5.{??
6.????struct?stat?????statbuf;??
7.??
8.????/*?turn?on?set-group-ID?and?turn?off?group-execute?*/??
9.??
10.????if?(stat("foo",?&statbuf)?<?0)??
11.????????err_sys("stat?error?for?foo");??
12.????if?(chmod("foo",?(statbuf.st_mode?&?~S_IXGRP)?|?S_ISGID)?<?0)??
13.????????err_sys("chmod?error?for?foo");??
14.??
15.????/*?set?absolute?mode?to?"rw-r--r--"?*/??
16.??
17.????if?(chmod("bar",?S_IRUSR?|?S_IWUSR?|?S_IRGRP?|?S_IROTH)?<?0)??
18.????????err_sys("chmod?error?for?bar");??
19.??
20.????exit(0);??
21.}??
22.??
23./**?
24.[xingqiji@work78?filedir]$?ls?-l?foo?bar?
25.-rw-------?1?xingqiji?xingqiji?0?2月??23?10:18?bar?
26.-rw-rw-rw-?1?xingqiji?xingqiji?0?2月??23?10:18?foo?
27.[xingqiji@work78?filedir]$?./changemod?
28.[xingqiji@work78?filedir]$?ls?-l?foo?bar?
29.-rw-r--r--?1?xingqiji?xingqiji?0?2月??23?10:18?bar?
30.-rw-rwSrw-?1?xingqiji?xingqiji?0?2月??23?10:18?foo?
31.?
32.*/?

注意?ls命令?列出?foo的??group-execute 权限为?S??,表明了?set-group-ID bit?是被设置,并且没有group-execute bit 被设置。

最后, 注意?ls?命令?列出的?时间?和日期?在经过?./changemod 程序后?,并没有改变。

在下面的情况下: chmod 函数会自动?清除?2个权限bit

a: 对?regular files 设置??sticky bit 时,如果我们不是?superuser privileges ,在mode中的sticky bit 设置?就会被自动的?关闭(turned off) 。为了防止恶意的用户?设置?sticky bit 并且?有害地影响系统性能, 只能是?superuser 可以设置?一个?regular file 的?sticky bit。

b: ?一个新的将要被创建的文件的?group ID 可能?和?调用的进程的group ID 不一样。回想下4.6 新文件的?group ID ?可以和?父目录的?group ID 一样。 尤其是特别地??,新文件的?group ID

不等于?进程的?effective group ID 或?也不是?补充group IDs 的其中一个,并且进程没有超级用户权限,那么?set-group-ID bit 就会自动地被关闭(turned off)

4.10 Sticky Bit

S_ISVTX bit??有一个有趣的历史。 如果对?一个可执行程序?设置了?sticky bit , 那么当程序被第一执行过, 程序的text?(程序机器指令) 的一份copy 就会被保存在?swap area。 下次它被执行时,程序将被很快地加载到?memory 中, 因为?swap area 是?个?连续的(contiguous file) ?(普通的?Unix file system 的data?blocks 是随机的). ??这个sticky bit 常常被设置在哪些?通用的公共的程序,如?text?editor , c编译器?等。 自然地?对?设置?sticky files的数量有限制的。 之后的?Unix system ?把?sticky?叫做?saved-text bit ,对应的常量是?S_ISVTX。 现在的系统,大部分有一个虚拟内存系统,并且有一个更快的file?system , 所以对这个技术的?需求已经消失了。

在当代的(contemporary system?) ,sticky bit 的使用已经被扩展了, Single Unix 规范允许?对

目录设置??sticky bit 。 如果一个目录?被设置了?, 在目录下的??文件?可以被删除?或重命名?,只需要?用户对目录有?写权限?并且?有如下其一标准(criteria):

owns?这文件;

owns?这个目录;

是超级用户。

/tmp , /var/tmp 是典型的使用?sticky bit 的目录。 在这个目录下用户可以创建文件,这两个目录的权限通常是: read, write, execute (user, group, other)。 但是用户不能删除或重命名?别人拥有的的文件。

drwxrwxrwt.? 28 root root 4096 2月? 23 10:35 tmp

drwxrwxrwt. 17 root root 4096 2月? 23 10:06 tmp

4.11 chown, fchown, fchownat, lchown 函数

chown 函数允许我们改变文件的?user?ID ?和?group 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);

????????????????????????????????????????????????????????????????????????????????All four return: 0 if OK, -1 on error

lchown , fchownat( 带着/带随着??AT_SYMLINK_NOFOLLOW flag 被设置),他两只是改变?symbolic link 本身的?所有权,而不是改变?symbolic link 引用的文件的

fchown 函数?改变?fd?参数指向的?打开的文件?的?所有权。由于它操作的是一个已打开的文件,所以这个函数不能改变一个?symbolic link的?所有权(ownership)

只有超级用户进程可以改变?文件的?user?ID

非超级用户的进程?可以改变?文件的?group ID , 当?进程拥有文件( 进程的effective user ID 等于?文件的?user?ID), ?此时?owner?参数应该是?-1 或者?等于文件的user?ID, 并且?group 参数?等于?要么是?进程的?effective group ID ,要么是?进程的补充group IDs 中的一个。

在这些函数?被??非超级用户进程?调用后,成功返回后, set-user-ID bit 和?set-group-ID bit?都会被清除。

4.12 File Size

stat 结构体的成员st_size ?包含了?文件的大小(字节为单位),这个字段/成员?只对?regular files, 目录, symbolic links 是有意义的。

对regular?file , 文件的?大小?是?0 是被允许的。 对于目录, file size 通常是?一个数字的倍数,如?16 or?512。 我们在?4.22 部分讨论?reading 目录。

对于?symbolic link , file?size?就是?文件名filename的?字节数。例如,在下面的例子, 7 是?usr/lib 的长度

lrwxrwxrwx. ? 1 root root? ? 7 9月? 23 2020 lib?-> usr/lib

lrwxrwxrwx. ? 1 root root? ? 9 9月? 23 2020 lib64?-> usr/lib64

注意?symbolic links 不包含?标准?C?里的?null byte (字符串末尾的),因为长度是?st_size 来指定的。

大多数现代Unix 系统?提供了?st_blksize 和?st_blocks 字段。 第一个是?对文件的I/O 来说是首选的?block?size。 第二个是?真实的?512 字节的?block?的数量。

Holes in File ?文件中的空洞

$ ls -l core

-rw-r--r-- 1 sar 8483248 Nov 18 12:18 core

$ du -s core

272 core

size of file 是?超过了?8M, du命令?显示是?272 个?512-byte?blocks?(139264 字节)。明显地file 有许多holes.

在Linux 上?报告的单位?取决于?POSIXLY_CORRECT 环境是否被设置。如果被设置?报告单位就是?1024-byte?block units; 没有被设置时, 报告的就是?512-byte block?units。

就像我们在3.6 部分提到的, read?函数?对read的?任何位置上没有被写时,返回的都是?data?bytes of 0 :

$ wc -c core

8483248 core

wc(1) 命令?带上?-c ?选项统计??file中的?字符数量。

如果我们?copy 这个文件, 使用?cat(1), 所有的这些?holes 写出去的都写作??data?bytes of 0 :

$ cat core > core.copy

$ ls -l core*

-rw-r--r-- 1 sar 8483248 Nov 18 12:18 core

-rw-rw-r-- 1 sar 8483248 Nov 18 12:27 core.copy

$ du -s core*

272 core

16592 core.copy

文件的真实?字节数是?8,495,104 (512 *?16,592)

4.13 File Truncation

#include <unistd.h>

int truncate(const char *pathname, off_t length);

int ftruncate(int fd, off_t length);

????????????????????????????????????????????????????????????????????????????????Both return: 0 if OK, ?1 on error

这两个函数truncate?已存在文件??length?bytes。 如果之前的?文件大小?大于?参数length, 在length后边的数据就不再?可访问否则如果之前的文件大小小于??参数?length , file size 将增加老文件末尾??新文件末尾之间的数据将被读做?0 ( 在这个文件中可能产生hole)

4.14 File Systems ?文件系统

理解?一个?i-node ?和?一个?directory entry 之间的不同?是很有用的。

目前,有多种?UNIX file system .

我们可以把一个磁盘(disk) 分成一个或多个分区(partitions)。 每个分区都可以包含一个file?system 。i-nodes ?是固定长度的entries?,包含了?一个文件的大部分信息。

说明:cylinder group : 柱面组

i-node 和?data?block ?更详细的内容如下图:

上图说明:

两个?directory entries 指向/引用?相同的?i-node entry . 每个i-node 有?一个link count(连接数):“包含了directory entries 指向它的数量”。仅当?link?count 变成0时,文件才能被删除(删除指:释放文件关联的?blocks)。这就是?为什么“unlinking a file” 不总是意味着“删除文件关联的?blocks” 。 这就是为什么?移除一个?directory?entry 被叫做?unlink , 而不是?delete。在?stat?结构体?中?st_nlink member?代表了link?count。st_nlink 的数据类型?是?nlink_t。 这种链接类型?被叫做?hard?links

另一种?链接类型?叫做?symbolic link (符合链接)。一个symbolic link , 它的?文件内容/data?blocks : 存储了文件的名称。 下面例子中?directory entry 是包含了3个字符(lib) 的文件名。

lrwxrwxrwx 1 root 7 Sep 25 07:14 lib -> usr/lib

在i-node中的file?type 是S_IFLNK?目的是?让系统知道它的类型是?symbolic link.

i-node 包含了文件的所有信息:文件类型,文件的访问权限位,文件大小,指向data?blocks的指针,等等。 在stat结构体中的大多数信息都是从i-node 获取的。只有两项重要的数据是?从?directory entry 中获取,就是?filename, i-node number。另外两项数据:文件名的长度,目录记录的长度?不在此讨论。i-node number 的数据类型是ino_t

一个directory entry 不能引用?不同文件系统的一个i-node. 这就是为什么ln(1) 命令(创建一个新的directory entry 它?指向一个已存在的文件)不能跨越?file?systems.

当重命名一个文件(前提不改变文件系统),实际的文件的内容不需要改变,仅仅是添加一个新的?directory entry 并且删除?老的?directory entry ,link count 保持相同。这是mv(1)命令通常的操作。

那么?一个目录的?link?count 又是什么概念? 假如我们如下创建一个目录:

mkdir testdir

上图说明:i-node 它的?number是2549 ,它的link?count 等于?2。 任何叶子目录(里边不包含任何其它目录)总是?link?count 等于?2. 2的怎么来的一个?来自哪个包含名字的directory entry,另一个来自在那个目录的?dot?代表的directory?entry 。number?是?1267 它的link?count 肯定是?大于等于?3.??3怎么来的,一个是?他的名字对应的?directory entry (注意上图没有画出来!),一个是?dot?,一个是来自testdir 里边的?dot-dot。注意子目录每多一个都会引起本目录的link?count 增加?1

4.15 link , linkat, unlink, unlinkat, remove 函数

就像在先前部分那样,一个file?可以?是??有多个directory entries 指向它的i-node。我们可以使用?link 或?linkat 函数来?对一个已存在的file?创建link。

#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);

????????????????????????????????????????????????????????????????????????????????????????Both return: 0 if OK, -1 on error

如果newpath 已经存在, error 就会返回,

当existing file 是?symbolic link , flag参数?控制?linkat 函数是创建一个?symbolic link 的?link, 还是?创建一个?针对symbolic link 指向file?的?link 。如果AT_SYMLINK_FOLLOW flag 被设置,那么?创建的link?指向??symbolic link的目标。如果flag?被清除,那么?创建的link?指向的是?symbolic link 本身。

注意:新new?directory entry 的创建?和?link count 的增加?必须是一个?原子操作。

大多数实现?要求?两个pathnames 在同一个?file?system 上。

如果一个实现支持?对目录?创建?hard?links ,那么它一定是?仅对于?超级用户。这个约束存在的原因是?hard?links ?在file?system可能引起?loop。 基于如上?原因,许多file?system 的实现?都不允许?对目录?hard?links.

为了?移除?已存在的?directory entry ,我们可以调用unlink 函数

#include <unistd.h>

int unlink(const char *pathname);

int unlinkat(int fd, const char *pathname, int flag);

????????????????????????????????????????????????????????????????????????Both return: 0 if OK, -1 on error

这些函数?移除?directory entry 并且减少?文件( pathname指定的文件)的?link?count 。如果这里有其他link?指向这个文件, ?那么仍然可以通过其他link?来访问文件中的数据。

就像早先提到的, 去unlink 一个file?,我们必须?对??directory entry 所在的?目录?有write 和?execute 权限。也如?4.10提到的,如果?这个目录的?sticky bit 被设置了,我们必须?对?这个目录有?write?权限?并且?满足如下的标准之一:

own 这个文件

own 这个目录

有超级用户权限

仅当?link?count 达到0 时,file的contents才可能被删除,另一个情况会阻止文件被删除的是: 只要有进程open??file?, 它的contents就不能被删除。当一个file被closed , 内核首先检查?进程打开此file的数量,如果这个数量达到0了,内核再检查?link count, 如果是0 ,file的内容就会被删除。

flag?参数?给了调用者?一种改变unlinkat 函数行为的方式。 当AT_REMOVEDIR flag?被设置, unlinkat函数通常被用来?移除?一个目录,类似?rmdir。 如果flag被清除,那么unlinkat 操作就像?unlink.

例子:

#include "apue.h"  
#include <fcntl.h>  
  
int  
main(void)  
{  
    if (open("tempfile", O_RDWR) < 0)  
        err_sys("open error");  
    if (unlink("tempfile") < 0)  
        err_sys("unlink error");  
    printf("file unlinked\n");  
    sleep(15);  
    printf("done\n");  
    exit(0);  
}  
  
/** 
[xingqiji@work78 filedir]$ echo 3344 > tempfile 
[xingqiji@work78 filedir]$ ./unlink 
file unlinked 
 
done 
[xingqiji@work78 filedir]$  
 

unlink?常常被用来?确保程序创建的?临时文件?不会被留下(在程序奔溃时)。调用unlink 后文件仍然没有被删除,因为?它仍然open, 仅当进程?要么关闭file?或者?终止(terminates), 这会引起?内核去关闭所有程序打开的?files。这个时候?file?被删除。

如果pathname 是?symbolic link , ?unlink 移除的是??symbolic link 不是?link指向的file. 这里没有函数?能?移除?symbolic link 指向的file.

我们可以?用remove 函数来?unlink 一个文件或??目录, 对于?文件?remove ?完全等效于??unlink。 对于目录,remove 完全等效于?rmdir。

#include <stdio.h>

int remove(const char *pathname);

????????????????????????????????????????????????????????????????????????????????????????????????Returns: 0 if OK, -1 on error

4.16 ?rename??and ?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);

????????????????????????????????????????????????????????????????????????????????????????????????Both return: 0 if OK, ?1 on error

对于这些函数,这里有几种情况?需要来描述,这些情况?取决于?oldname 指向的是?file ,是?directory 还是?symbolic link。我们也要描述下?如果?newname 已经存在了,会发生什么。

  1. oldname 指向??file?或?symbolic link。在这种情况下, 如果newname 存在, 它不能是目录。如果newname存在?并且不是目录,那么它被删除,并且?oldname 被重命名为?newname。我们必须对?包含?oldname 的目录?以及?包含?newname 的目录?有写权限,(由于,我们正都改变两个目录)
  2. ?如果oldname 指向一个目录,那么我们正重命名一个目录。如果newname 存在,它必须指向一个目录,并且这个目录?必须是空的。如果newname 存在?并且是空的,它就被删除,并且?oldname 被重命名为?newname。 另外?newname 不能包含?一个path prefix 它的名是?oldname。 例如:我们不能把??/usr/foo 命名到?/usr/foo/testdir ?。
  3. 如果??oldname 或?newname 指向一个?symbolic link ,那么?link 本身被处理,而不是link 指向的file 被处理
  4. 我们不能?重命名?dot 或?dot-dot 。更加准确(precisely)的说法是, dot?和?dot-dot 不能出现在?oldname 或?newname 的组成的最后一个部分。

  5. 有个特殊情况, 如果oldname 和?newname 指向?相同的file,那么函数返回成功, 并且不改变任何事情。

  6. oldfd 或?newfd 参数都可以被设置为?AT_FDCWD , 这样在解析?pathname时就参考的是当前目录。

4.17 Symbolic Links

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 20:30:30  更:2022-03-21 20:35:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 3:02:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码