不带缓冲的文件I/O之open

        本节介绍不带缓冲的文件I/O中如何打开或创建一个文件,这可以用以下两个函数实现:
#include <fcntl.h>

int open(const char *path, int oflag, ... /* mode_t mode */);
int openat(int fd, const char *path, int oflag, ... /* mode_t mode */);
    /* 两个函数的返回值:若成功,返回文件描述符;否则返回 -1 */

        最后一个参数“...”表明余下的参数的数量和类型是可变的。对于 open 函数而言,仅当创建新文件时才使用。
        path 参数是要打开或创建的文件的名字。oflag 参数可用来说明此函数的多个选项。用下列一个或多个常量进行“或”运算构成(都在头文件 <fcntl.h> 中定义):
    O_RDONLY / 0:只读打开。
    O_WRONLY / 1:只写打开。
    O_RDWR / 2:读、写打开。
    O_EXEC:只执行打开。
    O_SEARCH:只搜索打开(应用于目录)。O_SEARCH 常量的目的在于在打开目录时验证它的搜索权限,对目录的文件描述符的后续操作就不需要再次检查该目录的搜索权限。不过多数系统目前并不支持该选项。
        以上这5个常量中必须且只能指定一个,下列常量则是可选的:
    O_APPEND:每次写时都追加到文件的尾端。
    O_CLOEXEC:把 FD_CLOEXEC 常量设置为文件描述符标志。
    O_CREAT:若此文件不存在则创建它。使用此选项时,open/openat 函数需同时说明第 3/4 个参数 mode,用以指定新文件的访问权限位。
    O_DIRECTORY:若 path 引用的不是目录,则出错。
    O_EXCL:若同时指定了 O_CREAT,而文件已经存在,则出错。这可以用来原子性地测试和创建文件。
    O_NOCTTY:如果 path 引用的是终端设备,则不将该设备分配作为此进程的控制终端。
    O_NOFOLLOW:若 path 引用的是一个符号链接,则出错。
    O_NONBLOCK:若 path 引用的是一个 FIFO、一个块特殊文件或一个字符特殊文件,则此选项为文件的本次打开操作和后续的 I/O 操作设置非阻塞方式。
    O_SYNC:使每次 write 等待物理 I/O 操作完成,包括由该 write 操作引起的文件属性更新所需的 I/O。
    O_TRUNC:若此文件存在,而且为只写或读-写成功打开,则将其长度截断为 0。
    O_TTY_INIT:如果打开一个还未打开的终端设备,设置非标准 termios 参数值,使其符合 Single UNIX Specification。
        下面两个标志也是可选的,它们是 Single UNIX Specification 以及 POSIX.1 中同步输入和输出选项的一部分:
    O_DSYNC:使每次 write 要等到物理 I/O 操作完成,但是如果该写操作并不影响读取刚写入的数据,则不需要等待文件属性被更新。该选项和 O_SYNC 标志有微妙的区别:仅当文件属性需要更新以反映文件数据变化(例如,更新文件大小以反映文件中包含了更多的数据)时,O_DSYNC 才影响文件属性。而设置 O_SYNC 后,数据和属性总是同步更新。当文件用 O_DSYNC 打开,在重写其现有的部分内容时,文件时间属性不会同步更新。而用 O_SYNC 时,那么对该文件的每一次 write 都将在 write 返回前更新文件时间。这与是否改写现有字节或追加写文件无关。
    O_RSYNC:使每一个以文件描述符作为参数进行的 read 操作等待,直至所有对文件同一部分挂起的写操作都完成。

        fd 参数把 open 和 openat 函数区分开,共有3种可能性:
    1、path 指定的是绝对路径名,在这种情况下,fd 参数被忽略,openat 函数就相当于 open 函数。
    2、path 指定的是相对路径名,fd 参数指出了相对路径名在文件系统中的开始地址。fd 参数是通过打开相对路径名所在的目录来获取。
    3、path 指定了相对路径名,fd 参数具有特殊值 AT_FDCWD。此时路径名在当前工作目录中获取,openat 函数在操作上与 open 函数类似。

        openat 函数是 POSIX.1 中新增的函数之一,希望解决两个问题:
    1、让线程可以使用相对路径名打开目录中的文件,而不再只能打开当前工作目录。因为同一进程中的所有线程共享相同的当前工作目录,因此很难让同一进程的多个不同线程在同一时间工作在不同的目录中。
    2、可以避免 TOCTTOU(time-of-check-to-time-of-use)错误。该错误的基本思想是:如果有两个基于文件的函数调用,其中第二个依赖于第一个的结果,那么程序是脆弱的。因为两个调用并不是原子操作,在两个调用之间文件可能改变了,这样也就造成了第一个调用的结果不再有效,使得程序最终的结果是错误的。文件系统命名空间中的 TOCTTOU 错误通常处理的就是那些颠覆文件系统权限的小把戏,这些小把戏通过骗取特权程序降低特权文件的权限控制或者让特权文件打开一个安全漏洞等方式进行。

猜你喜欢

转载自aisxyz.iteye.com/blog/2370533