[PATCH v6] linux: Add openat2 (BZ 31664)
Paul Eggert
eggert@cs.ucla.edu
Tue Nov 18 19:09:43 GMT 2025
Thanks for bird-dogging this. Here are some comments about the
documentation. To some extent they're also comments on the man page that
the documentation is derived from, but I'll focus on the glibc manual.
On 2025-11-10 08:19, Adhemerval Zanella wrote:
> +The @code{open_how} structure describes how to open a file using @code{openat2}.
> +
> +@strong{Portability note:} In the future, additional fields can be added
> +to @code{struct open_how} at the end, so that the size of this data
> +type changes. Do not use it in places where this matters, such as
> +structure fields in installed header files, where such a change could
> +affect the application binary interface (ABI).
Remove "at the end" (since in theory new fields could be added in the
middle so long as existing field offsets are not changed), and change
"changes" to "increases" (since the size cannot decrease).
> +Unlike @code{openat}, @code{openat2} returns an error for unknown
> +or conflicting values.
> +
> +@item mode
> +This field specifies the mode for the new file, similar to @code{mode}
> +argument of @code{openat}. Unlike @code{openat}, @code{openat2}
> +returns an error for invalid values (i.e., values greater than @code{07777}).
Sometimes openat does return an error for unknown or conflicting flags
(e.g., O_RDONLY | O_TMPFILE). And sometimes openat2 does not (e.g.,
O_RDONLY | O_APPEND) - though admittedly it is undocumented and unclear
whether values are "conflicting". What's actually happening, is that
openat2 is stricter than openat. So let's document it that way, by
rewording to something like the following:
@code{openat2} is stricter than @code{openat} in rejecting unknown or
conflicting values. For example, @code{openat2} rejects a mode that
exceeds 07777, whereas @code{openat} silently ignores
excess high-order bits.
@item mode
This field specifies the mode for the new file, similar to @code{mode}
argument of @code{openat}. It should be in the range 0..07777.
> +@item RESOLVE_BENEATH
> +Do not permit the path resolution to succeed if any component of the
> +resolution is not a descendant of the directory indicated by @var{dirfd}.
> +This causes absolute symbolic links and absolute values of @var{pathname}
> +to be reject.
The last sentence is incomplete and ungrammatical. Reword to:
This rejects absolute pathnames, pathnames containing @file{..}@:
components that would be resolved relative to @var{dfd},
and symbolic links that resolve to pathnames that would be rejected.
The rejection occurs even if @var{dfd} is the root directory.
> +@item RESOLVE_IN_ROOT
> +Treat the directory referred to by @var{dirfd} as the root directory
> +while resolving the @var{pathname}.
> +Absolute symbolic links and absolute values of @var{pathnames} are
> +interpreted relative to @var{dirfd}.
The last sentence is incomplete as it does not mention "..". Reword to:
Treat absolute pathnames as being relative to @var{dfd},
treat a @file{..}@: component as being equivalent to @file{.}@:
if it is resolved relative to @var{dfd},
and treat symbolic link contents consistently with this.
> +@item RESOLVE_CACHED
> +Make the open operation fail unless all path components are already
> +present in the kernel's lookup cache.
I have no idea why an application would want to use this flag.
Please add a cogent explanation for why it's useful.
> +For additional information, consult the manual page @manpageurl{openat2,2}.
> +@xref{Linux Kernel}.
Please remove this, and put any such additional information (what is
it?) into the glibc manual.
> +@deftypefun int openat2 (int @var{dirfd}, const char *restrict @var{pathname}, const struct open_how *restrict @var{how}, size_t @var{size})
Now that all pointer arguments are pointer-to-const, there is no need
for 'restrict'. The situation is similar to utimensat.
> For portability
> +to future API versions that extend @code{struct open_how}, @code{*@var{how}}
> +should be initialized either by a struct initializer or by @code{memset} to zero.
There are other ways to fully initialize *how, e.g., via structure
assignment from a fully-initialized structure, or by being in static
storage that is initialized to zero. So please change to:
For portability to future API versions that may extend
@code{struct open_how}, @code{*@var{how}} should be fully initialized,
e.g., by a struct initializer, by @code{memset} to zero,
or by having static storage duration.
> +On failure, @code{openat2} returns @math{-1} and sets @code{errno}. It can
> +fail for any of the reasons @code{openat} fails, plus the following reasons:
> +
> +@table @code
> +@item E2BIG
> +An extension that the kernel does not support was specified in @code{*@var{how}},
> +or a larger struct was used with non-zero members.
I don't see the former in the kernel source code. Also, a struct that's
waaay too large (bigger than a page size) also gets E2BIG. So, change to
something like this:
@var{size} is too large for any future extension,
or @code{*@var{how}} contains non-zero members
that are future extensions not supported by this kernel.
> +@item EAGAIN
> +@code{@var{how}->resolve} contains either RESOLVE_IN_ROOT or RESOLVE_BENEATH, and
> +the kernel could not ensure that @code{".."} component did not escape. Or
> +@code{RESOLVE_CACHED} was set, and the open operation cannot be performed
> +using only cached information.
This requires more explanation. How can the kernel not know whether ".."
escapes? Please give a scenario.
> +It can also return all the errors @code{openat} returns, or other errors
> +due to new members added by the kernel.
The "other errors" can occur only if *how is not properly initialized,
right? If so, please say so; if not, please remove the "other errors"
clause.
> +extern int openat2 (int __dfd, const char *__restrict __filename,
> + const struct open_how *__restrict __how,
> + size_t __usize)
> + __nonnull ((2, 3));
No need for the two "__restrict"s.
More information about the Libc-alpha
mailing list