struct statx root_stx, stx;
bool need_absolute = false; /* allocate early to avoid compiler warnings around goto */
const char *todo;
- unsigned mask = STATX_TYPE|STATX_UID|STATX_INO|STATX_MNT_ID;
+ unsigned mask = STATX_TYPE|STATX_UID|STATX_INO;
int r;
assert(!FLAGS_SET(flags, CHASE_PREFIX_ROOT));
if (fd < 0)
return -errno;
- r = xstatx(fd, /* path= */ NULL, /* flags= */ 0, mask, &stx);
+ r = xstatx_full(fd,
+ /* path= */ NULL,
+ /* statx_flags= */ 0,
+ XSTATX_MNT_ID_BEST,
+ mask,
+ /* optional_mask= */ 0,
+ /* mandatory_attributes= */ 0,
+ &stx);
if (r < 0)
return r;
if (fd < 0)
return -errno;
- r = xstatx(fd, /* path= */ NULL, /* flags= */ 0, mask, &stx);
+ r = xstatx_full(fd,
+ /* path= */ NULL,
+ /* statx_flags= */ 0,
+ XSTATX_MNT_ID_BEST,
+ mask,
+ /* optional_mask= */ 0,
+ /* mandatory_attributes= */ 0,
+ &stx);
if (r < 0)
return r;
root_stx = stx; /* remember stat data of the root, so that we can recognize it later */
if (fd_parent < 0)
return -errno;
- r = xstatx(fd_parent, /* path= */ NULL, /* flags= */ 0, mask, &stx_parent);
+ r = xstatx_full(fd_parent,
+ /* path= */ NULL,
+ /* statx_flags= */ 0,
+ XSTATX_MNT_ID_BEST,
+ mask,
+ /* optional_mask= */ 0,
+ /* mandatory_attributes= */ 0,
+ &stx_parent);
if (r < 0)
return r;
if (fd_grandparent < 0)
return -errno;
- r = xstatx(fd_grandparent, /* path= */ NULL, /* flags= */ 0, mask, &stx_grandparent);
+ r = xstatx_full(fd_grandparent,
+ /* path= */ NULL,
+ /* statx_flags= */ 0,
+ XSTATX_MNT_ID_BEST,
+ mask,
+ /* optional_mask= */ 0,
+ /* mandatory_attributes= */ 0,
+ &stx_grandparent);
if (r < 0)
return r;
}
/* ... and then check what it actually is. */
- r = xstatx(child, /* path= */ NULL, /* flags= */ 0, mask, &stx_child);
+ r = xstatx_full(child,
+ /* path= */ NULL,
+ /* statx_flags= */ 0,
+ XSTATX_MNT_ID_BEST,
+ mask,
+ /* optional_mask= */ 0,
+ /* mandatory_attributes= */ 0,
+ &stx_child);
if (r < 0)
return r;
if (fd < 0)
return fd;
- r = xstatx(fd, /* path= */ NULL, /* flags= */ 0, mask, &stx);
+ r = xstatx_full(fd,
+ /* path= */ NULL,
+ /* statx_flags= */ 0,
+ XSTATX_MNT_ID_BEST,
+ mask,
+ /* optional_mask= */ 0,
+ /* mandatory_attributes= */ 0,
+ &stx);
if (r < 0)
return r;
r = xstatx_full(dir_fd,
de->d_name,
AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT,
+ /* xstatx_flags= */ 0,
/* mandatory_mask= */ STATX_TYPE,
/* optional_mask= */ STATX_INO,
/* mandatory_attributes= */ 0,
at_flags_normalize_nofollow(flags) |
AT_NO_AUTOMOUNT | /* don't trigger automounts – mounts are a local concept, hence no need to trigger automounts to determine STATX_ATTR_MOUNT_ROOT */
AT_STATX_DONT_SYNC, /* don't go to the network for this – for similar reasons */
+ /* xstatx_flags = */ 0,
STATX_TYPE|STATX_INO,
/* optional_mask = */ 0,
STATX_ATTR_MOUNT_ROOT,
int xstatx_full(int fd,
const char *path,
- int flags,
+ int statx_flags,
+ XStatXFlags xstatx_flags,
unsigned mandatory_mask,
unsigned optional_mask,
uint64_t mandatory_attributes,
* 3. Takes separate mandatory and optional mask params, plus mandatory attributes.
* Returns -EUNATCH if statx() does not return all masks specified as mandatory,
* > 0 if all optional masks are supported, 0 otherwise.
+ * 4. Supports a new flag XSTATX_MNT_ID_BEST which acquires STATX_MNT_ID_UNIQUE if available and
+ * STATX_MNT_ID if not.
*/
assert(fd >= 0 || IN_SET(fd, AT_FDCWD, XAT_FDROOT));
assert((mandatory_mask & optional_mask) == 0);
+ assert(!FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST) || !((mandatory_mask|optional_mask) & (STATX_MNT_ID|STATX_MNT_ID_UNIQUE)));
assert(ret);
_cleanup_free_ char *p = NULL;
if (r < 0)
return r;
- if (statx(fd, strempty(path),
- flags|(isempty(path) ? AT_EMPTY_PATH : 0),
- mandatory_mask|optional_mask,
+ unsigned request_mask = mandatory_mask|optional_mask;
+ if (FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST))
+ request_mask |= STATX_MNT_ID|STATX_MNT_ID_UNIQUE;
+
+ if (statx(fd,
+ strempty(path),
+ statx_flags|(isempty(path) ? AT_EMPTY_PATH : 0),
+ request_mask,
&sx) < 0)
return negative_errno();
+ if (FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST) &&
+ !(sx.stx_mask & (STATX_MNT_ID|STATX_MNT_ID_UNIQUE)))
+ return log_debug_errno(SYNTHETIC_ERRNO(EUNATCH), "statx() did not return either STATX_MNT_ID or STATX_MNT_ID_UNIQUE.");
+
if (!FLAGS_SET(sx.stx_mask, mandatory_mask)) {
if (DEBUG_LOGGING) {
_cleanup_free_ char *mask_str = statx_mask_to_string(mandatory_mask & ~sx.stx_mask);
return null_or_empty_path_with_root(fn, NULL);
}
+typedef enum XStatXFlags {
+ XSTATX_MNT_ID_BEST = 1 << 0, /* Like STATX_MNT_ID_UNIQUE if available, STATX_MNT_ID otherwise */
+} XStatXFlags;
+
int xstatx_full(int fd,
const char *path,
- int flags,
+ int statx_flags,
+ XStatXFlags xstatx_flags,
unsigned mandatory_mask,
unsigned optional_mask,
uint64_t mandatory_attributes,
static inline int xstatx(
int fd,
const char *path,
- int flags,
+ int statx_flags,
unsigned mandatory_mask,
struct statx *ret) {
- return xstatx_full(fd, path, flags, mandatory_mask, 0, 0, ret);
+ return xstatx_full(fd, path, statx_flags, 0, mandatory_mask, 0, 0, ret);
}
int fd_is_read_only_fs(int fd);
* concept is useful for determining how "old" a file really is, and hence using the older of the two makes
* most sense. */
- r = xstatx_full(fd, path,
+ r = xstatx_full(fd,
+ path,
at_flags_normalize_nofollow(at_flags)|AT_STATX_DONT_SYNC,
- /* mandatory_mask = */ 0,
+ /* xstatx_flags= */ 0,
+ /* mandatory_mask= */ 0,
STATX_BTIME,
- /* mandatory_attributes = */ 0,
+ /* mandatory_attributes= */ 0,
&sx);
if (r > 0 && sx.stx_btime.tv_sec != 0) /* > 0: all optional masks are supported */
a = statx_timestamp_load(&sx.stx_btime);
r = xstatx_full(fd,
/* path= */ NULL,
AT_EMPTY_PATH,
- /* mandatory_mask= */ STATX_TYPE|STATX_UID|STATX_MNT_ID|STATX_INO,
+ /* xstatx_flags= */ XSTATX_MNT_ID_BEST,
+ /* mandatory_mask= */ STATX_TYPE|STATX_UID|STATX_INO,
/* optional_mask= */ 0,
/* mandatory_attributes= */ STATX_ATTR_MOUNT_ROOT,
&stx);
r = xstatx_full(new_parent_fd,
/* path= */ NULL,
AT_EMPTY_PATH,
- /* mandatory_mask= */ STATX_UID|STATX_MNT_ID|STATX_INO,
+ /* xstatx_flags= */ XSTATX_MNT_ID_BEST,
+ /* mandatory_mask= */ STATX_UID|STATX_INO,
/* optional_mask= */ 0,
/* mandatory_attributes= */ STATX_ATTR_MOUNT_ROOT,
&new_stx);
r = xstatx_full(dir_fd, f,
AT_SYMLINK_NOFOLLOW,
+ /* xstatx_flags= */ 0,
STATX_TYPE|STATX_INO,
/* optional_mask = */ 0,
STATX_ATTR_MOUNT_ROOT,
return 0;
}
- r = xstatx_full(dirfd(d), /* path = */ NULL, AT_EMPTY_PATH,
+ r = xstatx_full(dirfd(d),
+ /* path= */ NULL,
+ AT_EMPTY_PATH,
+ /* xstatx_flags= */ 0,
STATX_MODE|STATX_INO|STATX_ATIME|STATX_MTIME,
- /* optional_mask = */ 0,
+ /* optional_mask= */ 0,
STATX_ATTR_MOUNT_ROOT,
&sx);
if (r < 0)
struct statx sx;
r = xstatx_full(dirfd(d), de->d_name,
AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT,
+ /* xstatx_flags= */ 0,
STATX_TYPE|STATX_MODE|STATX_UID,
STATX_ATIME|STATX_MTIME|STATX_CTIME|STATX_BTIME,
STATX_ATTR_MOUNT_ROOT,