Skip to content

lib/fileutils: add ul_safe_statx() and ul_safe_stat() - #4658

Open
karelzak wants to merge 1 commit into
util-linux:masterfrom
karelzak:PR/fileutils-safe-stat
Open

karelzak wants to merge 1 commit into
util-linux:masterfrom
karelzak:PR/fileutils-safe-stat

Conversation

@karelzak

Copy link
Copy Markdown
Collaborator

Prerequisite split out of #4647 ("lsfd: do not block on hung file systems"),
so that @masatake can rebase on top of it and drop his first two commits.

Moves the non-blocking statx() based stat() alternative from libmount to
lib/fileutils.c, so that lsfd and others can use it too, and fixes the
original code to honour stx_mask.

Move the non-blocking statx() based stat() alternative out of libmount,
so that other tools can use it too. lsfd needs it to avoid blocking on
hung filesystems.

ul_safe_statx() is the primitive; it adds AT_STATX_DONT_SYNC and
AT_NO_AUTOMOUNT (and AT_EMPTY_PATH for an empty path) to the caller's
flags, so there is one place that knows what "do not block" means.

ul_safe_stat() converts the result to struct stat. It's an improved
version of the original libmount code:

 * the caller says what it needs by a statx(2) attribute mask, see
   UL_STATX_{ESSENTIAL,BASIC}. These are plain statx masks, so they can
   be composed, for example UL_STATX_ESSENTIAL | STATX_ATIME.
   UL_STATX_ESSENTIAL is always added, so 0 is a valid request for the
   minimum.

 * ul_statx_to_stat() honours stx_mask. statx(2) is not obliged to
   return everything we ask for, and with AT_STATX_DONT_SYNC that's
   exactly the case we care about. The original code copied the fields
   unconditionally, which could report garbage as a valid attribute.

 * the optional @Retmask returns what the kernel really provided, so
   the caller can tell which fields in the struct stat are usable.

 * it fails with EOPNOTSUPP when the kernel does not provide the
   essential attributes, so the caller can fall back to stat().

Both functions return 0 or a negative errno, and they set errno too.
ul_safe_statx() always zeroizes the struct statx, so the caller never
sees stale data on an error path. The HAVE_UL_SAFE_STAT{,X} macros hide
the HAVE_STATX, HAVE_STRUCT_STATX and AT_* feature tests from the
callers.

libmount's safe_stat() and get_mnt_id() now use the new functions. Note
that this changes mnt_id_from_path(), mnt_id_from_fd() and the internal
mnt_safe_stat()/mnt_safe_lstat() to return -errno rather than -1. All
in-tree callers only test for non-zero, and the public functions are
documented as "<0 on error" only.

get_mnt_id() honours stx_mask now too. The kernel silently ignores
unsupported mask bits rather than failing, so the old "errno == EINVAL"
probe for STATX_MNT_ID_UNIQUE never triggered; statx(2) returns EINVAL
only for STATX__RESERVED. On kernels older than 6.8 that made
mnt_id_from_path() report the legacy non-unique mount ID as if it were
the unique one. Both IDs are returned only when the kernel advertises
them in stx_mask, otherwise -ENOSYS -- which is what mnt_fs_fetch_ids()
already expects for its fallback to the old mount ID.

Add test_fileutils --safe-stat and tests/ts/misc/safe-stat. The stat
attributes differ between systems, so the test program does not print
them; it compares the ul_safe_stat() result with the classic stat() and
prints OK/FAILED per attribute. The reported attributes depend on the
requested mask only, not on how generous the kernel is.

Addresses: util-linux#4647
Signed-off-by: Karel Zak <kzak@redhat.com>
@karelzak
karelzak force-pushed the PR/fileutils-safe-stat branch from ca6ae96 to 052be81 Compare October 2, 2026 09:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant