OPENAT2 - Linux手册页
Linux程序员手册 第2部分
更新日期: 2020-04-11
名称
openat2-打开并可能创建文件(扩展)
语法
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <openat2.h> int openat2(int dirfd, const char *pathname, struct open_how *how, size_t size);
注意:此系统调用没有glibc包装器。请参阅注释。
说明
openat2()系统调用是openat(2)的扩展,并提供了其功能的超集。
openat2()系统调用将打开由路径名指定的文件。如果指定的文件不存在,则可以选择创建(如果在how.flags中指定了O_CREAT)。
与openat(2)一样,如果pathname是相对路径名,则它将相对于文件描述符dirfd所引用的目录(或调用进程的当前工作目录,如果dirfd是特殊值AT_FDCWD)进行解释。如果路径名是绝对路径名,则dirfd将被忽略(除非how.resolve包含RESOLVE_IN_ROOT,在这种情况下,路径名是相对于dirfd解析的)。
而不是采用单个标志参数,而是传递一个可扩展的结构(如何)以允许将来的扩展。 size参数必须指定为sizeof(struct open_how)。
The open_how structure
how参数指定应如何打开路径名,并充当openat(2)的标志和模式参数的超集。此参数是指向以下形式结构的指针:
struct open_how { u64 flags; /* O_* flags */ u64 mode; /* Mode for O_{CREAT,TMPFILE} */ u64 resolve; /* RESOLVE_* flags */ /* ... */ };
对openat2()的任何将来扩展都将作为附加到上述结构的新字段来实现,新字段中的值为零,导致内核表现为好像不存在该扩展字段。因此,调用者必须在初始化时对该结构进行零填充。 (有关为何需要这样做的更多详细信息,请参见NOTES的"可扩展性"部分。)
open_how结构的字段如下:
- flags
- 该字段指定打开文件时使用的文件创建和文件状态标志。为openat(2)定义的所有O_ *标志都是有效的openat2()标志值。
- openat(2)忽略其flags参数中的未知位,而openhow2()如果在how.flags中指定了未知或冲突的标志,则会返回错误。
- mode
- 该字段指定新文件的模式,其语义与openat(2)的mode参数相同。
- 尽管openat(2)在其mode参数中忽略了07777范围内的位以外的其他位,但如果how.mode包含07777以外的位,则openat2()将返回错误。类似地,如果用non调用openat2(),则返回错误。 -零how.mode和how.flags不包含O_CREAT或O_TMPFILE。
- resolve
- 这是标志的位掩码,这些标志修改了解析路径名的所有组件的方式。 (有关背景信息,请参见path_resolution(7)。)
- The primary use case for these flags is to allow trusted programs to restrict
how untrusted paths (or paths inside untrusted directories) are resolved.
The full list of
resolve
flags is as follows:
- RESOLVE_BENEATH
- 如果解析的任何组成部分都不是dirfd指示的目录的后代,则不允许路径解析成功。这将导致绝对符号链接(以及路径名的绝对值)被拒绝。
- 当前,该标志还禁用魔术链接解析(请参见下文)。但是,将来可能会改变。因此,为确保不解决魔术链接,调用者应明确指定RESOLVE_NO_MAGICLINKS。
- RESOLVE_IN_ROOT
- 解析路径名时,将dirfd引用的目录视为根目录。绝对符号链接是相对于dirfd解释的。如果路径名的前缀组件等于dirfd,则紧随其后的..组件也等同于dirfd(就像/ ..在传统上等效于/)。如果路径名是绝对路径,则它也将相对于dirfd进行解释。
- 该标志的作用就好像调用进程已使用chroot(2)(临时)修改其根目录(到dirfd所引用的目录)一样。但是,与chroot(2)(为进程永久更改文件系统根目录)不同,RESOLVE_IN_ROOT允许程序在每次打开的基础上有效地限制路径解析。
- 当前,此标志还禁用魔术链接解析。但是,将来可能会改变。因此,为确保不解决魔术链接,调用者应明确指定RESOLVE_NO_MAGICLINKS。
- RESOLVE_NO_MAGICLINKS
- 在路径解析期间禁止所有魔术链接解析。
- 魔术链接是类似于符号链接的对象,在proc(5)中最常见;示例包括/ proc / [pid] / exe和/ proc / [pid] / fd / *。 (有关更多详细信息,请参见symlink(7)。)
- Unknowingly opening magic links can be risky for some applications.
Examples of such risks include the following:
- *
- 如果打开路径名的进程是当前没有控制终端的控制进程(请参阅凭据(7)),则在/ proc / [pid] / fd内部打开一个碰巧指向终端的魔术链接将导致该进程获得控制终端。
- *
- 在容器化的环境中,/ proc内部的魔术链接可以引用容器外部的对象,因此可以提供一种从容器中逃脱的方法。
- 由于存在此类风险,应用程序可能更喜欢使用RESOLVE_NO_MAGICLINKS标志来禁用魔术链接解析。
- 如果路径名的结尾部分(即基本名称)是魔术链接,则how.resolve包含RESOLVE_NO_MAGICLINKS,而how.flags同时包含O_PATH和O_NOFOLLOW,则将返回引用魔术链接的O_PATH文件描述符。
- RESOLVE_NO_SYMLINKS
- 在路径解析期间,不允许解析符号链接。此选项暗含RESOLVE_NO_MAGICLINKS。
- 如果路径名的结尾部分(即基本名称)是符号链接,则how.resolve包含RESOLVE_NO_SYMLINKS,而how.flags同时包含O_PATH和O_NOFOLLOW,则将返回引用该符号链接的O_PATH文件描述符。
- 请注意,RESOLVE_NO_SYMLINKS标志的影响会影响路径名的所有组件中符号链接的处理,它与O_NOFOLLOW文件创建标志的影响(在how.flags中)不同,后者仅在以下情况下影响符号链接的处理:路径名的最后一部分。
- 鼓励使用RESOLVE_NO_SYMLINKS标志的应用程序使其可配置使用(除非将其用于特定的安全目的),因为符号链接已被最终用户广泛使用。对于openat2()的所有使用,不加选择地设置此标志(即,出于与安全性不特别相关的目的)可能会导致先前运行的系统出现虚假错误。例如,如果修改了应用程序使用的系统路径名(例如,在新的发行版中),从而使路径名组件(现在)包含符号链接,则可能会发生这种情况。
- RESOLVE_NO_XDEV
- 在路径解析期间不允许遍历安装点(包括所有绑定安装)。因此,如果将dirfd指定为AT_FDCWD,则路径名必须与dirfd所引用的目录位于同一装载上,或者与当前工作目录位于同一装载上。
- 鼓励使用RESOLVE_NO_XDEV标志的应用程序使其可配置使用(除非它用于特定的安全目的),因为绑定安装已被最终用户广泛使用。对于openat2()的所有使用,不加选择地设置此标志(即,出于与安全性不特别相关的目的)可能会导致先前运行的系统出现虚假错误。例如,如果修改了应用程序使用的系统路径名(例如,在新的发行版中),从而使路径名组件(现在)包含绑定安装,则可能会发生这种情况。
- 如果在how.resolve中设置了除以上所列以外的任何其他位,则返回错误。
返回值
成功后,将返回一个新的文件描述符。如果出错,则返回-1,并正确设置errno。
错误说明
openat2()返回的错误集包括openat(2)返回的所有错误,以及以下其他错误:
- E2BIG
- 如何指定了该内核不支持的扩展。 (有关如何处理扩展的更多详细信息,请参见NOTES的"扩展性"部分。)
- EAGAIN
- how.resolve包含RESOLVE_IN_ROOT或RESOLVE_BENEATH,并且内核无法确保" .."组件不会逃逸(由于竞争条件或潜在的攻击)。调用方可以选择重试openat2()调用。
- EINVAL
- 如何指定了未知标志或无效值。
- EINVAL
- 模式不为零,但是how.flags不包含O_CREAT或O_TMPFILE。
- EINVAL
- 大小小于任何已知的struct open_how结构。
- ELOOP
- how.resolve包含RESOLVE_NO_SYMLINKS,并且路径组件之一是符号链接(或魔术链接)。
- ELOOP
- how.resolve包含RESOLVE_NO_MAGICLINKS,路径组件之一是魔术链接。
- EXDEV
- how.resolve包含RESOLVE_IN_ROOT或RESOLVE_BENEATH,并且在路径解析期间检测到从根的转义。
- EXDEV
- how.resolve包含RESOLVE_NO_XDEV,并且路径组件穿过安装点。
版本
openat2()首次出现在Linux 5.6中。
遵循规范
此系统调用是特定于Linux的。
RESOLVE_BENEATH的语义是在FreeBSD的O_BENEATH之后建模的。
备注
Glibc不为该系统调用提供包装器;使用syscall(2)调用它。
Extensibility
为了允许将来的可扩展性,openat2()要求用户空间应用程序指定其传递的open_how结构的大小。通过提供此信息,openat2()可以提供前向兼容和后向兼容,并且大小充当隐式版本号。 (由于将始终附加新的扩展字段,因此结构大小将始终增加。)此可扩展性设计与其他系统调用(例如perf_setattr(2),perf_event_open(2)和clone3(2))非常相似。
如果我们让usize为用户空间应用程序指定的结构大小,而ksize为内核支持的结构大小,则需要考虑以下三种情况:
- *
- 如果ksize等于usize,则不会出现版本不匹配以及如何逐字使用的情况。
- *
- 如果ksize大于usize,则内核支持用户空间应用程序不知道的某些扩展字段。因为任何添加的扩展字段中的零值表示无操作,所以内核会将用户空间应用程序未提供的所有扩展字段都视为具有零值。这提供了向后兼容性。
- *
- 如果ksize小于usize,则有一些扩展字段,用户空间应用程序知道这些扩展字段,但内核不支持。因为任何扩展字段的零值都必须表示无操作,所以内核可以完全忽略不支持的扩展字段(如果它们全为零)。如果任何不受支持的扩展字段非零,则返回-1并将errno设置为E2BIG。这提供了前向兼容性。
由于struct open_how的定义将来可能会更改(更新系统头时会添加新字段),因此用户空间应用程序应将struct open_how填充为零,以确保使用新的头重新编译程序不会导致出现错误。运行。最简单的方法是使用指定的初始化程序:
struct open_how how = { .flags = O_RDWR, .resolve = RESOLVE_IN_ROOT };
或显式使用memset(3)或类似的方法:
struct open_how how; memset(&how, 0, sizeof(how)); how.flags = O_RDWR; how.resolve = RESOLVE_IN_ROOT;
希望确定运行的内核支持哪些扩展的用户空间应用程序可以通过对二进制大小的结构进行二进制搜索来实现,该结构的每个字节都为非零(以找到不会产生E2BIG错误的最大值)。
另外参见
openat(2),path_resolution(7),symlink(7)
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。