Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package lxcfs for openSUSE:Factory checked in at 2026-03-10 17:57:18 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/lxcfs (Old) and /work/SRC/openSUSE:Factory/.lxcfs.new.8177 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "lxcfs" Tue Mar 10 17:57:18 2026 rev:29 rq:1337887 version:6.0.6 Changes: -------- --- /work/SRC/openSUSE:Factory/lxcfs/lxcfs.changes 2025-08-17 14:50:30.836936443 +0200 +++ /work/SRC/openSUSE:Factory/.lxcfs.new.8177/lxcfs.changes 2026-03-10 18:01:56.608901093 +0100 @@ -1,0 +2,30 @@ +Thu Feb 26 08:46:36 UTC 2026 - Johannes Kastl <[email protected]> + +- update to 6.0.6: + https://discuss.linuxcontainers.org/t/lxcfs-6-0-6-lts-has-been-released/26263 + The highlights for this release are: + * Fix issue with CPU count in /proc/stat + * Fix issue causing the cgroup2 mount flags to be changed on + startup + Detailed changelog + * proc_fuse: fix proc_stat_read reporting host cpu count under + cgroup v2 + * cgroups: extract cgfsng_can_use_memory_feature() util function + * cgroups: replace dup() call with openat_safe() + * src/utils: fix in_same_namespace helper + * bindings: add private_data field to struct file_info + * meson: add “mocks” option for CI/testing purposes + * github: enable mocks for CI builds + * lxcfs: use macro to generate liblxcfs call helpers + * lxcfs: wire up ->write callback for /proc + * lxcfs: wire up ->poll callback for /proc + * proc_fuse: move release/releasedir at the end + * cgroups/cgfsng: fix whitespace errors in __cg_mount_direct + * cgroups/cgfsng: do not change host-wide cgroup2 superblock + options + * github: pass LIBFUSE env variable to upgrade tests + * github: enable mocks for CI upgrade tests + * meson: don’t forget to set PSI trigger mocks for liblxcfstest + * cgroups/cgfsng: check memory allocation in add_hierarchy + +------------------------------------------------------------------- Old: ---- lxcfs-6.0.5.tar.gz lxcfs-6.0.5.tar.gz.asc New: ---- lxcfs-6.0.6.tar.gz lxcfs-6.0.6.tar.gz.asc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ lxcfs.spec ++++++ --- /var/tmp/diff_new_pack.u1iKhf/_old 2026-03-10 18:01:57.236926914 +0100 +++ /var/tmp/diff_new_pack.u1iKhf/_new 2026-03-10 18:01:57.240927078 +0100 @@ -1,7 +1,7 @@ # # spec file for package lxcfs # -# Copyright (c) 2025 SUSE LLC and contributors +# Copyright (c) 2026 SUSE LLC and contributors # Copyright (c) 2025 Andreas Stieger <[email protected]> # # All modifications and additions to the file contributed by third parties @@ -18,7 +18,7 @@ Name: lxcfs -Version: 6.0.5 +Version: 6.0.6 Release: 0 Summary: FUSE filesystem for LXC License: Apache-2.0 ++++++ lxcfs-6.0.5.tar.gz -> lxcfs-6.0.6.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lxcfs-6.0.5/.github/actions/build/action.yml new/lxcfs-6.0.6/.github/actions/build/action.yml --- old/lxcfs-6.0.5/.github/actions/build/action.yml 2025-08-15 06:47:54.000000000 +0200 +++ new/lxcfs-6.0.6/.github/actions/build/action.yml 2026-02-23 20:21:25.000000000 +0100 @@ -48,6 +48,7 @@ meson setup build \ -Ddocs=false \ -Dtests=true \ + -Dmocks=true \ -Dinit-script=systemd \ -Dprefix=/usr \ -Db_sanitize=address,undefined diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lxcfs-6.0.5/.github/workflows/tests.yml new/lxcfs-6.0.6/.github/workflows/tests.yml --- old/lxcfs-6.0.5/.github/workflows/tests.yml 2025-08-15 06:47:54.000000000 +0200 +++ new/lxcfs-6.0.6/.github/workflows/tests.yml 2026-02-23 20:21:25.000000000 +0100 @@ -75,12 +75,14 @@ run: | git clone -b stable-6.0 https://github.com/lxc/lxcfs.git ../upstream-lxcfs cd ../upstream-lxcfs + meson setup -Ddocs=false -Dtests=true -Dmocks=true -Dinit-script=systemd -Dprefix=/usr -Db_sanitize=address,undefined build/ || \ meson setup -Ddocs=false -Dtests=true -Dinit-script=systemd -Dprefix=/usr -Db_sanitize=address,undefined build/ meson compile -C build - name: Test env: CC: ${{ matrix.compiler }} + LIBFUSE: ${{ matrix.fuse }} WORKSPACE_PATH: ${{ github.workspace }} run: | UPSTREAM_LXCFS_TREE=$(realpath ${WORKSPACE_PATH}/../upstream-lxcfs) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lxcfs-6.0.5/meson.build new/lxcfs-6.0.6/meson.build --- old/lxcfs-6.0.5/meson.build 2025-08-15 06:47:54.000000000 +0200 +++ new/lxcfs-6.0.6/meson.build 2026-02-23 20:21:25.000000000 +0100 @@ -4,7 +4,7 @@ project( 'lxcfs', 'c', - version: '6.0.5', + version: '6.0.6', license: 'LGPLv2+', default_options: [ 'b_colorout=always', @@ -69,6 +69,7 @@ # Custom configuration. init_script = get_option('init-script') want_tests = get_option('tests') +want_mocks = get_option('mocks') want_docs = get_option('docs') # Build flags. @@ -232,12 +233,20 @@ libfuse, ]) +liblxcfs_cargs = [] +liblxcfstest_cargs = ['-DRELOADTEST', '-DDEBUG'] +if want_mocks == true + liblxcfs_cargs = ['-DPSITRIGGERTEST', '-DDEBUG'] + liblxcfstest_cargs += ['-DPSITRIGGERTEST'] +endif + liblxcfs = shared_module( 'lxcfs', liblxcfs_sources, dependencies: liblxcfs_common_dependencies, install: true, - install_dir: lxcfsdir) + install_dir: lxcfsdir, + c_args: liblxcfs_cargs) # Tests. test_programs = [] @@ -248,7 +257,7 @@ dependencies: liblxcfs_common_dependencies, install: false, install_dir: lxcfsdir, - c_args: '-DRELOADTEST -DDEBUG') + c_args: liblxcfstest_cargs) endif # RPM spec. @@ -306,6 +315,7 @@ 'lxcfs source root directory: @0@'.format(project_source_root), 'init system(s): @0@'.format(init_script), 'tests: @0@'.format(want_tests), + 'mocks: @0@'.format(want_mocks), 'documentation: @0@'.format(want_docs), ] message('\n '.join(status)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lxcfs-6.0.5/meson_options.txt new/lxcfs-6.0.6/meson_options.txt --- old/lxcfs-6.0.5/meson_options.txt 2025-08-15 06:47:54.000000000 +0200 +++ new/lxcfs-6.0.6/meson_options.txt 2026-02-23 20:21:25.000000000 +0100 @@ -3,6 +3,9 @@ option('tests', type : 'boolean', value: 'false', description : 'enable tests') +option('mocks', type : 'boolean', value: 'false', + description : 'enable CI-only features (mocks some things in LXCFS for CI/testing purposes)') + option('runtime-path', type : 'string', value : '/run', description : 'the runtime directory') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lxcfs-6.0.5/src/bindings.h new/lxcfs-6.0.6/src/bindings.h --- old/lxcfs-6.0.5/src/bindings.h 2025-08-15 06:47:54.000000000 +0200 +++ new/lxcfs-6.0.6/src/bindings.h 2026-02-23 20:21:25.000000000 +0100 @@ -94,13 +94,20 @@ extern int rwlock_wrlock_interruptible(pthread_rwlock_t *l); struct file_info { - char *controller; - char *cgroup; - char *file; + union { + struct { + char *controller; + char *cgroup; + char *file; + }; + struct { + void *private_data; + }; + }; int type; char *buf; /* unused */ int buflen; - int size; /*actual data size */ + int size; /* actual data size */ int cached; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lxcfs-6.0.5/src/cgroups/cgfsng.c new/lxcfs-6.0.6/src/cgroups/cgfsng.c --- old/lxcfs-6.0.5/src/cgroups/cgfsng.c 2025-08-15 06:47:54.000000000 +0200 +++ new/lxcfs-6.0.6/src/cgroups/cgfsng.c 2026-02-23 20:21:25.000000000 +0100 @@ -285,7 +285,8 @@ struct hierarchy *new; int newentry; - new = zalloc(sizeof(*new)); + new = must_realloc(NULL, sizeof(*new)); + memset(new, 0, sizeof(*new)); new->controllers = clist; new->mountpoint = mountpoint; new->base_path = base_path; @@ -399,24 +400,39 @@ */ static int __cg_mount_direct(struct hierarchy *h, const char *controllerpath) { - __do_free char *controllers = NULL; - char *fstype = "cgroup2"; - unsigned long flags = 0; - int ret; - - flags |= MS_NOSUID; - flags |= MS_NOEXEC; - flags |= MS_NODEV; - flags |= MS_RELATIME; - - if (h->version != CGROUP2_SUPER_MAGIC) { - controllers = lxc_string_join(",", (const char **)h->controllers, false); - if (!controllers) - return -ENOMEM; - fstype = "cgroup"; + __do_free char *controllers = NULL; + char *fstype = "cgroup2"; + unsigned long flags = 0; + int ret; + + flags |= MS_NOSUID; + flags |= MS_NOEXEC; + flags |= MS_NODEV; + flags |= MS_RELATIME; + + if (h->version != CGROUP2_SUPER_MAGIC) { + controllers = lxc_string_join(",", (const char **)h->controllers, false); + if (!controllers) + return -ENOMEM; + fstype = "cgroup"; + ret = mount("cgroup", controllerpath, fstype, flags, controllers); + } else { + __do_free const char *sb_opts = NULL; + + /* + * Before mounting cgroup2 fs we have to try out best to find + * an existing mount and extract mount existing mount options from it. + * It is important because otherwise we can change cgroup2 superblock + * options. See kernel logic in apply_cgroup_root_flags for more details: + * https://github.com/torvalds/linux/blob/18f7fcd5e69a04df57b563360b88be72471d6b62/kernel/cgroup/cgroup.c#L2047 + * + * If we haven't found an existing mount, just mount a new one with + * an empty list of options. + */ + sb_opts = cgroup2_extract_sb_opts(DEFAULT_CGROUP_MOUNTPOINT); + ret = mount(fstype, controllerpath, fstype, flags, sb_opts); } - ret = mount("cgroup", controllerpath, fstype, flags, controllers); if (ret < 0) return -1; @@ -631,22 +647,16 @@ return openat(h->fd, path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW); } -static bool cgfsng_can_use_swap(struct cgroup_ops *ops, const char *cgroup) +static bool cgfsng_can_use_memory_feature(struct cgroup_ops *ops, + struct hierarchy *h, const char *cgroup, const char *file) { __do_free char *cgroup_rel = NULL, *junk_value = NULL; - const char *file; - struct hierarchy *h; bool ret; - h = ops->get_hierarchy(ops, "memory"); - if (!h) - return false; - cgroup_rel = must_make_path_relative(cgroup, NULL); - file = is_unified_hierarchy(h) ? "memory.swap.current" : "memory.memsw.usage_in_bytes"; /* For v2, we need to look at the lower levels of the hierarchy because - * no 'memory.swap.current' file exists at the root. We must search + * no 'memory.<feature>.current' file exists at the root. We must search * upwards in the hierarchy in case memory accounting is disabled via * cgroup.subtree_control for the given cgroup itself. */ @@ -663,7 +673,7 @@ __do_closedir DIR *dir = NULL; struct dirent *dent; - fd = dup(h->fd); + fd = openat_safe(h->fd, "."); if (fd < 0) return false; @@ -684,7 +694,7 @@ if (dent->d_type == DT_DIR) { __do_free char *path; - path = must_make_path_relative(dent->d_name, "memory.swap.current", NULL); + path = must_make_path_relative(dent->d_name, file, NULL); if (!faccessat(h->fd, path, F_OK, 0)) { /* We found it. Exit. */ @@ -704,6 +714,17 @@ return ret; } +static bool cgfsng_can_use_swap(struct cgroup_ops *ops, const char *cgroup) +{ + struct hierarchy *h; + + h = ops->get_hierarchy(ops, "memory"); + if (!h) + return false; + + return cgfsng_can_use_memory_feature(ops, h, cgroup, is_unified_hierarchy(h) ? "memory.swap.current" : "memory.memsw.usage_in_bytes"); +} + static int cgfsng_get_memory_stats(struct cgroup_ops *ops, const char *cgroup, char **value) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lxcfs-6.0.5/src/cgroups/cgroup_utils.c new/lxcfs-6.0.6/src/cgroups/cgroup_utils.c --- old/lxcfs-6.0.5/src/cgroups/cgroup_utils.c 2025-08-15 06:47:54.000000000 +0200 +++ new/lxcfs-6.0.6/src/cgroups/cgroup_utils.c 2026-02-23 20:21:25.000000000 +0100 @@ -13,6 +13,7 @@ #include <sys/types.h> #include <sys/vfs.h> #include <unistd.h> +#include <mntent.h> #include "../macro.h" #include "../memory_utils.h" @@ -810,3 +811,88 @@ return log_error_errno(-ELOOP, ELOOP, "To many nested cgroups or invalid mount tree. Terminating walk"); } + +/** + * get_mount_opts() - Returns the mount options for a given mnt and with fs_type + * + * @mnt: Mount point. + * @fs_type: File system type. + * + * Returns: mount options if success, NULL if mount is not found or error occurred. + */ +static char *get_mount_opts(const char *mnt, const char *fs_type) { + FILE *fp; + char *mnt_opts = NULL; + struct mntent *ent; + + if (mnt == NULL || fs_type == NULL) + return NULL; + + fp = setmntent("/proc/self/mounts", "r"); + if (!fp) + return NULL; + + while ((ent = getmntent(fp)) != NULL) { + if (strncmp(ent->mnt_dir, mnt, strlen(mnt)) == 0 && + strncmp(ent->mnt_type, fs_type, strlen(fs_type)) == 0) { + mnt_opts = strdup(ent->mnt_opts); /* allocate, caller frees */ + goto out; + } + } + +out: + endmntent(fp); + return mnt_opts; +} + +/** + * cgroup2_extract_sb_opts() - Returns cgroup2 fs superblock options for a given mount point + * + * @mnt: cgroup2 fs mount point. + * + * Returns: sb options if success, NULL if mount is not found or error occurred. + */ +const char *cgroup2_extract_sb_opts(const char *mnt) +{ + __do_free char *mnt_opts = NULL; + char *tok; + char *buf; + size_t buf_len = 0; + static const char *wanted_opts[] = { + "nsdelegate", + "favordynmods", + "memory_localevents", + "memory_recursiveprot", + "memory_hugetlb_accounting", + "pids_localevents", + NULL + }; + + mnt_opts = get_mount_opts(mnt, "cgroup2"); + if (mnt_opts == NULL) { + /* report as info, because it is not critical */ + lxcfs_info("Failed to find an existing cgroup2 mount and get mount options from it"); + return NULL; + } + + for (int i = 0; wanted_opts[i] != NULL; i++) + buf_len += strlen(wanted_opts[i]) + 1; /* for comma or null terminator */ + + buf = calloc(buf_len, 1); + if (!buf) + return NULL; + buf[0] = '\0'; + + lxc_iterate_parts(tok, mnt_opts, ",") { + for (int i = 0; wanted_opts[i] != NULL; i++) { + if (strcmp(tok, wanted_opts[i]) != 0) + continue; + + if (buf[0] != '\0') + (void)strlcat(buf, ",", buf_len); + (void)strlcat(buf, tok, buf_len); + } + } + + return buf; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lxcfs-6.0.5/src/cgroups/cgroup_utils.h new/lxcfs-6.0.6/src/cgroups/cgroup_utils.h --- old/lxcfs-6.0.5/src/cgroups/cgroup_utils.h 2025-08-15 06:47:54.000000000 +0200 +++ new/lxcfs-6.0.6/src/cgroups/cgroup_utils.h 2026-02-23 20:21:25.000000000 +0100 @@ -98,4 +98,6 @@ return !s || strcmp(s, "") == 0; } +const char *cgroup2_extract_sb_opts(const char *mnt); + #endif /* __LXC_CGROUP_UTILS_H */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lxcfs-6.0.5/src/lxcfs.c new/lxcfs-6.0.6/src/lxcfs.c --- old/lxcfs-6.0.5/src/lxcfs.c 2025-08-15 06:47:54.000000000 +0200 +++ new/lxcfs-6.0.6/src/lxcfs.c 2026-02-23 20:21:25.000000000 +0100 @@ -243,427 +243,101 @@ } /* Functions to run the library methods */ -static int do_cg_getattr(const char *path, struct stat *sb) -{ - char *error; - int (*__cg_getattr)(const char *path, struct stat *sb); - - dlerror(); - __cg_getattr = (int (*)(const char *, struct stat *))dlsym(dlopen_handle, "cg_getattr"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find cg_getattr()", error); - - return __cg_getattr(path, sb); -} - -static int do_proc_getattr(const char *path, struct stat *sb) -{ - char *error; - int (*__proc_getattr)(const char *path, struct stat *sb); - - dlerror(); - __proc_getattr = (int (*)(const char *, struct stat *)) dlsym(dlopen_handle, "proc_getattr"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find proc_getattr()", error); - - return __proc_getattr(path, sb); -} - -static int do_sys_getattr(const char *path, struct stat *sb) -{ - char *error; - int (*__sys_getattr)(const char *path, struct stat *sb); - - dlerror(); - __sys_getattr = (int (*)(const char *, struct stat *)) dlsym(dlopen_handle, "sys_getattr"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find sys_getattr()", error); - - return __sys_getattr(path, sb); -} - -static int do_cg_read(const char *path, char *buf, size_t size, off_t offset, - struct fuse_file_info *fi) -{ - char *error; - int (*__cg_read)(const char *path, char *buf, size_t size, off_t offset, - struct fuse_file_info *fi); - - dlerror(); - __cg_read = (int (*)(const char *, char *, size_t, off_t, struct fuse_file_info *))dlsym(dlopen_handle, "cg_read"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find cg_read()", error); - - return __cg_read(path, buf, size, offset, fi); -} - -static int do_proc_read(const char *path, char *buf, size_t size, off_t offset, - struct fuse_file_info *fi) -{ - char *error; - int (*__proc_read)(const char *path, char *buf, size_t size, - off_t offset, struct fuse_file_info *fi); - - dlerror(); - __proc_read = (int (*)(const char *, char *, size_t, off_t, struct fuse_file_info *))dlsym(dlopen_handle, "proc_read"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find proc_read()", error); - - return __proc_read(path, buf, size, offset, fi); -} - -static int do_sys_read(const char *path, char *buf, size_t size, off_t offset, - struct fuse_file_info *fi) -{ - char *error; - int (*__sys_read)(const char *path, char *buf, size_t size, - off_t offset, struct fuse_file_info *fi); - - dlerror(); - __sys_read = (int (*)(const char *, char *, size_t, off_t, struct fuse_file_info *))dlsym(dlopen_handle, "sys_read"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find sys_read()", error); - - return __sys_read(path, buf, size, offset, fi); -} - -static int do_cg_write(const char *path, const char *buf, size_t size, - off_t offset, struct fuse_file_info *fi) -{ - char *error; - int (*__cg_write)(const char *path, const char *buf, size_t size, - off_t offset, struct fuse_file_info *fi); - - dlerror(); - __cg_write = (int (*)(const char *, const char *, size_t, off_t, struct fuse_file_info *))dlsym(dlopen_handle, "cg_write"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find cg_write()", error); - - return __cg_write(path, buf, size, offset, fi); -} - -static int do_sys_write(const char *path, const char *buf, size_t size, - off_t offset, struct fuse_file_info *fi) -{ - char *error; - int (*__sys_write)(const char *path, const char *buf, size_t size, - off_t offset, struct fuse_file_info *fi); - - dlerror(); - __sys_write = (int (*)(const char *, const char *, size_t, off_t, struct fuse_file_info *))dlsym(dlopen_handle, "sys_write"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find sys_write()", error); - - return __sys_write(path, buf, size, offset, fi); -} - -static int do_cg_mkdir(const char *path, mode_t mode) -{ - char *error; - int (*__cg_mkdir)(const char *path, mode_t mode); - - dlerror(); - __cg_mkdir = (int (*)(const char *, mode_t))dlsym(dlopen_handle, "cg_mkdir"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find cg_mkdir()", error); - - return __cg_mkdir(path, mode); -} - -static int do_cg_chown(const char *path, uid_t uid, gid_t gid) -{ - char *error; - int (*__cg_chown)(const char *path, uid_t uid, gid_t gid); - - dlerror(); - __cg_chown = (int (*)(const char *, uid_t, gid_t))dlsym(dlopen_handle, "cg_chown"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find cg_chown()", error); - - return __cg_chown(path, uid, gid); -} - -static int do_cg_rmdir(const char *path) -{ - char *error; - int (*__cg_rmdir)(const char *path); - - dlerror(); - __cg_rmdir = (int (*)(const char *path))dlsym(dlopen_handle, "cg_rmdir"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find cg_rmdir()", error); - - return __cg_rmdir(path); -} - -static int do_cg_chmod(const char *path, mode_t mode) -{ - char *error; - int (*__cg_chmod)(const char *path, mode_t mode); - - dlerror(); - __cg_chmod = (int (*)(const char *, mode_t))dlsym(dlopen_handle, "cg_chmod"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find cg_chmod()", error); - - return __cg_chmod(path, mode); -} - -static int do_cg_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) -{ - char *error; - int (*__cg_readdir)(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi); - - dlerror(); - __cg_readdir = (int (*)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *))dlsym(dlopen_handle, "cg_readdir"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find cg_readdir()", error); - - return __cg_readdir(path, buf, filler, offset, fi); -} - -static int do_proc_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) -{ - char *error; - int (*__proc_readdir)(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi); - - dlerror(); - __proc_readdir = (int (*)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *))dlsym(dlopen_handle, "proc_readdir"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find proc_readdir()", error); - - return __proc_readdir(path, buf, filler, offset, fi); -} - -static int do_sys_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) -{ - char *error; - int (*__sys_readdir)(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi); - - dlerror(); - __sys_readdir = (int (*)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *))dlsym(dlopen_handle, "sys_readdir"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find sys_readdir()", error); - - return __sys_readdir(path, buf, filler, offset, fi); -} - -static int do_sys_readlink(const char *path, char *buf, size_t size) -{ - char *error; - int (*__sys_readlink)(const char *path, char *buf, size_t size); - - dlerror(); - __sys_readlink = (int (*)(const char *, char *, size_t))dlsym(dlopen_handle, "sys_readlink"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find sys_readlink()", error); - - return __sys_readlink(path, buf, size); -} - -static int do_cg_open(const char *path, struct fuse_file_info *fi) -{ - char *error; - int (*__cg_open)(const char *path, struct fuse_file_info *fi); - - dlerror(); - __cg_open = (int (*)(const char *, struct fuse_file_info *))dlsym(dlopen_handle, "cg_open"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find cg_open()", error); - return __cg_open(path, fi); -} - -static int do_cg_access(const char *path, int mode) -{ - char *error; - int (*__cg_access)(const char *path, int mode); - - dlerror(); - __cg_access = (int (*)(const char *, int mode))dlsym(dlopen_handle, "cg_access"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find cg_access()", error); - - return __cg_access(path, mode); -} - -static int do_proc_open(const char *path, struct fuse_file_info *fi) -{ - char *error; - int (*__proc_open)(const char *path, struct fuse_file_info *fi); - - dlerror(); - __proc_open = (int (*)(const char *path, struct fuse_file_info *fi))dlsym(dlopen_handle, "proc_open"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find proc_open()", error); - - return __proc_open(path, fi); -} - -static int do_proc_access(const char *path, int mode) -{ - char *error; - int (*__proc_access)(const char *path, int mode); - - dlerror(); - __proc_access = (int (*)(const char *, int mode))dlsym(dlopen_handle, "proc_access"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find proc_access()", error); - - return __proc_access(path, mode); -} - -static int do_sys_open(const char *path, struct fuse_file_info *fi) -{ - char *error; - int (*__sys_open)(const char *path, struct fuse_file_info *fi); - - dlerror(); - __sys_open = (int (*)(const char *path, struct fuse_file_info *fi))dlsym(dlopen_handle, "sys_open"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find sys_open()", error); - - return __sys_open(path, fi); -} - -static int do_sys_opendir(const char *path, struct fuse_file_info *fi) -{ - char *error; - int (*__sys_opendir)(const char *path, struct fuse_file_info *fi); - - dlerror(); - __sys_opendir = (int (*)(const char *path, struct fuse_file_info *fi))dlsym(dlopen_handle, "sys_opendir"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find sys_opendir()", error); - - return __sys_opendir(path, fi); -} - -static int do_sys_access(const char *path, int mode) -{ - char *error; - int (*__sys_access)(const char *path, int mode); - - dlerror(); - __sys_access = (int (*)(const char *, int mode))dlsym(dlopen_handle, "sys_access"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find sys_access()", error); - - return __sys_access(path, mode); -} - -static int do_cg_release(const char *path, struct fuse_file_info *fi) -{ - char *error; - int (*__cg_release)(const char *path, struct fuse_file_info *fi); - - dlerror(); - __cg_release = (int (*)(const char *path, struct fuse_file_info *))dlsym(dlopen_handle, "cg_release"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find cg_release()", error); - - return __cg_release(path, fi); -} - -static int do_proc_release(const char *path, struct fuse_file_info *fi) -{ - char *error; - int (*__proc_release)(const char *path, struct fuse_file_info *fi); - - dlerror(); - __proc_release = (int (*)(const char *path, struct fuse_file_info *)) dlsym(dlopen_handle, "proc_release"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find proc_release()", error); - - return __proc_release(path, fi); -} - -static int do_sys_release(const char *path, struct fuse_file_info *fi) -{ - char *error; - int (*__sys_release)(const char *path, struct fuse_file_info *fi); - - dlerror(); - __sys_release = (int (*)(const char *path, struct fuse_file_info *))dlsym(dlopen_handle, "sys_release"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find sys_release()", error); - - return __sys_release(path, fi); -} - -static int do_cg_opendir(const char *path, struct fuse_file_info *fi) -{ - char *error; - int (*__cg_opendir)(const char *path, struct fuse_file_info *fi); - - dlerror(); - __cg_opendir = (int (*)(const char *path, struct fuse_file_info *fi))dlsym(dlopen_handle, "cg_opendir"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find cg_opendir()", error); - - return __cg_opendir(path, fi); -} - -static int do_cg_releasedir(const char *path, struct fuse_file_info *fi) -{ - char *error; - int (*__cg_releasedir)(const char *path, struct fuse_file_info *fi); - - dlerror(); - __cg_releasedir = (int (*)(const char *path, struct fuse_file_info *))dlsym(dlopen_handle, "cg_releasedir"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find cg_releasedir()", error); - - return __cg_releasedir(path, fi); -} - -static int do_sys_releasedir(const char *path, struct fuse_file_info *fi) -{ - char *error; - int (*__sys_releasedir)(const char *path, struct fuse_file_info *fi); - - dlerror(); - __sys_releasedir = (int (*)(const char *path, struct fuse_file_info *))dlsym(dlopen_handle, "sys_releasedir"); - error = dlerror(); - if (error) - return log_error(-1, "%s - Failed to find sys_releasedir()", error); - - return __sys_releasedir(path, fi); -} +#define DEF_LIB_FS_OP(type, fsop) \ +static int do_##type##_##fsop(LIB_FS_##fsop##_OP_ARGS_TYPE) \ +{ \ + char *error; \ + int (*__##type##_##fsop)(LIB_FS_##fsop##_OP_ARGS_TYPE); \ + \ + dlerror(); \ + __##type##_##fsop = (int (*)(LIB_FS_##fsop##_OP_ARGS_TYPE))dlsym(dlopen_handle, #type"_"#fsop); \ + error = dlerror(); \ + if (error) \ + return log_error(-1, "%s - Failed to find "#type"_"#fsop"()", error); \ + \ + return __##type##_##fsop(LIB_FS_##fsop##_OP_ARGS); \ +} + +#define LIB_FS_getattr_OP_ARGS_TYPE const char *path, struct stat *sb +#define LIB_FS_getattr_OP_ARGS path, sb +DEF_LIB_FS_OP(cg , getattr) +DEF_LIB_FS_OP(proc , getattr) +DEF_LIB_FS_OP(sys , getattr) + +#define LIB_FS_read_OP_ARGS_TYPE const char *path, char *buf, size_t size, \ + off_t offset, struct fuse_file_info *fi +#define LIB_FS_read_OP_ARGS path, buf, size, offset, fi +DEF_LIB_FS_OP(cg , read) +DEF_LIB_FS_OP(proc , read) +DEF_LIB_FS_OP(sys , read) + +#define LIB_FS_write_OP_ARGS_TYPE const char *path, const char *buf, size_t size, \ + off_t offset, struct fuse_file_info *fi +#define LIB_FS_write_OP_ARGS path, buf, size, offset, fi +DEF_LIB_FS_OP(cg , write) +DEF_LIB_FS_OP(proc , write) +DEF_LIB_FS_OP(sys , write) + +#define LIB_FS_poll_OP_ARGS_TYPE const char *path, struct fuse_file_info *fi, \ + struct fuse_pollhandle *ph, unsigned *reventsp +#define LIB_FS_poll_OP_ARGS path, fi, ph, reventsp +DEF_LIB_FS_OP(proc , poll) + +#define LIB_FS_mkdir_OP_ARGS_TYPE const char *path, mode_t mode +#define LIB_FS_mkdir_OP_ARGS path, mode +DEF_LIB_FS_OP(cg, mkdir) + +#define LIB_FS_chown_OP_ARGS_TYPE const char *path, uid_t uid, gid_t gid +#define LIB_FS_chown_OP_ARGS path, uid, gid +DEF_LIB_FS_OP(cg, chown) + +#define LIB_FS_rmdir_OP_ARGS_TYPE const char *path +#define LIB_FS_rmdir_OP_ARGS path +DEF_LIB_FS_OP(cg, rmdir) + +#define LIB_FS_chmod_OP_ARGS_TYPE const char *path, mode_t mode +#define LIB_FS_chmod_OP_ARGS path, mode +DEF_LIB_FS_OP(cg, chmod) + +#define LIB_FS_readdir_OP_ARGS_TYPE const char *path, void *buf, fuse_fill_dir_t filler, \ + off_t offset, struct fuse_file_info *fi +#define LIB_FS_readdir_OP_ARGS path, buf, filler, offset, fi +DEF_LIB_FS_OP(cg , readdir) +DEF_LIB_FS_OP(proc , readdir) +DEF_LIB_FS_OP(sys , readdir) + +#define LIB_FS_readlink_OP_ARGS_TYPE const char *path, char *buf, size_t size +#define LIB_FS_readlink_OP_ARGS path, buf, size +DEF_LIB_FS_OP(sys , readlink) + +#define LIB_FS_open_OP_ARGS_TYPE const char *path, struct fuse_file_info *fi +#define LIB_FS_open_OP_ARGS path, fi +DEF_LIB_FS_OP(cg , open) +DEF_LIB_FS_OP(proc , open) +DEF_LIB_FS_OP(sys , open) + +#define LIB_FS_access_OP_ARGS_TYPE const char *path, int mode +#define LIB_FS_access_OP_ARGS path, mode +DEF_LIB_FS_OP(cg , access) +DEF_LIB_FS_OP(proc , access) +DEF_LIB_FS_OP(sys , access) + +#define LIB_FS_opendir_OP_ARGS_TYPE const char *path, struct fuse_file_info *fi +#define LIB_FS_opendir_OP_ARGS path, fi +DEF_LIB_FS_OP(cg , opendir) +DEF_LIB_FS_OP(sys , opendir) + +#define LIB_FS_release_OP_ARGS_TYPE const char *path, struct fuse_file_info *fi +#define LIB_FS_release_OP_ARGS path, fi +DEF_LIB_FS_OP(cg , release) +DEF_LIB_FS_OP(proc , release) +DEF_LIB_FS_OP(sys , release) + +#define LIB_FS_releasedir_OP_ARGS_TYPE const char *path, struct fuse_file_info *fi +#define LIB_FS_releasedir_OP_ARGS path, fi +DEF_LIB_FS_OP(cg , releasedir) +DEF_LIB_FS_OP(sys , releasedir) static bool cgroup_is_enabled = false; @@ -929,6 +603,13 @@ return ret; } + if (LXCFS_TYPE_PROC(type)) { + up_users(); + ret = do_proc_write(path, buf, size, offset, fi); + down_users(); + return ret; + } + if (LXCFS_TYPE_SYS(type)) { up_users(); ret = do_sys_write(path, buf, size, offset, fi); @@ -939,6 +620,27 @@ return -EINVAL; } +int lxcfs_poll(const char *path, struct fuse_file_info *fi, + struct fuse_pollhandle *ph, unsigned *reventsp) +{ + int ret; + enum lxcfs_virt_t type; + + type = file_info_type(fi); + + if (LXCFS_TYPE_PROC(type)) { + up_users(); + ret = do_proc_poll(path, fi, ph, reventsp); + down_users(); + return ret; + } + + /* default f_op->poll() behavior when not supported */ + fuse_pollhandle_destroy(ph); + *reventsp = DEFAULT_POLLMASK; + return 0; +} + int lxcfs_readlink(const char *path, char *buf, size_t size) { int ret; @@ -1166,6 +868,9 @@ .truncate = lxcfs_truncate, .write = lxcfs_write, .readlink = lxcfs_readlink, +#if HAVE_FUSE3 + .poll = lxcfs_poll, +#endif .create = NULL, .destroy = NULL, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lxcfs-6.0.5/src/macro.h new/lxcfs-6.0.6/src/macro.h --- old/lxcfs-6.0.5/src/macro.h 2025-08-15 06:47:54.000000000 +0200 +++ new/lxcfs-6.0.6/src/macro.h 2026-02-23 20:21:25.000000000 +0100 @@ -20,6 +20,8 @@ #define CGROUP2_SUPER_MAGIC 0x63677270 #endif +#define DEFAULT_POLLMASK (EPOLLIN | EPOLLOUT | EPOLLRDNORM | EPOLLWRNORM) + #define lxcfs_debug_stream(stream, format, ...) \ do { \ fprintf(stream, "%s: %d: %s: " format "\n", __FILE__, \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lxcfs-6.0.5/src/proc_fuse.c new/lxcfs-6.0.6/src/proc_fuse.c --- old/lxcfs-6.0.5/src/proc_fuse.c 2025-08-15 06:47:54.000000000 +0200 +++ new/lxcfs-6.0.6/src/proc_fuse.c 2026-02-23 20:21:25.000000000 +0100 @@ -7,6 +7,7 @@ #include <fcntl.h> #include <inttypes.h> #include <libgen.h> +#include <poll.h> #include <pthread.h> #include <sched.h> #include <stdarg.h> @@ -242,12 +243,6 @@ return 0; } -__lxcfs_fuse_ops int proc_release(const char *path, struct fuse_file_info *fi) -{ - do_release_file_info(fi); - return 0; -} - /** * Gets a non-hierarchical memory controller limit, or UINT64_MAX if no limit is * in place. If `swap` is true, reads 'swap' (v2) or 'memsw' (v1); otherwise @@ -999,6 +994,8 @@ char *cache = d->buf + CPUALL_MAX_SIZE; size_t cache_size = d->buflen - CPUALL_MAX_SIZE; int cg_cpu_usage_size = 0; + bool use_view; + int max_cpus = 0; if (offset) { size_t left; @@ -1064,6 +1061,13 @@ lxcfs_v("proc_stat_read failed to read from cpuacct, falling back to the host's /proc/stat"); } + if (cgroup_ops->can_use_cpuview(cgroup_ops) && opts && opts->use_cfs) + use_view = true; + else + use_view = false; + if (use_view) + max_cpus = max_cpu_count(cg, cpu_cg); + while (getline(&line, &linelen, f) != -1) { ssize_t l; char cpu_char[10]; /* That's a lot of cores */ @@ -1091,12 +1095,15 @@ if (sscanf(cpu_char, "%d", &physcpu) != 1) continue; + if (use_view && max_cpus > 0 && (curcpu + 1) == max_cpus) + continue; // cannot break here because we need to consume all non-cpu lines + if (!cpu_in_cpuset(physcpu, cpuset)) continue; curcpu++; - if (cgroup_ops->can_use_cpuview(cgroup_ops) && opts && opts->use_cfs) + if (use_view) cpu_to_render = curcpu; else cpu_to_render = physcpu; @@ -1710,3 +1717,23 @@ return -EINVAL; } + +__lxcfs_fuse_ops int proc_write(const char *path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + return -EINVAL; +} + +__lxcfs_fuse_ops int proc_poll(const char *path, struct fuse_file_info *fi, + struct fuse_pollhandle *ph, unsigned *reventsp) +{ + fuse_pollhandle_destroy(ph); + *reventsp = DEFAULT_POLLMASK; + return 0; +} + +__lxcfs_fuse_ops int proc_release(const char *path, struct fuse_file_info *fi) +{ + do_release_file_info(fi); + return 0; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lxcfs-6.0.5/src/proc_fuse.h new/lxcfs-6.0.6/src/proc_fuse.h --- old/lxcfs-6.0.5/src/proc_fuse.h 2025-08-15 06:47:54.000000000 +0200 +++ new/lxcfs-6.0.6/src/proc_fuse.h 2026-02-23 20:21:25.000000000 +0100 @@ -20,6 +20,8 @@ __visible extern int proc_open(const char *path, struct fuse_file_info *fi); __visible extern int proc_access(const char *path, int mask); __visible extern int proc_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi); +__visible extern int proc_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi); +__visible extern int proc_poll(const char *path, struct fuse_file_info *fi, struct fuse_pollhandle *ph, unsigned *reventsp); __visible extern int proc_release(const char *path, struct fuse_file_info *fi); #endif /* __LXCFS_PROC_FUSE_H */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lxcfs-6.0.5/src/utils.c new/lxcfs-6.0.6/src/utils.c --- old/lxcfs-6.0.5/src/utils.c 2025-08-15 06:47:54.000000000 +0200 +++ new/lxcfs-6.0.6/src/utils.c 2026-02-23 20:21:25.000000000 +0100 @@ -113,7 +113,7 @@ return false; fd = in_same_namespace(pid, getpid(), "pid"); - if (fd == EINVAL) + if (fd == -EINVAL) return true; return false;
