From 09811ccaa1aa3146e5c72c3090105b02007cedba Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 4 Feb 2026 00:07:02 +0900 Subject: [PATCH 1/5] mountpoint-util: make name_to_handle_at_loop() gracefully handle NULL path --- src/basic/mountpoint-util.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index 9c04d381f3..3bb82ef239 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -63,6 +63,11 @@ int name_to_handle_at_loop( * This improves on raw name_to_handle_at() also in one other regard: ret_handle and ret_mnt_id can be passed * as NULL if there's no interest in either. */ + if (isempty(path)) { + flags |= AT_EMPTY_PATH; + path = ""; + } + for (;;) { _cleanup_free_ struct file_handle *h = NULL; int mnt_id = -1; @@ -73,7 +78,7 @@ int name_to_handle_at_loop( h->handle_bytes = n; - if (name_to_handle_at(fd, strempty(path), h, &mnt_id, flags) >= 0) { + if (name_to_handle_at(fd, path, h, &mnt_id, flags) >= 0) { if (ret_handle) *ret_handle = TAKE_PTR(h); From e7ce1e22b936c6e77c3d90f681fc211e8888bb21 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 4 Feb 2026 00:27:10 +0900 Subject: [PATCH 2/5] mountpoint-util: introduce name_to_handle_at_u64() and friends They will be used later. --- src/basic/mountpoint-util.c | 22 ++++++++++++++++++++++ src/basic/mountpoint-util.h | 7 +++++++ 2 files changed, 29 insertions(+) diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index 3bb82ef239..547183395e 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -17,6 +17,7 @@ #include "stat-util.h" #include "string-util.h" #include "strv.h" +#include "unaligned.h" /* This is the original MAX_HANDLE_SZ definition from the kernel, when the API was introduced. We use that in place of * any more currently defined value to future-proof things: if the size is increased in the API headers, and our code @@ -138,6 +139,27 @@ int name_to_handle_at_try_fid( return name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, flags & ~AT_HANDLE_FID); } +int name_to_handle_at_u64(int fd, const char *path, uint64_t *ret) { + _cleanup_free_ struct file_handle *h = NULL; + int r; + + assert(fd >= 0 || fd == AT_FDCWD); + + /* This provides the first 64bit of the file handle. */ + + r = name_to_handle_at_loop(fd, path, &h, /* ret_mnt_id= */ NULL, /* flags= */ 0); + if (r < 0) + return r; + if (h->handle_bytes < sizeof(uint64_t)) + return -EBADMSG; + + if (ret) + /* Note, "struct file_handle" is 32bit aligned usually, but we need to read a 64bit value from it */ + *ret = unaligned_read_ne64(h->f_handle); + + return 0; +} + bool file_handle_equal(const struct file_handle *a, const struct file_handle *b) { if (a == b) return true; diff --git a/src/basic/mountpoint-util.h b/src/basic/mountpoint-util.h index 659f038556..8799224f3d 100644 --- a/src/basic/mountpoint-util.h +++ b/src/basic/mountpoint-util.h @@ -36,6 +36,13 @@ bool is_name_to_handle_at_fatal_error(int err); int name_to_handle_at_loop(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags); int name_to_handle_at_try_fid(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags); +int name_to_handle_at_u64(int fd, const char *path, uint64_t *ret); +static inline int path_to_handle_u64(const char *path, uint64_t *ret) { + return name_to_handle_at_u64(AT_FDCWD, path, ret); +} +static inline int fd_to_handle_u64(int fd, uint64_t *ret) { + return name_to_handle_at_u64(fd, NULL, ret); +} bool file_handle_equal(const struct file_handle *a, const struct file_handle *b); From 2eab8fa1e4420b7c021b477b726038720511e5df Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 4 Feb 2026 00:29:06 +0900 Subject: [PATCH 3/5] pidfd-util: use fd_to_handle_u64() --- src/basic/pidfd-util.c | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/src/basic/pidfd-util.c b/src/basic/pidfd-util.c index 8c4c832452..7bfe83d319 100644 --- a/src/basic/pidfd-util.c +++ b/src/basic/pidfd-util.c @@ -1,6 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include #include #include #include @@ -236,25 +235,10 @@ int pidfd_get_inode_id_impl(int fd, uint64_t *ret) { assert(fd >= 0); + /* Since kernel 6.14 (b3caba8f7a34a2bbaf45ffc6ff3a49b70afeb192), we can use name_to_handle_at(). */ if (file_handle_supported) { - union { - struct file_handle file_handle; - uint8_t space[MAX_HANDLE_SZ]; - } fh = { - .file_handle.handle_bytes = sizeof(uint64_t), - .file_handle.handle_type = FILEID_KERNFS, - }; - int mnt_id; - - r = RET_NERRNO(name_to_handle_at(fd, "", &fh.file_handle, &mnt_id, AT_EMPTY_PATH)); - if (r >= 0) { - if (ret) - /* Note, "struct file_handle" is 32bit aligned usually, but we need to read a 64bit value from it */ - *ret = unaligned_read_ne64(fh.file_handle.f_handle); - return 0; - } - assert(r != -EOVERFLOW); - if (is_name_to_handle_at_fatal_error(r)) + r = fd_to_handle_u64(fd, ret); + if (r >= 0 || is_name_to_handle_at_fatal_error(r)) return r; file_handle_supported = false; From f37931bd78bbbc41590789368b9f8fc94cf8a68a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 29 Jan 2026 02:02:56 +0900 Subject: [PATCH 4/5] tree-wide: replace cg_{fd,path}_get_cgroupid() with {fd,path}_to_handle_u64() --- src/basic/cgroup-util.c | 21 --------------------- src/basic/cgroup-util.h | 7 ------- src/core/cgroup.c | 10 ++++++---- src/nsresourced/nsresourcework.c | 2 +- src/shared/cgroup-show.c | 3 ++- src/test/test-cgroup-util.c | 5 +++-- 6 files changed, 12 insertions(+), 36 deletions(-) diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 6fb4d55958..5b21392154 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -47,9 +47,6 @@ typedef union { .file_handle.handle_type = FILEID_KERNFS, \ } -/* The .f_handle field is not aligned to 64bit on some archs, hence read it via an unaligned accessor */ -#define CG_FILE_HANDLE_CGROUPID(fh) unaligned_read_ne64(fh.file_handle.f_handle) - int cg_is_available(void) { struct statfs fs; @@ -112,24 +109,6 @@ int cg_path_from_cgroupid(int cgroupfs_fd, uint64_t id, char **ret) { return 0; } -int cg_get_cgroupid_at(int dfd, const char *path, uint64_t *ret) { - cg_file_handle fh = CG_FILE_HANDLE_INIT; - int mnt_id; - - assert(dfd >= 0 || (dfd == AT_FDCWD && path_is_absolute(path))); - assert(ret); - - /* This is cgroupfs so we know the size of the handle, thus no need to loop around like - * name_to_handle_at_loop() does in mountpoint-util.c */ - if (name_to_handle_at(dfd, strempty(path), &fh.file_handle, &mnt_id, isempty(path) ? AT_EMPTY_PATH : 0) < 0) { - assert(errno != EOVERFLOW); - return -errno; - } - - *ret = CG_FILE_HANDLE_CGROUPID(fh); - return 0; -} - int cg_enumerate_processes(const char *path, FILE **ret) { _cleanup_free_ char *fs = NULL; FILE *f; diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index 641a179526..68a771e5ae 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -125,13 +125,6 @@ int cg_path_open(const char *path); int cg_cgroupid_open(int cgroupfs_fd, uint64_t id); int cg_path_from_cgroupid(int cgroupfs_fd, uint64_t id, char **ret); -int cg_get_cgroupid_at(int dfd, const char *path, uint64_t *ret); -static inline int cg_path_get_cgroupid(const char *path, uint64_t *ret) { - return cg_get_cgroupid_at(AT_FDCWD, path, ret); -} -static inline int cg_fd_get_cgroupid(int fd, uint64_t *ret) { - return cg_get_cgroupid_at(fd, NULL, ret); -} typedef enum CGroupFlags { CGROUP_SIGCONT = 1 << 0, diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 35c0898e03..ad889db3b9 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -34,6 +34,7 @@ #include "ip-protocol-list.h" #include "limits-util.h" #include "manager.h" +#include "mountpoint-util.h" #include "netlink-internal.h" #include "nulstr-util.h" #include "parse-util.h" @@ -2069,13 +2070,14 @@ static int unit_update_cgroup( uint64_t cgroup_id = 0; r = cg_get_path(crt->cgroup_path, /* suffix= */ NULL, &cgroup_full_path); - if (r == 0) { - r = cg_path_get_cgroupid(cgroup_full_path, &cgroup_id); + if (r < 0) + log_unit_warning_errno(u, r, "Failed to get full cgroup path on cgroup %s, ignoring: %m", empty_to_root(crt->cgroup_path)); + else { + r = path_to_handle_u64(cgroup_full_path, &cgroup_id); if (r < 0) log_unit_full_errno(u, ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING, r, "Failed to get cgroup ID of cgroup %s, ignoring: %m", cgroup_full_path); - } else - log_unit_warning_errno(u, r, "Failed to get full cgroup path on cgroup %s, ignoring: %m", empty_to_root(crt->cgroup_path)); + } crt->cgroup_id = cgroup_id; diff --git a/src/nsresourced/nsresourcework.c b/src/nsresourced/nsresourcework.c index 7004d03c48..60d3a01ce0 100644 --- a/src/nsresourced/nsresourcework.c +++ b/src/nsresourced/nsresourcework.c @@ -1354,7 +1354,7 @@ static int validate_cgroup(sd_varlink *link, int fd, uint64_t *ret_cgroup_id) { if (r == 0) return sd_varlink_error_invalid_parameter_name(link, "controlGroupFileDescriptor"); - r = cg_fd_get_cgroupid(fd, ret_cgroup_id); + r = fd_to_handle_u64(fd, ret_cgroup_id); if (r < 0) return log_debug_errno(r, "Failed to read cgroup ID from cgroupfs: %m"); diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index 109fe016f5..cfc80d666f 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -18,6 +18,7 @@ #include "glyph-util.h" #include "hostname-util.h" #include "log.h" +#include "mountpoint-util.h" #include "nulstr-util.h" #include "output-mode.h" #include "path-util.h" @@ -150,7 +151,7 @@ static int show_cgroup_name( delegate = r > 0; if (FLAGS_SET(flags, OUTPUT_CGROUP_ID)) { - r = cg_fd_get_cgroupid(fd, &cgroupid); + r = fd_to_handle_u64(fd, &cgroupid); if (r < 0) log_debug_errno(r, "Failed to determine cgroup ID of %s, ignoring: %m", path); } diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c index 0fd81284e8..b8393f3368 100644 --- a/src/test/test-cgroup-util.c +++ b/src/test/test-cgroup-util.c @@ -7,6 +7,7 @@ #include "errno-util.h" #include "fd-util.h" #include "format-util.h" +#include "mountpoint-util.h" #include "path-util.h" #include "pidref.h" #include "process-util.h" @@ -493,7 +494,7 @@ TEST(cgroupid) { ASSERT_OK(fd_get_path(fd, &p)); ASSERT_TRUE(path_equal(p, "/sys/fs/cgroup")); - ASSERT_OK(cg_fd_get_cgroupid(fd, &id)); + ASSERT_OK(fd_to_handle_u64(fd, &id)); fd2 = cg_cgroupid_open(fd, id); @@ -509,7 +510,7 @@ TEST(cgroupid) { ASSERT_OK(fd_get_path(fd2, &p2)); ASSERT_TRUE(path_equal(p2, "/sys/fs/cgroup")); - ASSERT_OK(cg_fd_get_cgroupid(fd2, &id2)); + ASSERT_OK(fd_to_handle_u64(fd2, &id2)); ASSERT_EQ(id, id2); From ede1caeada6a055a0ae0ca9893376084347f312b Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 29 Jan 2026 02:16:32 +0900 Subject: [PATCH 5/5] cgroup-util: declare file_handle union in cg_cgroupid_open() This also effectively reverts eb2e91b9815bcbc59997c1092d7b52fc18930bff, as now the object is only used as input, and kernel would need to handle input with only ID no matter whether the struct later gets extended. --- src/basic/cgroup-util.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 5b21392154..d613e65c4b 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -35,18 +35,6 @@ #include "user-util.h" #include "xattr-util.h" -/* The structure to pass to name_to_handle_at() on cgroupfs2 */ -typedef union { - struct file_handle file_handle; - uint8_t space[MAX_HANDLE_SZ]; -} cg_file_handle; - -#define CG_FILE_HANDLE_INIT \ - (cg_file_handle) { \ - .file_handle.handle_bytes = sizeof(uint64_t), \ - .file_handle.handle_type = FILEID_KERNFS, \ - } - int cg_is_available(void) { struct statfs fs; @@ -82,7 +70,14 @@ int cg_cgroupid_open(int cgroupfs_fd, uint64_t id) { cgroupfs_fd = fsfd; } - cg_file_handle fh = CG_FILE_HANDLE_INIT; + union { + struct file_handle file_handle; + uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)]; + } fh = { + .file_handle.handle_bytes = sizeof(uint64_t), + .file_handle.handle_type = FILEID_KERNFS, + }; + unaligned_write_ne64(fh.file_handle.f_handle, id); return RET_NERRNO(open_by_handle_at(cgroupfs_fd, &fh.file_handle, O_DIRECTORY|O_CLOEXEC));