From 1c248d7fb725d63a968181901e235e325413f706 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 12 Mar 2024 10:23:34 +0100 Subject: [PATCH] stat-util: add explicit helpers for checking if stat/statx is initialized This is a follow-up for 945a8210c770801c8492eda03b6e9af3ec5d03a3 and makes the st_dev check generic, so that we can reuse it some other places. It also incorporates the non-NULL check now, to be a comprehensive one-stop solution. The helper is static inline so that compilers can optimize the redundant checks away in case it is combined with other checks. --- src/basic/stat-util.c | 7 +++---- src/basic/stat-util.h | 16 ++++++++++++++++ src/resolve/resolved-etc-hosts.c | 2 +- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 70d5b74224..6d09f86766 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -364,8 +364,7 @@ bool stat_inode_same(const struct stat *a, const struct stat *b) { /* Returns if the specified stat structure references the same (though possibly modified) inode. Does * a thorough check, comparing inode nr, backing device and if the inode is still of the same type. */ - return a && b && - a->st_dev != 0 && /* is the structure ever initialized? */ + return stat_is_set(a) && stat_is_set(b) && ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ a->st_dev == b->st_dev && a->st_ino == b->st_ino; @@ -393,7 +392,7 @@ bool statx_inode_same(const struct statx *a, const struct statx *b) { /* Same as stat_inode_same() but for struct statx */ - return a && b && + return statx_is_set(a) && statx_is_set(b) && FLAGS_SET(a->stx_mask, STATX_TYPE|STATX_INO) && FLAGS_SET(b->stx_mask, STATX_TYPE|STATX_INO) && ((a->stx_mode ^ b->stx_mode) & S_IFMT) == 0 && a->stx_dev_major == b->stx_dev_major && @@ -402,7 +401,7 @@ bool statx_inode_same(const struct statx *a, const struct statx *b) { } bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) { - if (!a || !b) + if (!new_statx_is_set(a) || !new_statx_is_set(b)) return false; /* if we have the mount ID, that's all we need */ diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index c5dfd2db16..06bd9fdd97 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -9,6 +9,7 @@ #include #include +#include "fs-util.h" #include "macro.h" #include "missing_stat.h" #include "siphash24.h" @@ -126,3 +127,18 @@ extern const struct hash_ops inode_hash_ops; const char* inode_type_to_string(mode_t m); mode_t inode_type_from_string(const char *s); + +/* Macros that check whether the stat/statx structures have been initialized already. For "struct stat" we + * use a check for .st_dev being non-zero, since the kernel unconditionally fills that in, mapping the file + * to its originating superblock, regardless if the fs is block based or virtual (we also check for .st_mode + * being MODE_INVALID, since we use that as an invalid marker for separate mode_t fields). For "struct statx" + * we use the .stx_mask field, which must be non-zero if any of the fields have already been initialized. */ +static inline bool stat_is_set(const struct stat *st) { + return st && st->st_dev != 0 && st->st_mode != MODE_INVALID; +} +static inline bool statx_is_set(const struct statx *sx) { + return sx && sx->stx_mask != 0; +} +static inline bool new_statx_is_set(const struct new_statx *sx) { + return sx && sx->stx_mask != 0; +} diff --git a/src/resolve/resolved-etc-hosts.c b/src/resolve/resolved-etc-hosts.c index 2d334d38dd..7b05386213 100644 --- a/src/resolve/resolved-etc-hosts.c +++ b/src/resolve/resolved-etc-hosts.c @@ -342,7 +342,7 @@ static int manager_etc_hosts_read(Manager *m) { m->etc_hosts_last = ts; - if (m->etc_hosts_stat.st_mode != 0) { + if (stat_is_set(&m->etc_hosts_stat)) { if (stat("/etc/hosts", &st) < 0) { if (errno != ENOENT) return log_error_errno(errno, "Failed to stat /etc/hosts: %m");