Notes for Apue —— chapter 4 Files and Directories(文件和目录)

4.1 Introduction

4.2 stat, fstat, fstatat, and lstat Functions

image

The lstat function is similar to stat, but when the named file is a symbolic link, lstat returns information about the symbolic link, not the file referenced by the symbolic link.

image

4.3 文件类型

1. 普通文件

2. 目录文件

Directory file. A file that contains the names of other files and pointers to
information on these files. Any process that has read permission for a directory
file can read the contents of the directory, but only the kernel can write directly
to a directory file. Processes must use the functions described in this chapter to
make changes to a directory.

目录文件包含了其他文件的名字以及指向与这些文件有关的信息的指针。对一个目录文件具有读权限的任一进程都可以都该目录的内容,但只有内核可以直接写目录文件。进程必须使用本章介绍的函数才能更改目录。

3. 块特殊文件

Block special file. A type of file providing buffered I/O access in fixed-size units
to devices such as disk drives.

可以带缓冲的访问块特殊文件上的固定大小的单元,例如磁盘驱动就是一种属于块特殊文件的设备。

4. 字符特殊文件

Character special file. A type of file providing unbuffered I/O access in
variable-sized units to devices. All devices on a system are either block special
files or character special files.

可以不带缓冲的访问字符特殊文件上的固定大小的单元。一个系统上的所有设备不是块特殊文件就是字符特殊文件。

5. FIFO

这种类型的文件用于进程间通信,有时也称为命名管道(named pipe)。

6. socket

7. 符号链接

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

与一个进程相关联的 ID 有6个或者更多。

image

  1. real user ID 以及 real group ID 表示我们究竟是谁。
  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 的拷贝。

通常情况下,effective user ID 等于 real user ID,effective group ID 等于 real group ID。

每一个文件有一个 owner 和一个 group owner。stat structure(struct stat) 中的 st_uid 成员指定文件的 owner 是谁,同样,stat structure 中的 st_gid 成员指定文件的 group owner 是谁。

当我们 execute(执行)一个程序文件(program file)时,进程中的 effective user ID 通常是 real user ID,同样,进程中的 effective group ID 通常是 real group ID。但是,我们可以通过设置 struct stat 中 的 st_mode 成员中的一个相关的 flag 来改变这一状况。当进程执行某一个文件时,可以将进程的 effective user ID 设置成该文件的 owner(即该文件的 struct stat 中的 st_uid 成员的值),同样,可以将进程的 effective group ID 设置成该文件的 group owner (即该文件的 struct stat 中的 st_gid 成员的值)。我们称以上两个在 st_mode 中的 bits(flag) 为 set-user-ID bit 以及 set-group-ID bit。  

举个例子,如果某个文件的 owner 是 superuser,并且这个文件的 set-user-ID bit 已经 set 了,那么当进程 run 这个文件时(这个文件本身是一个program,run 起来就是一个进程),这个进程将拥有 superuser 的 privileges 。无论执行这个文件的进程的 real user ID 是谁,上述情况一定会发生。再看一个例子,UNIX 系统程序允许任何用户改变自己的 password ,实际上,passwd(1) 就是一个 set-user-ID 程序,这是我们需要的,因为 /etc/passwd 以及 /etc/shadow 只有 superuser 才能 write (那么当我们的进程运行passwd(1)程序时,进程的 effictive user ID 就会被设置成 superuser,如此一来,该进程就拥有 superuser 的 privileges,就可对 /etc/passwd 或者 /etc/shadow 进行改写)。 一个进程执行 set-user-ID 程序通常会拥有其他 user 的 permissions ,因此,编写这样的代码需要特别谨慎。

再回到 stat 函数,set-user-ID bit 和 set-group-ID bit 包含于文件的 struct stat 结构的 st-mode 成员中。这两个 bit 是否被设置可以通过常量 S_ISUID 和 S_ISGID 来测试。

4.5 文件访问权限(File Access permission)

文件的 struct stat 结构中的 st_mode 成员也包含了文件的 access permission bits (访问权限位)。当提到文件,指的是文件类型(如普通文件、目录文件等等)。所有文件类型都有 permission (权限)。许多人想当然的认为只有普通文件(regular files)才有访问权限。

对于每一个文件有9种 permission ,被分成如下3类:

image

在前3行的术语 user,指的是文件 的 owner。chmod(1) 命令(用于修改这9种 permission bit)允许我们用 u 表示 user(owner),用 g 表示 group,用 o 表示 other。

上图中的3类访问权限(read, write, and execute)被不同的函数通过各种方式来使用。此处将这些使用方式汇总在下面。

1. 第一个规则是,每当我们想通过名字来打开任何类型的文件时,那么对于包含这个文件名字的目录(包括当前目录),必须有 execute permission (执行权限)。这就是为什么一个目录的 execute permission bit 通常被叫做 search bit。

For example, to open the file /usr/include/stdio.h, we need execute
permission in the directory /, execute permission in the directory /usr, and execute
permission in the directory /usr/include. We then need appropriate permission
for the file itself, depending on how we’re trying to open it: read-only, read–write,
and so on.
If the current directory is /usr/include, then we need execute permission in the
current directory to open the file stdio.h. This is an example of the current
directory being implied, not specifically mentioned. It is identical to our opening the
file ./stdio.h.

需要注意的是,对于一个目录文件来说,读权限与执行权限是两回事。读权限允许我们读目录,获得在该目录中所有文件名的列表。当一个目录是我们要访问的路径名的一个组成部分时,对该目录的执行权限使我们可以通过该目录(也就是搜索该目录,寻找一个特定的文件名)。

对于文件来说,从字面上就可以理解,但对于目录来说,执行权限代表什么?它与读、写权限有什么不同呢?

先做一些小实验,然后再总结。

### 实验数据准备 ###
$ mkdir test                         # 创建目录test
$ echo "hello" > test/f1          # 在目录test下创建文件f1
##################

1. 读权限
$ chmod 444 test                   # 修改目录为读权限(包括用户、组和其它)
$ ls test                               # 查看目录test的文件列表
f1                                        # 结果显示
$ cat test/f1                         # 再试下查看一下目录test中的文件f1
cat: test/f1: Permission denied

由此可见,目录的读权限仅允许我们读目录,获得在该目录中所有文件名的列表,但无法查看目录中文件的内容。

2. 执行权限
$ chmod 111 test                  # 修改目录为执行权限(包括用户、组和其它)
$ ls test                              # 查看目录test的文件列表
ls: test/: Permission denied
$ cat test/f1                        # 查看目录test中的文件f1
hello

由此可见,目录的执行权限不允许我们读取目录的文件列表,但可以查看目录中文件的内容。当一个目录是我们要访问文件的路径名的一个组成部分时,对该目录的执行权限使我们可通过该目录。

3. 写权限
$ chmod 222 test                        # 修改目录为写权限(包括用户、组和其它)
$ echo "bye" > test/f1                 # 修改目录test中的文件f1的内容
-bash: test/f1: Permission denied
$ chmod 333 test                        # 修改目录为执行、写权限(包括用户、组和其它)
$ echo "bye" > test/f1
$ cat test/f1
bye

由此可见,要修改目录中的文件内容,不仅仅需要目录的写权限,还需要目录的执行权限(这个很显然)。

2. The read permission for a file determines whether we can open an existing file for reading: the O_RDONLY and O_RDWR flags for the open function.

3. The write permission for a file determines whether we can open an existing file for writing: the O_WRONLY and O_RDWR flags for the open function.

4. We must have write permission for a file to specify the O_TRUNC flag in the open function.

5. We cannot create a new file in a directory unless we have write permission and execute permission in the directory.

6. To delete an existing file, we need write permission and execute permission in the directory containing the file. We do not need read permission or write permission for the file itself.

7. Execute permission for a file must be on if we want to execute the file using any of the seven exec functions (Section 8.10). The file also has to be a regular file.

进程每次open、create 或者 delete 某一个文件时,内核都会进行文件访问权限测试,这种测试将涉及到该文件的 owners(st_uid and st_gid),进程的 effective IDs(effective user ID and effective group ID) 以及进程 supplementary group IDs(如果支持的话)。两种 owner IDs 是文件的属性,而两种 effective IDs 以及 supplementary group IDs 是进程的属性。内核进行的测试具体如下:

1. 如果进程的 effective user ID 是0(the superuser),那么允许访问。

2. 如果进程的 effective user ID 等于文件的 owner(例如,进程拥有该文件),并且合适的 user access permission bit(文件的 struct stat 结构中的 st_mode 成员的一个bit) 被 set,那么允许访问该文件,否则,拒绝访问。此处,合适的 user access permission bit 是指:如果进程打开文件是为了 read,那么 user-read bit 应为1,如果进程打开文件是为了 write,那么 user-write bit 应为1,如果进程打开文件是为了执行,那么 user-execute 应为1。

3. 如果进程的 effective group ID 或者 其中一个 supplementary group ID 等于文件的 group ID,并且合适的 group access permission bit(文件的 struct stat 结构中的 st_mode 成员的一个bit)被set,那么进程允许访问该文件,否则,拒绝访问。

4. 如果合适的 other access permission bit(文件的 struct stat 结构中的 st_mode 成员的一个bit)被set,那么进程允许访问该文件,否则,拒绝访问。

以上4个步骤按顺序尝试进行。注意,如果进程拥有该文件(step 2),那么进程能否访问该文件,将仅仅取决于文件的 user access permission,不会再涉及文件的 group access permission。同样,如果进程并不拥有该文件但是属于一个适合的 group,那么进程能否访问该文件,仅仅取决于该文件的 group access permission,不会再涉及文件的 other permission。

4.6 Ownership of New Files and Directories(新文件和目录的所有权)

当我们在 Chapter 3 描述文件的创建时(使用 open 或者 create),并没有说明给该文件的 user ID 以及 group ID 赋什么样的值。The rules for the ownership of a new directory are identical to the rules in this section for the ownership of a new file.

一个新文件的 user ID 将会被设置成进程的 effective user ID。对于一个新文件的 group ID,POSIX.1 允许选择下述某一选项来确定:

1. 一个新文件的 group ID 可以是进程的 effective group ID。

2. 一个新文件的 group ID 可以是将要创建该文件的目录的 group ID。

FreeBSD 8.0 and Mac OS X 10.6.8 always copy the new file’s group ID from the directory.
Several Linux file systems allow the choice between the two options to be selected using a
mount(1) command option. The default behavior for Linux 3.2.0 and Solaris 10 is to determine
the group ID of a new file depending on whether the set-group-ID bit(目录的 struct stat 结构中的 st_mode 成员中的一个 bit) is set for the directory in which the file is created. If this bit is set, the new file’s group ID is copied from the directory; otherwise, the new file’s group ID is set to the effective group ID of the process.

使用第2个选项(继承目录的 group ID)确保了所有在某个目录下创建的文件和目录具有和该目录相同的 group ID。这样,文件和目录的所有权(ownership)会从该点沿着层次结构向下传递。This is used in the Linux directory /var/mail, for example.

As we mentioned earlier, this option for group ownership is the default for FreeBSD 8.0 and
Mac OS X 10.6.8, but an option for Linux and Solaris. Under Solaris 10, and by default under
Linux 3.2.0, we have to enable the set-group-ID bit (目录的 struct stat 结构中的 st_mode 成员中的一个 bit), and the mkdir function has to propagate a directory’s set-group-ID bit automatically for this to work. (This is described in Section 4.21.)

4.7 access and faccessat Functions

正如之前所说的那样,当我们打开一个文件,内核基于进程的 effective user ID 以及 effective group ID 来进行 access tests(访问测试)。但是有的时候,一个进程想要对自身的 real user ID 以及 real group ID 进行访问测试(为什么这样说?不是通常情况下进程的 effective user ID 就是等于自身的 real user ID,effective group ID 等于自身的 effective group ID 吗?事实上是,有的时候如果进程所执行的文件设置了 set-user-ID bit 和 set-group-ID bit (文件的 struct stat 结构中的 st_mode 成员中的 bit)的话,进程的 effective user ID 和 effective group ID 就会等于该执行文件的 user ID 和 group ID)。Even though a process might be set-user-ID to root, it might still want to verify that the real user can access a given file. The access and faccessat functions base their tests on the real user and group IDs. (Replace effective with real in the four steps at the end of Section 4.5.)

image

The mode is either the value F_OK to test if a file exists, or the bitwise OR of any of the flags shown in Figure 4.7.

01

The faccessat function behaves like access when the pathname argument is absolute or when the fd argument has the value AT_FDCWD and the pathname argument is relative. Otherwise, faccessat evaluates the pathname relative to the open directory referenced by the fd argument. (否则,faccessat function 打开目录(由 fd 参数指定)下的 pathname)

flag 参数可用于改变 faccessat function 的行为,如果 flag 设置成 AT_EACCESS,访问检查用的是进程的 effective user ID 以及 effective group ID,而不是进程的 real user ID 以及 real group ID。

EXAMPLE

Figure 4.8 shows the use of the access function.

01

Here is a sample session with this program:

image

这个例子分为两部分,第一部分:可执行文件 a.out 的 effective user ID 以及 effective group ID 就是等于进程自身的 real user ID 以及 real group ID,而 /etc/shadow 的 owner 是 superuser,通过 ls 命令我们看到只有 superuser(owner) 才有读权限,因此此进程无论是 open 该文件(通过进程的 effective user ID 以及 effective group ID 进行 access test)还是对该文件通过 real user ID 以及 real group ID 进行访问测试,都是 Permission denied。对于第二部分:首先切换至 superuser,将可执行文件 a.out 的 user ID 更改成 superuser,并同时设置该文件(a.out)的 set-user-ID bit,之后切换到原来的用户。这样,当本进程执行 a.out 时,该进程的 effective user ID 就称为了 superuser,而 superuser 对 /etc/shadow 又具有读权限,因此 open for reading OK。但是,对 /etc/passwd 通过进程的 real user ID 以及 real group ID 进行访问测试时,显然还是 Permission denied。

4.8 umask Function

Now that we’ve described the nine permission bits associated with every file, we can describe the file mode creation mask that is associated with every process.
The umask function sets the file mode creation mask for the process and returns the previous value. (This is one of the few functions that doesn’t have an error return.)

01

The cmask argument is formed as the bitwise OR of any of the nine constants from Figure 4.6: S_IRUSR, S_IWUSR, and so on.
The file mode creation mask is used whenever the process creates a new file or a new directory. (Recall from Sections 3.3 and 3.4 our description of the open and creat functions. Both accept a mode argument that specifies the new file’s access permission bits.) We describe how to create a new directory in Section 4.21. Any bits that are on in the file mode creation mask are turned off in the file’s mode(在文件模式创建掩码(屏蔽字)中被设置为1的 bit,在文件 mode 中的相应位一定被关闭).

EXAMPLE

The program in Figure 4.9 creates two files: one with a umask of 0 and one with a umask that disables(禁用) all the group and other permission bits.

01

If we run this program, we can see how the permission bits have been set.

01

分析下以上例子,首先执行 umask 命令,获得当前进程的 umask value 为 002(对应8进制为 000 000 010),也就是说当前进程对于新创建的文件或者目录将屏蔽掉 others 的 write 功能。当 a.out 执行时,先将掩码设置为0(对应8进制为 000 000 000),没有屏蔽掉任何访问权限,因此新创建的文件 foo 的权限为 -rw-rw-rw- 。随后系统将掩码设置为 000 110 110(8进制形式),屏蔽掉了新创建文件 group 以及 others 的 read 和 write 权限,因此新创建的文件 bar 的权限为 -rw-------。

Most users of UNIX systems never deal with their umask value. It is usually set once, on login, by the shell’s start-up file, and never changed. Nevertheless, when writing programs that create new files, if we want to ensure that specific access
permission bits are enabled, we must modify the umask value while the process is running. For example, if we want to ensure that anyone can read a file, we should set the umask to 0. Otherwise, the umask value that is in effect when our process is running can cause permission bits to be turned off.

In the preceding example, we use the shell’s umask command to print the file mode creation mask both before we run the program and after it completes. This shows us that changing the file mode creation mask of a process doesn’t affect the mask of its parent (often a shell). All of the shells have a built-in umask command that we can use to set or print the current file mode creation mask.

Users can set the umask value to control the default permissions on the files they create. This value is expressed in octal, with one bit representing one permission to be masked off, as shown in Figure 4.10. Permissions can be denied by setting the corresponding bits. Some common umask values are 002 to prevent others from writing your files, 022 to prevent group members and others from writing your files, and 027 to prevent group members from writing your files and others from reading, writing, or executing your files.

image

The Single UNIX Specification requires that the umask command support a symbolic mode of operation. Unlike the octal format, the symbolic format specifies which permissions are to be allowed (i.e., clear in the file creation mask) instead of
which ones are to be denied (i.e., set in the file creation mask). Compare both forms of the command, shown below.

image

4.9 chmod, fchmod, and fchmodat Functions

The chmod, fchmod, and fchmodat functions allow us to change the file access permissions for an existing file.

image

The chmod function operates on the specified file, whereas the fchmod function operates on a file that has already been opened. The fchmodat function behaves like chmod when the pathname argument is absolute or when the fd argument has the value AT_FDCWD and the pathname argument is relative. Otherwise, fchmodat evaluates the pathname relative to the open directory referenced by the fd argument. The flag argument can be used to change the behavior of fchmodat—when the AT_SYMLINK_NOFOLLOW flag is set, fchmodat doesn’t follow symbolic links.

To change the permission bits of a file, the effective user ID of the process must be equal to the owner ID of the file, or the process must have superuser permissions.

The mode is specified as the bitwise OR of the constants shown in Figure 4.11.

image

Note that nine of the entries in Figure 4.11 are the nine file access permission bits from Figure 4.6. We’ve added the two set-ID constants (S_ISUID and S_ISGID), the saved-text constant (S_ISVTX), and the three combined constants(S_IRWXU, S_IRWXG, and S_IRWXO).

The saved-text bit (S_ISVTX) is not part of POSIX.1. It is defined in the XSI option in the Single UNIX Specification. We describe its purpose in the next section.

Example

Recall the final state of the files foo and bar when we ran the program in Figure 4.9 to demonstrate the umask function:

image

The program shown in Figure 4.12 modifies the mode of these two files.

image

After running the program in Figure 4.12, we see that the final state of the two files is

image

In this example, we have set the permissions of the file bar to an absolute value, regardless of the current permission bits. For the file foo, we set the permissions relative to their current state. To do this, we first call stat to obtain the current
permissions and then modify them. We have explicitly turned on the set-group-ID bit and turned off the group-execute bit. Note that the ls command lists the group-execute permission as S to signify that the set-group-ID bit is set without the group-execute bit being set.

Finally, note that the time and date listed by the ls command did not change after we ran the program in Figure 4.12. We’ll see in Section 4.19 that the chmod function updates only the time that the i-node was last changed. By default, the ls -l lists the time when the contents of the file were last modified.(chmod 函数更新的是 i-node 最近一次被更改的时间,而按照系统默认方式,ls –l 列出的是最后修改文件内容的时间)

chmod function 在以下两种情况下自动 clear 2个 permission bit:

1. On systems, such as Solaris, that place special meaning on the sticky bit when used with regular files, if we try to set the sticky bit (S_ISVTX) on a regular file and do not have superuser privileges, the sticky bit in the mode is automatically turned off. (We describe the sticky bit in the next section.) To prevent malicious users(恶意用户) from setting the sticky bit and adversely affecting system performance, only the superuser can set the sticky bit of a regular file.(为了防止恶意用户设置 sticky bit,进而影响系统性能,只有超级用户才可以设置普通文件的 sticky bit)。

In FreeBSD 8.0 and Solaris 10, only the superuser can set the sticky bit on a regular file. Linux 3.2.0 and Mac OS X 10.6.8 place no such restriction on the setting of the sticky bit, because the bit has no meaning when applied to regular files on these systems. Although the bit also has no meaning when applied to regular files on FreeBSD, everyone except the superuser is prevented from setting it on a regular file.

2. 新创建文件的 group ID 可能不是调用进程所属的组。在4.6小节提到,一个新创建文件的 group ID 更有可能是将要创建该文件的父级目录的 group ID。Specifically, if the group ID of the new file does not equal either the effective group ID of the process or one of the process’s supplementary group IDs and if the process does not have superuser privileges, then the set-group-ID bit is automatically turned off. This prevents a user from creating a set-group-ID file owned by a group that the user doesn’t belong to.(这就防止了用户创建一个 set-group-ID 的文件,而该文件是由并非该用户所属的 group 拥有的)

(意思就是说,新创建文件的 group ID 很可能不是该user(进程)所属的group,那么我们就屏蔽掉该文件的 set-group-ID bit,为了防止以后该 user 执行该文件时,拥有该文件的所属组的特权(如果 set-group-ID bit 被设置,那么进程的 effective group ID 会等于该文件的 group ID))

FreeBSD 8.0 fails an attempt to set the set-group-ID in this case. The other systems silently turn the bit off, but don’t fail the attempt to change the file access permissions.
FreeBSD 8.0, Linux 3.2.0, Mac OS X 10.6.8, and Solaris 10 add another security feature to try to prevent misuse of some of the protection bits. If a process that does not have superuser privileges writes to a file, the set-user-ID and set-group-ID bits are automatically turned off. If malicious users find a set-group-ID or a set-user-ID file they can write to, even though they can modify the file, they lose the special privileges of the file.

4.10 Sticky Bit

S_ISVTX bit 有一段有趣的历史。在 UNIX 尚未使用分页式技术的早期版本中,S_ISVTX 位被称为黏着位(sticky bit)。如果一个可执行文件的这一位被设置了,那么当程序第一次被执行时,a copy of the program’s text was saved in the swap area when the process terminated. (The text portion of a program is the machine instructions.),这使得下次执行该程序时能较快地将其载入内存。其原因是:通产过的 UNIX 文件系统中,文件的各数据块很可能是随机存放的,相比较而言,交换区是被作为一个连续文件来处理的。对于通用的应用程序,如文本编辑器和 C 语言编译器,我们常常设置它们所在文件的黏着位。自然地,对于设置黏着位的文件需要有一定的数量限制,因为这些文件将来可能在交换区中,以免过多占用交换区空间,但无论如何这是一项有用的技术。The name sticky came about because the text portion of the file stuck around in the swap area until the system was rebooted. Later versions of the UNIX System referred to this as the saved-text bit; hence the constant S_ISVTX. With today’s newer UNIX systems, most of which have a virtual memory system and a faster file system, the need for this technique has disappeared.

On contemporary systems, the use of the sticky bit has been extended. The Single UNIX Specification allows the sticky bit to be set for a directory. If the bit is set for a directory, a file in the directory can be removed or renamed only if the user has write permission for the directory and meets one of the following criteria:、

    • Owns the file
    • Owns the directory
    • Is the superuser

The directories /tmp and /var/tmp are typical candidates for the sticky bit—they are directories in which any user can typically create files. The permissions for these two directories are often read, write, and execute for everyone (user, group, and other). But users should not be able to delete or rename files owned by others.

The saved-text bit is not part of POSIX.1. It is part of the XSI option defined in the Single UNIX Specification, and is supported by FreeBSD 8.0, Linux 3.2.0, Mac OS X 10.6.8, and Solaris 10.
Solaris 10 places special meaning on the sticky bit if it is set on a regular file. In this case, if none of the execute bits is set, the operating system will not cache the contents of the file.

4.11 chown, fchown, fchownat, and lchown Functions

The chown functions allow us to change a file’s user ID and group ID, but if either of the arguments owner or group is −1, the corresponding ID is left unchanged.

image

These four functions operate similarly unless the referenced file is a symbolic link. In that case, lchown and fchownat (with the AT_SYMLINK_NOFOLLOW flag set)change the owners of the symbolic link itself, not the file pointed to by the symbolic link.

The fchown function changes the ownership of the open file referenced by the fd argument. Since it operates on a file that is already open, it can’t be used to change the ownership of a symbolic link.

The fchownat function behaves like either chown or lchown when the pathname argument is absolute or when the fd argument has the value AT_FDCWD and the pathname argument is relative. In these cases, fchownat acts like lchown if the AT_SYMLINK_NOFOLLOW flag is set in the flag argument, or it acts like chown if the AT_SYMLINK_NOFOLLOW flag is clear. When the fd argument is set to the file descriptor of an open directory and the pathname argument is a relative pathname,
fchownat evaluates the pathname relative to the open directory.

Historically, BSD-based systems have enforced the restriction that only the superuser can change the ownership of a file. This is to prevent users from giving away their files to others, thereby defeating any disk space quota restrictions.(这样做的原因是防止用户改变其文件的 owner,从而摆脱磁盘空间限额对他们的限制) System V, however, has allowed all users to change the ownership of any files they own.

POSIX.1 allows either form of operation, depending on the value of _POSIX_CHOWN_RESTRICTED.
With Solaris 10, this functionality is a configuration option, whose default value is to enforce the restriction. FreeBSD 8.0, Linux 3.2.0, and Mac OS X 10.6.8 always enforce the chown restriction.

Recall from Section 2.6 that the _POSIX_CHOWN_RESTRICTED constant can optionally be defined in the header <unistd.h>, and can always be queried using either the pathconf function or the fpathconf function. Also recall that this option can depend on the referenced file; it can be enabled or disabled on a per file system basis. We’ll use the phrase “if _POSIX_CHOWN_RESTRICTED is in effect,” to mean “if it applies to the particular file that we’re talking about,”regardless of whether this actual constant is defined in the header. (回忆2.6节,_POSIX_CHOWN_RESTRICTED constant 可选地定义在头文件 <unistd.h> 中,而且总是可以用 pathconf 或者 fpathconf 函数进行查询。此选项还与所引用的文件相关,但是可以在每个文件系统的基础上,使该选项起作用或者不起作用。在下文中,如提及“若 _POSIX_CHOWN_RESTRICTED constant 生效“,则表示”这适用于我们正在谈及的文件“,而不管该实际常量是否在头文件中定义。)

若 _POSIX_CHOWN_RESTRICTED 对指定的文件有效,则

1. 只有超级用户进程能更改该文件的 user ID;

2. 如果一个非超级用户进程可以改变文件的 group ID,需要进程拥有此文件(进程的 effective user ID 等于 文件的 user ID),参数 owner 等于 –1或者等于文件的 user ID ,并且参数 group 等于 either the effective group ID of the process or one of the process’s supplementary group IDs.

这意味着 _POSIX_CHOWN_RESTRICTED 生效时,我们不能改变文件的 user ID。我们仅仅能改变我们所拥有文件的 group ID 至我们所属于的组。

如果以上这些函数是被一个非超级用户进程调用,那么在成功返回时,该文件的 set-user-ID bit 和 set-group-ID bit 都会被 clear。

4.12 File Size

The st_size member of the stat structure contains the size of the file in bytes. This field is meaningful only for regular files, directories, and symbolic links.

FreeBSD 8.0, Mac OS X 10.6.8, and Solaris 10 also define the file size for a pipe as the number of bytes that are available for reading from the pipe. We’ll discuss pipes in Section 15.2.

对于一个普通文件,允许文件大小为0,在开始读这种文件时,将得到文件结束(end-of-file)指示。对于目录,文件大小通常是一个数(例如16或者512)的整数倍,我们将在4.22节中说明读目录操作。

For a symbolic link, the file size is the number of bytes in the filename. For example, in the following case, the file size of 7 is the length of the pathname usr/lib:

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

(Note that symbolic links do not contain the normal C null byte at the end of the name, as the length is always specified by st_size.)

大多数现代 UNIX 系统提供字段 st_blksize 和 st_blocks。 其中,st_blksize 是对文件 I/O 比较合适的块长度, st_blocks 是所分配的实际以512字节作为块长度的块数。回忆3.9节,其中提到可当我们将 st_blksize 用于读操作时,读一个文件所需的时间量最小。为了提高效率,标准 I/O 库(将在第5章中说明)也试图一次读、写 st_blksize 个字节。

应当了解的是,不用的 UNIX 版本其 st_blksize 所用的单位可能不是 512字节。使用此值并不是可移植的。

Holes in a File(文件中的空洞)

In Section 3.6, we mentioned that a regular file can contain “holes”. We showed an example of this in Figure 3.2. Holes are created by seeking past the current end of file and writing some data. As an example, consider the following:
image

core 文件实际大小超过8M,但是通过 du 命令看到该文件所占用的磁盘空间仅仅为 272 512-byte blocks(139,264 bytes)。显然,该文件有许多空洞。

The du command on many BSD-derived systems reports the number of 1,024-byte blocks.
Solaris reports the number of 512-byte blocks. On Linux, the units reported depend on the whether the POSIXLY_CORRECT environment is set. When it is set, the du command reports 1,024-byte block units; when it is not set, the command reports 512-byte block units.

正如我们在3.6节中提及的,对于没有写过的字节位置,read 函数读到的字节是0。如果执行下面的命令,可以看出正常的 I/O 操作读整个文件的长度。(带 –c 选项的 wc(1) 命令计算文件中的字符数(字节))

image

如果使用使用程序(如cat(1))复制这个文件,那么所有这些空洞都会被填满,其中所有实际数据字节都会被填写为0。

image

从中可见,新文件中所用的实际字节数是 8495104 (512 * 16592)。The difference between this size and the size reported by ls is caused by the number of blocks used by the file system to hold pointers to the actual data blocks.(此长度与 ls 命令报告的长度不同,其原因是,文件系统使用了若干块 blocks 来存放指向实际数据块的各个指针)。、

4.13 File Truncation(文件截断)

有时我们需要在文件尾端处截去一些数据以缩短文件。将一个文件的长度截断为0是一个特例,在打开文件时使用 O_TRUNC 标志可以做到这一点。为了截断文件可以调用函数 truncate 和 ftruncate。

image

这两个函数将一个现有文件长度截断为 length。如果该文件以前的长度大于length,则超过 length 以外的数据就不能再访问。如果以前的长度小于 length,文件长度将增加,在以前的文件尾端和新的文件尾端之间的数据将读作0。(也就是可能在文件中创建了一个空洞)

4.14 File Systems

为了说明文件链接的概念,先要介绍 UNIX 文件系统的基本结构。同时,了解 i 节点和指向 i 节点的目录项(a directory entry)之间的区别也是有益的。

目前,正在使用的 UNIX 文件系统有多种实现。例如, Solaris 支持多种不同类型的磁盘文件系统:传统的基于 BSD 的 UNIX 文件系统(称为 UFS),读、写 DOS 格式软盘的文件系统(称为 PCFS),以及读 CD 的文件系统(称为 HSFS)。在图 2-20 中,我们已经看到了不同类型文件系统的一个区别。UFS 是以 Berkeley 快速文件系统为基础的。本节讨论该文件系统。

4.15 link, linkat, unlink, unlinkat, and remove Functions

As we saw in the previous section, a file can have multiple directory entries pointing to its i-node. We can use either the link function or the linkat function to create a link to an existing file.

image

p117

Most implementations require that both pathnames be on the same file system, although POSIX.1 allows an implementation to support linking across file systems. If an implementation supports the creation of hard links to directories, it is restricted to only the superuser. This constraint exists because such hard links can cause loops in the file system, which most utilities that process the file system aren’t capable of handling. (We show an example of a loop introduced by a symbolic link in Section 4.17.) Many file system implementations disallow hard links to directories for this reason. (为了避免循环)

image

image

image

posted @ 2015-05-04 21:57  Acjx  阅读(715)  评论(0编辑  收藏  举报