From patchwork Wed Nov 11 04:20:29 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103612 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.2565.1794370864776787971 for ; Tue, 10 Nov 2026 20:21:05 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3ll2428068 for ; Tue, 10 Nov 2026 22:21:04 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 01/34] pseudo_client: serialize path changes across clients Date: Tue, 10 Nov 2026 22:20:29 -0600 Message-Id: <1794370862-30908-2-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:12 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5260 MIME-Version: 1.0 From: Mark Hatle test-parallel-rename and test-parallel-symlinks failed with "inode mismatch" and "path mismatch" errors, and clients were aborted by the server. rename() and unlink() look at the destination, send OP_MAY_UNLINK, make the change on disk, and then send OP_DID_UNLINK and OP_RENAME, as separate steps. The wrapper lock only covers one process, so another client can rename onto the same name in between. The first client then marks and removes the wrong inode, and its OP_RENAME finds a different entry at the destination. ln -sf does the same, as it creates a temporary symlink and renames it over the link. Readers race in the same way: a client can lstat() a name before another client replaces it, but only report the result to the server afterwards. If the filesystem has meanwhile reused the inode number for a new file, the server sees the inode under the wrong path, and aborts the reader. Add a path lock shared by all clients of a server, using fcntl() locks on pseudo.pathlock in the local state directory. makewrappers gains a path_lock option which holds the lock around the guts of a wrapper: exclusive (path_lock=1) for rename, renameat, unlink, unlinkat and rmdir, and shared (path_lock=shared) for the stat family, so readers don't block each other but can't observe a name in the middle of a change. fcntl() locks belong to the process, so a forked child doesn't share them, and they are released if the process dies. The descriptor is kept open and protected like pseudo's other internal descriptors. If the lock can't be taken the operation runs unlocked as before. Both tests now pass consistently, including with PSEUDO_DEBUG set. The lock adds about 4us to an lstat() (about 12%) and 2% to a rename. AI-Generated: Uses GitHub Copilot Signed-off-by: Mark Hatle --- makewrappers | 17 +++++++++ ports/linux/statx/wrapfuncs.in | 2 +- ports/linux/wrapfuncs.in | 24 ++++++------ ports/unix/wrapfuncs.in | 10 ++--- pseudo.h | 1 + pseudo_client.c | 83 +++++++++++++++++++++++++++++++++++++++++- pseudo_client.h | 2 + templates/wrapfuncs.c | 2 +- 8 files changed, 121 insertions(+), 20 deletions(-) diff --git a/makewrappers b/makewrappers index 1ac46bf..303ae32 100755 --- a/makewrappers +++ b/makewrappers @@ -248,6 +248,11 @@ class Function: self.noignore_path = False self.hand_wrapped = None self.async_skip = None + # Hold the client path lock (pseudo_client_path_lock()) around + # the guts: path_lock=1 (exclusive) for operations that check, + # change and then record a path in separate steps, and + # path_lock=shared for operations that only look at a path. + self.path_lock = None # used for the copyright date when creating stub functions self.date = datetime.date.today().year # Used to define pointers that should EFAULT if null @@ -492,6 +497,18 @@ class Function: else: return "rc =" + def path_lock_begin(self): + """take the client path lock, for path_lock=1 functions""" + if self.path_lock: + return "int path_lock_fd = pseudo_client_path_lock(%d);\n\t\t\t" % (self.path_lock == "shared") + return "" + + def path_lock_end(self): + """release the client path lock, for path_lock=1 functions""" + if self.path_lock: + return "\n\t\t\tpseudo_client_path_unlock(path_lock_fd);" + return "" + def def_return(self): """return default value (or just return)""" if self.type == 'void': diff --git a/ports/linux/statx/wrapfuncs.in b/ports/linux/statx/wrapfuncs.in index a81fb91..3ca40ac 100644 --- a/ports/linux/statx/wrapfuncs.in +++ b/ports/linux/statx/wrapfuncs.in @@ -1 +1 @@ -int statx(int dirfd, const char *path, int flags, unsigned int mask, struct statx *statxbuf); /* efault=path */ +int statx(int dirfd, const char *path, int flags, unsigned int mask, struct statx *statxbuf); /* efault=path, path_lock=shared */ diff --git a/ports/linux/wrapfuncs.in b/ports/linux/wrapfuncs.in index d417ed3..65f2d7a 100644 --- a/ports/linux/wrapfuncs.in +++ b/ports/linux/wrapfuncs.in @@ -2,12 +2,12 @@ int open(const char *path, int flags, ...{mode_t mode}); /* flags=((flags&O_NOFO int __open_2(const char *path, int flags); /* flags=((flags&O_NOFOLLOW)||((flags&(O_CREAT|O_EXCL))==(O_CREAT|O_EXCL))), noignore_path=1 */ int __open64_2(const char *path, int flags); /* flags=((flags&O_NOFOLLOW)||((flags&(O_CREAT|O_EXCL))==(O_CREAT|O_EXCL))), noignore_path=1 */ char *get_current_dir_name(void); -int __xstat(int ver, const char *path, struct stat *buf); -int __lxstat(int ver, const char *path, struct stat *buf); /* flags=AT_SYMLINK_NOFOLLOW */ +int __xstat(int ver, const char *path, struct stat *buf); /* path_lock=shared */ +int __lxstat(int ver, const char *path, struct stat *buf); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=shared */ int __fxstat(int ver, int fd, struct stat *buf); int lchmod(const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW */ int lchown(const char *path, uid_t owner, gid_t group); /* flags=AT_SYMLINK_NOFOLLOW */ -int __fxstatat(int ver, int dirfd, const char *path, struct stat *buf, int flags); +int __fxstatat(int ver, int dirfd, const char *path, struct stat *buf, int flags); /* path_lock=shared */ int openat(int dirfd, const char *path, int flags, ...{mode_t mode}); /* flags=((flags&O_NOFOLLOW)||((flags&(O_CREAT|O_EXCL))==(O_CREAT|O_EXCL))), noignore_path=1 */ int __openat_2(int dirfd, const char *path, int flags); /* flags=((flags&O_NOFOLLOW)||((flags&(O_CREAT|O_EXCL))==(O_CREAT|O_EXCL))), noignore_path=1 */ int open_tree(int dirfd, const char *path, unsigned int flags); /* preserve_paths=1, noignore_path=1 */ @@ -25,18 +25,18 @@ int open64(const char *path, int flags, ...{mode_t mode}); /* flags=((flags&O_NO int openat64(int dirfd, const char *path, int flags, ...{mode_t mode}); /* flags=((flags&O_NOFOLLOW)||((flags&(O_CREAT|O_EXCL))==(O_CREAT|O_EXCL))), noignore_path=1 */ int __openat64_2(int dirfd, const char *path, int flags); /* flags=((flags&O_NOFOLLOW)||((flags&(O_CREAT|O_EXCL))==(O_CREAT|O_EXCL))), noignore_path=1 */ int creat64(const char *path, mode_t mode); -int stat(const char *path, struct stat *buf); /* real_func=pseudo_stat */ -int lstat(const char *path, struct stat *buf); /* real_func=pseudo_lstat, flags=AT_SYMLINK_NOFOLLOW */ +int stat(const char *path, struct stat *buf); /* real_func=pseudo_stat, path_lock=shared */ +int lstat(const char *path, struct stat *buf); /* real_func=pseudo_lstat, flags=AT_SYMLINK_NOFOLLOW, path_lock=shared */ int fstat(int fd, struct stat *buf); /* real_func=pseudo_fstat */ -int fstatat(int dirfd, const char *path, struct stat *buf, int flags); -int stat64(const char *path, struct stat64 *buf); /* real_func=pseudo_stat64 */ -int lstat64(const char *path, struct stat64 *buf); /* real_func=pseudo_lstat64, flags=AT_SYMLINK_NOFOLLOW */ +int fstatat(int dirfd, const char *path, struct stat *buf, int flags); /* path_lock=shared */ +int stat64(const char *path, struct stat64 *buf); /* real_func=pseudo_stat64, path_lock=shared */ +int lstat64(const char *path, struct stat64 *buf); /* real_func=pseudo_lstat64, flags=AT_SYMLINK_NOFOLLOW, path_lock=shared */ int fstat64(int fd, struct stat64 *buf); /* real_func=pseudo_fstat64 */ -int fstatat64(int dirfd, const char *path, struct stat64 *buf, int flags); -int __xstat64(int ver, const char *path, struct stat64 *buf); -int __lxstat64(int ver, const char *path, struct stat64 *buf); /* flags=AT_SYMLINK_NOFOLLOW */ +int fstatat64(int dirfd, const char *path, struct stat64 *buf, int flags); /* path_lock=shared */ +int __xstat64(int ver, const char *path, struct stat64 *buf); /* path_lock=shared */ +int __lxstat64(int ver, const char *path, struct stat64 *buf); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=shared */ int __fxstat64(int ver, int fd, struct stat64 *buf); -int __fxstatat64(int ver, int dirfd, const char *path, struct stat64 *buf, int flags); +int __fxstatat64(int ver, int dirfd, const char *path, struct stat64 *buf, int flags); /* path_lock=shared */ FILE *fopen64(const char *path, const char *mode); /* noignore_path=1 */ int nftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int, struct FTW *), int nopenfd, int flag); /* noignore_path=1 */ FILE *freopen64(const char *path, const char *mode, FILE *stream); /* noignore_path=1 */ diff --git a/ports/unix/wrapfuncs.in b/ports/unix/wrapfuncs.in index d797235..5956742 100644 --- a/ports/unix/wrapfuncs.in +++ b/ports/unix/wrapfuncs.in @@ -53,13 +53,13 @@ int mkstemp(char *template); /* flags=AT_SYMLINK_NOFOLLOW */ int mkostemp(char *template, int oflags); /* flags=AT_SYMLINK_NOFOLLOW */ int mkstemps(char *template, int suffixlen); /* flags=AT_SYMLINK_NOFOLLOW */ int mkostemps(char *template, int suffixlen, int oflags); /* flags=AT_SYMLINK_NOFOLLOW */ -int rename(const char *oldpath, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW */ -int renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW */ -int rmdir(const char *path); /* flags=AT_SYMLINK_NOFOLLOW */ +int rename(const char *oldpath, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ +int renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ +int rmdir(const char *path); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ int symlink(const char *oldname, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW */ int symlinkat(const char *oldname, int dirfd, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW */ -int unlink(const char *path); /* flags=AT_SYMLINK_NOFOLLOW */ -int unlinkat(int dirfd, const char *path, int rflags); /* flags=AT_SYMLINK_NOFOLLOW */ +int unlink(const char *path); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ +int unlinkat(int dirfd, const char *path, int rflags); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ # primarily for use with chroot() ssize_t readlink(const char *path, char *buf, size_t bufsiz); /* flags=AT_SYMLINK_NOFOLLOW */ ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz); /* flags=AT_SYMLINK_NOFOLLOW */ diff --git a/pseudo.h b/pseudo.h index 0047f2f..6d50b77 100644 --- a/pseudo.h +++ b/pseudo.h @@ -129,6 +129,7 @@ extern char *pseudo_version; #define PSEUDO_LOCKFILE "pseudo.lock" #define PSEUDO_LOGFILE "pseudo.log" +#define PSEUDO_PATHLOCK "pseudo.pathlock" #define PSEUDO_PIDFILE "pseudo.pid" #define PSEUDO_SOCKET "pseudo.socket" diff --git a/pseudo_client.c b/pseudo_client.c index 7f425dd..ffd3450 100644 --- a/pseudo_client.c +++ b/pseudo_client.c @@ -48,6 +48,7 @@ static char *base_path(int dirfd, const char *path, int leave_last); static int connect_fd = -1; static int server_pid = 0; int pseudo_localstate_dir_fd = -1; +static int pseudo_pathlock_fd = -1; int pseudo_pwd_fd = -1; int pseudo_pwd_lck_fd = -1; char *pseudo_pwd_lck_name = NULL; @@ -533,6 +534,7 @@ pseudo_init_client(void) { /* Ensure that all of the values are reset */ server_pid = 0; pseudo_localstate_dir_fd = -1; + pseudo_pathlock_fd = -1; pseudo_pwd_fd = -1; pseudo_pwd_lck_fd = -1; pseudo_pwd_lck_name = NULL; @@ -1149,6 +1151,80 @@ pseudo_fd(int fd, int how) { return(newfd); } +/* Operations such as rename() and unlink() look at the destination, change + * it on disk, and then tell the server what happened, in several separate + * steps. Another client doing the same to the same name in between makes + * the database describe a file that is no longer there, which the server + * reports as an inode or path mismatch. Wrappers marked path_lock=1 hold + * this lock, shared by every client of the server, for the whole sequence. + * + * fcntl() locks belong to the process, so a forked child does not share + * them even though it inherits the descriptor, and they are released if + * the process dies. The descriptor is kept open and protected like + * pseudo's other internal descriptors. + * + * Wrappers that only look at a path (stat() and friends) take the lock + * shared: they don't block each other, but can't observe a path between + * the change on disk and the database update. + * + * Returns the descriptor to pass to pseudo_client_path_unlock(), or -1 if + * the lock could not be taken, in which case the operation runs unlocked. + */ +int +pseudo_client_path_lock(int shared) { + static char *lock_path = NULL; + struct flock lk = { .l_type = shared ? F_RDLCK : F_WRLCK, .l_whence = SEEK_SET }; + int save_errno = errno; + int locked = 0, retried = 0; + + pseudo_antimagic(); + for (;;) { + if (pseudo_pathlock_fd == -1) { + if (!lock_path) + lock_path = pseudo_localstatedir_path(PSEUDO_PATHLOCK); + if (lock_path) + pseudo_pathlock_fd = pseudo_fd(open(lock_path, + O_RDWR | O_CREAT | O_CLOEXEC, 0644), MOVE_FD); + if (pseudo_pathlock_fd == -1) { + pseudo_debug(PDBGF_CLIENT, "path lock: can't open '%s': %s\n", + lock_path ? lock_path : "", strerror(errno)); + break; + } + } + if (fcntl(pseudo_pathlock_fd, F_SETLKW, &lk) == 0) { + locked = 1; + break; + } + if (errno == EINTR) + continue; + /* closed behind our back: reopen it once */ + if (errno == EBADF && !retried) { + pseudo_pathlock_fd = -1; + retried = 1; + continue; + } + pseudo_debug(PDBGF_CLIENT, "path lock: can't lock '%s': %s\n", + lock_path ? lock_path : "", strerror(errno)); + break; + } + pseudo_magic(); + errno = save_errno; + return locked ? pseudo_pathlock_fd : -1; +} + +void +pseudo_client_path_unlock(int fd) { + struct flock lk = { .l_type = F_UNLCK, .l_whence = SEEK_SET }; + int save_errno = errno; + + if (fd != -1) { + pseudo_antimagic(); + fcntl(fd, F_SETLK, &lk); + pseudo_magic(); + } + errno = save_errno; +} + static int client_connect(void) { /* we have a server pid, is it responsive? */ @@ -2018,6 +2094,8 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path startfd = pseudo_util_evlog_fd + 1; if (pseudo_localstate_dir_fd >= startfd) startfd = pseudo_localstate_dir_fd + 1; + if (pseudo_pathlock_fd >= startfd) + startfd = pseudo_pathlock_fd + 1; if (pseudo_pwd_fd >= startfd) startfd = pseudo_pwd_fd + 1; if (pseudo_pwd_lck_fd >= startfd) @@ -2031,7 +2109,8 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path */ for (i = fd; i < startfd && i <= close_range_maxfd; ++i) { if (i == pseudo_util_debug_fd || i == pseudo_util_evlog_fd || - i == pseudo_localstate_dir_fd || i == pseudo_pwd_fd || + i == pseudo_localstate_dir_fd || i == pseudo_pathlock_fd || + i == pseudo_pwd_fd || i == pseudo_pwd_lck_fd || i == pseudo_grp_fd || i == connect_fd) continue; @@ -2057,6 +2136,8 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path pseudo_util_debug_fd = pseudo_fd(fd, COPY_FD); } else if (fd == pseudo_localstate_dir_fd) { pseudo_localstate_dir_fd = pseudo_fd(fd, COPY_FD); + } else if (fd == pseudo_pathlock_fd) { + pseudo_pathlock_fd = pseudo_fd(fd, COPY_FD); } else if (fd == pseudo_pwd_fd) { pseudo_pwd_fd = pseudo_fd(fd, COPY_FD); /* since we have a FILE * on it, we close that... */ diff --git a/pseudo_client.h b/pseudo_client.h index 19f3901..3958f6d 100644 --- a/pseudo_client.h +++ b/pseudo_client.h @@ -27,6 +27,8 @@ extern void pseudo_client_touchuid(void); extern void pseudo_client_touchgid(void); extern int pseudo_client_shutdown(int); extern int pseudo_fd(int fd, int how); +extern int pseudo_client_path_lock(int shared); +extern void pseudo_client_path_unlock(int fd); #define MOVE_FD 0 #define COPY_FD 1 #define PSEUDO_MIN_FD 20 diff --git a/templates/wrapfuncs.c b/templates/wrapfuncs.c index 9385a40..a57c997 100644 --- a/templates/wrapfuncs.c +++ b/templates/wrapfuncs.c @@ -68,7 +68,7 @@ ${maybe_async_skip} } else { /* exec*() use this to restore the sig mask */ pseudo_saved_sigmask = saved; - ${rc_assign} wrap_$name(${call_args}); + ${path_lock_begin}${rc_assign} wrap_$name(${call_args});${path_lock_end} } } ${variadic_end} From patchwork Wed Nov 11 04:20:30 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103619 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.2566.1794370864802514744 for ; Tue, 10 Nov 2026 20:21:05 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3lm2428068 for ; Tue, 10 Nov 2026 22:21:04 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 02/34] ports: hold the path lock in create and metadata wrappers Date: Tue, 10 Nov 2026 22:20:30 -0600 Message-Id: <1794370862-30908-3-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:14 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5261 MIME-Version: 1.0 From: Mark Hatle Like rename() and unlink(), the wrappers that create a name, or that look at a path and then change its metadata in the database, do so in separate steps: the real operation on disk, then a request to the server. Another client renaming onto or removing the same name in between leaves the database out of step with the filesystem, which the server reports as an inode or path mismatch, and may abort a client. Hold the path lock exclusively in the wrappers that create a name: link, linkat, symlink, symlinkat, mkdir, mkdirat, mkfifo, mkfifoat, mknod, mknodat, __xmknod, __xmknodat, bind, mkdtemp and the mkstemp family. None of these can block indefinitely. Hold it shared in the wrappers that look at a path and then record or query its metadata: chmod, lchmod, fchmodat, chown, lchown, fchownat, fts_read, fts_children, and the path based xattr calls. AI-Generated: Uses GitHub Copilot Signed-off-by: Mark Hatle --- ports/linux/wrapfuncs.in | 16 +++++++-------- ports/linux/xattr/wrapfuncs.in | 16 +++++++-------- ports/unix/wrapfuncs.in | 46 +++++++++++++++++++++--------------------- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/ports/linux/wrapfuncs.in b/ports/linux/wrapfuncs.in index 65f2d7a..a682383 100644 --- a/ports/linux/wrapfuncs.in +++ b/ports/linux/wrapfuncs.in @@ -5,17 +5,17 @@ char *get_current_dir_name(void); int __xstat(int ver, const char *path, struct stat *buf); /* path_lock=shared */ int __lxstat(int ver, const char *path, struct stat *buf); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=shared */ int __fxstat(int ver, int fd, struct stat *buf); -int lchmod(const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW */ -int lchown(const char *path, uid_t owner, gid_t group); /* flags=AT_SYMLINK_NOFOLLOW */ +int lchmod(const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=shared */ +int lchown(const char *path, uid_t owner, gid_t group); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=shared */ int __fxstatat(int ver, int dirfd, const char *path, struct stat *buf, int flags); /* path_lock=shared */ int openat(int dirfd, const char *path, int flags, ...{mode_t mode}); /* flags=((flags&O_NOFOLLOW)||((flags&(O_CREAT|O_EXCL))==(O_CREAT|O_EXCL))), noignore_path=1 */ int __openat_2(int dirfd, const char *path, int flags); /* flags=((flags&O_NOFOLLOW)||((flags&(O_CREAT|O_EXCL))==(O_CREAT|O_EXCL))), noignore_path=1 */ int open_tree(int dirfd, const char *path, unsigned int flags); /* preserve_paths=1, noignore_path=1 */ int open_tree_attr(int dirfd, const char *path, unsigned int flags, struct mount_attr *attr, size_t size); /* preserve_paths=1, noignore_path=1*/ -int mknod(const char *path, mode_t mode, dev_t dev); /* real_func=pseudo_mknod */ -int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev); /* real_func=pseudo_mknodat */ -int __xmknod(int ver, const char *path, mode_t mode, dev_t *dev); /* flags=AT_SYMLINK_NOFOLLOW */ -int __xmknodat(int ver, int dirfd, const char *path, mode_t mode, dev_t *dev); /* flags=AT_SYMLINK_NOFOLLOW */ +int mknod(const char *path, mode_t mode, dev_t dev); /* real_func=pseudo_mknod, path_lock=1 */ +int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev); /* real_func=pseudo_mknodat, path_lock=1 */ +int __xmknod(int ver, const char *path, mode_t mode, dev_t *dev); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ +int __xmknodat(int ver, int dirfd, const char *path, mode_t mode, dev_t *dev); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ int fcntl(int fd, int cmd, ...{struct flock *lock}); /* noignore_path=1 */ int fcntl64(int fd, int cmd, ...{struct flock *lock}); /* noignore_path=1 */ # just so we know the inums of symlinks @@ -44,8 +44,8 @@ int ftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int), int glob64(const char *pattern, int flags, int (*errfunc)(const char *, int), glob64_t *pglob); int scandir64(const char *path, struct dirent64 ***namelist, int (*filter)(const struct dirent64 *), int (*compar)()); int truncate64(const char *path, off64_t length); -int mkostemp64(char *template, int oflags); /* flags=AT_SYMLINK_NOFOLLOW */ -int mkstemp64(char *template); /* flags=AT_SYMLINK_NOFOLLOW */ +int mkostemp64(char *template, int oflags); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ +int mkstemp64(char *template); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups); int setgroups(size_t size, const gid_t *list); int setfsgid(gid_t fsgid); diff --git a/ports/linux/xattr/wrapfuncs.in b/ports/linux/xattr/wrapfuncs.in index 09eba23..89b1c68 100644 --- a/ports/linux/xattr/wrapfuncs.in +++ b/ports/linux/xattr/wrapfuncs.in @@ -1,12 +1,12 @@ -ssize_t getxattr(const char *path, const char *name, void *value, size_t size); /* flags=0, version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */ -ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size); /* flags=AT_SYMLINK_NOFOLLOW, version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */ +ssize_t getxattr(const char *path, const char *name, void *value, size_t size); /* flags=0, version="GLIBC_2.3", version-aarch64="GLIBC_2.17", path_lock=shared */ +ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size); /* flags=AT_SYMLINK_NOFOLLOW, version="GLIBC_2.3", version-aarch64="GLIBC_2.17", path_lock=shared */ ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size); /* version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */ -int setxattr(const char *path, const char *name, const void *value, size_t size, int xflags); /* flags=0, version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */ -int lsetxattr(const char *path, const char *name, const void *value, size_t size, int xflags); /* flags=AT_SYMLINK_NOFOLLOW, version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */ +int setxattr(const char *path, const char *name, const void *value, size_t size, int xflags); /* flags=0, version="GLIBC_2.3", version-aarch64="GLIBC_2.17", path_lock=shared */ +int lsetxattr(const char *path, const char *name, const void *value, size_t size, int xflags); /* flags=AT_SYMLINK_NOFOLLOW, version="GLIBC_2.3", version-aarch64="GLIBC_2.17", path_lock=shared */ int fsetxattr(int filedes, const char *name, const void *value, size_t size, int xflags); /* version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */ -ssize_t listxattr(const char *path, char *list, size_t size); /* flags=0, version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */ -ssize_t llistxattr(const char *path, char *list, size_t size); /* flags=AT_SYMLINK_NOFOLLOW, version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */ +ssize_t listxattr(const char *path, char *list, size_t size); /* flags=0, version="GLIBC_2.3", version-aarch64="GLIBC_2.17", path_lock=shared */ +ssize_t llistxattr(const char *path, char *list, size_t size); /* flags=AT_SYMLINK_NOFOLLOW, version="GLIBC_2.3", version-aarch64="GLIBC_2.17", path_lock=shared */ ssize_t flistxattr(int filedes, char *list, size_t size); /* version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */ -int removexattr(const char *path, const char *name); /* flags=0, version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */ -int lremovexattr(const char *path, const char *name); /* flags=AT_SYMLINK_NOFOLLOW, version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */ +int removexattr(const char *path, const char *name); /* flags=0, version="GLIBC_2.3", version-aarch64="GLIBC_2.17", path_lock=shared */ +int lremovexattr(const char *path, const char *name); /* flags=AT_SYMLINK_NOFOLLOW, version="GLIBC_2.3", version-aarch64="GLIBC_2.17", path_lock=shared */ int fremovexattr(int filedes, const char *name); /* version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */ diff --git a/ports/unix/wrapfuncs.in b/ports/unix/wrapfuncs.in index 5956742..5ffee83 100644 --- a/ports/unix/wrapfuncs.in +++ b/ports/unix/wrapfuncs.in @@ -4,7 +4,7 @@ char *getwd(char *buf); int close(int fd); /* noignore_path=1 */ int fchmod(int fd, mode_t mode); int fchown(int fd, uid_t owner, gid_t group); -int lchown(const char *path, uid_t owner, gid_t group); /* flags=AT_SYMLINK_NOFOLLOW */ +int lchown(const char *path, uid_t owner, gid_t group); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=shared */ int dup2(int oldfd, int newfd); /* noignore_path=1 */ int dup(int fd); /* noignore_path=1 */ int chdir(const char *path); /* noignore_path=1 */ @@ -13,13 +13,13 @@ int access(const char *path, int mode); int faccessat(int dirfd, const char *path, int mode, int flags); int faccessat2(int dirfd, const char *path, int mode, int flags); FTS *fts_open(char * const *path_argv, int options, int (*compar)(const FTSENT **, const FTSENT **)); -FTSENT *fts_read(FTS *ftsp); /* noignore_path=1 */ -FTSENT *fts_children(FTS *ftsp, int options); /* noignore_path=1 */ +FTSENT *fts_read(FTS *ftsp); /* noignore_path=1, path_lock=shared */ +FTSENT *fts_children(FTS *ftsp, int options); /* noignore_path=1, path_lock=shared */ int ftw(const char *path, int (*fn)(const char *, const struct stat *, int), int nopenfd); int nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int nopenfd, int flag); int glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob); int lutimes(const char *path, const struct timeval *tv); /* flags=AT_SYMLINK_NOFOLLOW */ -char *mkdtemp(char *template); +char *mkdtemp(char *template); /* path_lock=1 */ char *mktemp(char *template); long pathconf(const char *path, int name); char *realpath(const char *name, char *resolved_name); /* version="GLIBC_2.3" */ @@ -37,27 +37,27 @@ int fclose(FILE *fp); /* noignore_path=1 */ FILE *freopen(const char *path, const char *mode, FILE *stream); /* noignore_path=1 */ int chroot(const char *path); /* noignore_path=1 */ int acct(const char *path); -int chmod(const char *path, mode_t mode); -int chown(const char *path, uid_t owner, gid_t group); -int fchmodat(int dirfd, const char *path, mode_t mode, int flags); -int fchownat(int dirfd, const char *path, uid_t owner, gid_t group, int flags); -int link(const char *oldname, const char *newname); /* flags=AT_SYMLINK_NOFOLLOW */ -int linkat(int olddirfd, const char *oldname, int newdirfd, const char *newname, int flags); -int mkdir(const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW */ -int mkdirat(int dirfd, const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW */ -int mkfifo(const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW */ -int mkfifoat(int dirfd, const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW */ -int mknod(const char *path, mode_t mode, dev_t dev); /* flags=AT_SYMLINK_NOFOLLOW */ -int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev); /* flags=AT_SYMLINK_NOFOLLOW */ -int mkstemp(char *template); /* flags=AT_SYMLINK_NOFOLLOW */ -int mkostemp(char *template, int oflags); /* flags=AT_SYMLINK_NOFOLLOW */ -int mkstemps(char *template, int suffixlen); /* flags=AT_SYMLINK_NOFOLLOW */ -int mkostemps(char *template, int suffixlen, int oflags); /* flags=AT_SYMLINK_NOFOLLOW */ +int chmod(const char *path, mode_t mode); /* path_lock=shared */ +int chown(const char *path, uid_t owner, gid_t group); /* path_lock=shared */ +int fchmodat(int dirfd, const char *path, mode_t mode, int flags); /* path_lock=shared */ +int fchownat(int dirfd, const char *path, uid_t owner, gid_t group, int flags); /* path_lock=shared */ +int link(const char *oldname, const char *newname); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ +int linkat(int olddirfd, const char *oldname, int newdirfd, const char *newname, int flags); /* path_lock=1 */ +int mkdir(const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ +int mkdirat(int dirfd, const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ +int mkfifo(const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ +int mkfifoat(int dirfd, const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ +int mknod(const char *path, mode_t mode, dev_t dev); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ +int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ +int mkstemp(char *template); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ +int mkostemp(char *template, int oflags); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ +int mkstemps(char *template, int suffixlen); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ +int mkostemps(char *template, int suffixlen, int oflags); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ int rename(const char *oldpath, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ int renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ int rmdir(const char *path); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ -int symlink(const char *oldname, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW */ -int symlinkat(const char *oldname, int dirfd, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW */ +int symlink(const char *oldname, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ +int symlinkat(const char *oldname, int dirfd, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ int unlink(const char *path); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ int unlinkat(int dirfd, const char *path, int rflags); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ # primarily for use with chroot() @@ -74,4 +74,4 @@ void sync(void); /* async_skip= */ int sync_file_range(int fd, off64_t offset, off64_t nbytes, unsigned int flags); /* async_skip=0 */ int msync(void *addr, size_t length, int flags); /* async_skip=0 */ mode_t umask(mode_t mask); -int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); +int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); /* path_lock=1 */ From patchwork Wed Nov 11 04:20:31 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103627 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.2567.1794370864964304612 for ; Tue, 10 Nov 2026 20:21:05 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3ln2428068 for ; Tue, 10 Nov 2026 22:21:04 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 03/34] ports: check created files under the path lock before recording them Date: Tue, 10 Nov 2026 22:20:31 -0600 Message-Id: <1794370862-30908-4-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:15 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5263 MIME-Version: 1.0 From: Mark Hatle open() and friends with O_CREAT create a file and then send OP_CREAT for the path, so another client can rename onto or remove the name in between, like the other wrappers that create names. They can't hold the path lock across the real call, as opening a FIFO can block indefinitely, which could deadlock against a client waiting for the lock. Instead, take the lock after the file has been opened, and only send OP_CREAT if the path still names the file that was opened, using a new pseudo_client_path_matches_fd(). With the lock held no other client is part way through changing the path, so if it now names a different file, that client has already recorded the change and recording ours would be wrong. This covers openat (and so open, creat and their variants), openat2, fopen, freopen, fopen64 and freopen64. openat and openat2 now also take the recorded stat data from the descriptor rather than from an earlier lookup of the path. AI-Generated: Uses GitHub Copilot Signed-off-by: Mark Hatle --- ports/linux/guts/fopen64.c | 5 ++++- ports/linux/guts/freopen64.c | 5 ++++- ports/linux/guts/openat.c | 8 +++++++- ports/linux/openat2/guts/openat2.c | 8 +++++++- ports/unix/guts/fopen.c | 5 ++++- ports/unix/guts/freopen.c | 5 ++++- pseudo_client.c | 25 +++++++++++++++++++++++++ pseudo_client.h | 1 + 8 files changed, 56 insertions(+), 6 deletions(-) diff --git a/ports/linux/guts/fopen64.c b/ports/linux/guts/fopen64.c index e76da69..a6efd18 100644 --- a/ports/linux/guts/fopen64.c +++ b/ports/linux/guts/fopen64.c @@ -22,8 +22,11 @@ pseudo_debug(PDBGF_FILE, "fopen64 '%s': fd %d \n", path, fd, (void *) rc); if (real___fxstat64(_STAT_VER, fd, &buf) != -1) { if (!existed) { + int path_lock_fd = pseudo_client_path_lock(0); real_fchmod(fd, PSEUDO_FS_MODE(0666 & ~pseudo_umask, 0)); - pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf); + if (pseudo_client_path_matches_fd(path, fd, 0)) + pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf); + pseudo_client_path_unlock(path_lock_fd); } pseudo_client_op(OP_OPEN, pseudo_access_fopen(mode), fd, -1, path, &buf); } else { diff --git a/ports/linux/guts/freopen64.c b/ports/linux/guts/freopen64.c index 5fc9073..6057eda 100644 --- a/ports/linux/guts/freopen64.c +++ b/ports/linux/guts/freopen64.c @@ -21,8 +21,11 @@ pseudo_debug(PDBGF_FILE, "freopen64 '%s': fd %d\n", path, fd); if (real___fxstat64(_STAT_VER, fd, &buf) != -1) { if (!existed) { + int path_lock_fd = pseudo_client_path_lock(0); real_fchmod(fd, PSEUDO_FS_MODE(0666 & ~pseudo_umask, 0)); - pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf); + if (pseudo_client_path_matches_fd(path, fd, 0)) + pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf); + pseudo_client_path_unlock(path_lock_fd); } pseudo_client_op(OP_OPEN, pseudo_access_fopen(mode), fd, -1, path, &buf); } else { diff --git a/ports/linux/guts/openat.c b/ports/linux/guts/openat.c index 0a44063..4fd83dd 100644 --- a/ports/linux/guts/openat.c +++ b/ports/linux/guts/openat.c @@ -147,9 +147,15 @@ if (stat_rc != -1) { buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, mode); if (!existed) { + int path_lock_fd = pseudo_client_path_lock(0); real_fchmod(rc, PSEUDO_FS_MODE(mode, 0)); // file has no path, but has been created - pseudo_client_op(OP_CREAT, 0, -1, dirfd, path, &buf); + if (pseudo_client_path_matches_fd(path, rc, flags & O_NOFOLLOW) && + real___fxstat64(_STAT_VER, rc, &buf) != -1) { + buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, mode); + pseudo_client_op(OP_CREAT, 0, -1, dirfd, path, &buf); + } + pseudo_client_path_unlock(path_lock_fd); } pseudo_client_op(OP_OPEN, PSEUDO_ACCESS(flags), rc, dirfd, path, &buf); } else { diff --git a/ports/linux/openat2/guts/openat2.c b/ports/linux/openat2/guts/openat2.c index f1c523e..06fcec8 100644 --- a/ports/linux/openat2/guts/openat2.c +++ b/ports/linux/openat2/guts/openat2.c @@ -182,9 +182,15 @@ if (stat_rc != -1) { buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, my_how.mode); if (!existed) { + int path_lock_fd = pseudo_client_path_lock(0); real_fchmod(rc, PSEUDO_FS_MODE(my_how.mode, 0)); // file has no path, but has been created - pseudo_client_op(OP_CREAT, 0, -1, dirfd, pseudo_path, &buf); + if (pseudo_client_path_matches_fd(pseudo_path, rc, my_how.flags & O_NOFOLLOW) && + real___fxstat64(_STAT_VER, rc, &buf) != -1) { + buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, my_how.mode); + pseudo_client_op(OP_CREAT, 0, -1, dirfd, pseudo_path, &buf); + } + pseudo_client_path_unlock(path_lock_fd); } pseudo_client_op(OP_OPEN, PSEUDO_ACCESS(my_how.flags), rc, dirfd, pseudo_path, &buf); } else { diff --git a/ports/unix/guts/fopen.c b/ports/unix/guts/fopen.c index 26a6abc..78388f7 100644 --- a/ports/unix/guts/fopen.c +++ b/ports/unix/guts/fopen.c @@ -21,8 +21,11 @@ pseudo_debug(PDBGF_OP, "fopen '%s': fd %d \n", path, fd, (void *) rc); if (base_fstat(fd, &buf) != -1) { if (!existed) { + int path_lock_fd = pseudo_client_path_lock(0); real_fchmod(fd, PSEUDO_FS_MODE(0666 & ~pseudo_umask, 0)); - pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf); + if (pseudo_client_path_matches_fd(path, fd, 0)) + pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf); + pseudo_client_path_unlock(path_lock_fd); } pseudo_client_op(OP_OPEN, pseudo_access_fopen(mode), fd, -1, path, &buf); } else { diff --git a/ports/unix/guts/freopen.c b/ports/unix/guts/freopen.c index 703b6aa..5d63c9f 100644 --- a/ports/unix/guts/freopen.c +++ b/ports/unix/guts/freopen.c @@ -21,8 +21,11 @@ pseudo_debug(PDBGF_OP, "freopen '%s': fd %d\n", path, fd); if (base_fstat(fd, &buf) != -1) { if (!existed) { + int path_lock_fd = pseudo_client_path_lock(0); real_fchmod(fd, PSEUDO_FS_MODE(0666 & ~pseudo_umask, 0)); - pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf); + if (pseudo_client_path_matches_fd(path, fd, 0)) + pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf); + pseudo_client_path_unlock(path_lock_fd); } pseudo_client_op(OP_OPEN, pseudo_access_fopen(mode), fd, -1, path, &buf); } else { diff --git a/pseudo_client.c b/pseudo_client.c index ffd3450..a3d461e 100644 --- a/pseudo_client.c +++ b/pseudo_client.c @@ -1225,6 +1225,31 @@ pseudo_client_path_unlock(int fd) { errno = save_errno; } +/* Operations that can block, such as open() on a FIFO, can't hold the path + * lock across the real call. Instead they take the lock afterwards and + * check that the path still names the file they opened before recording + * it. With the lock held, no other client is part way through changing the + * path, so if it now names a different file, that client has already + * recorded the change, and recording ours would be wrong. + */ +int +pseudo_client_path_matches_fd(const char *path, int fd, int nofollow) { + PSEUDO_STATBUF pbuf, fbuf; + int save_errno = errno; + int rc; + + if (!path || fd < 0 || !pseudo_real_fstat || !pseudo_real_lstat || !pseudo_real_stat) + return 1; + rc = pseudo_real_fstat(fd, &fbuf) == 0 && + (nofollow ? pseudo_real_lstat(path, &pbuf) : pseudo_real_stat(path, &pbuf)) == 0 && + pbuf.st_dev == fbuf.st_dev && pbuf.st_ino == fbuf.st_ino; + if (!rc) + pseudo_debug(PDBGF_FILE, "'%s' no longer names fd %d, not recording it.\n", + path, fd); + errno = save_errno; + return rc; +} + static int client_connect(void) { /* we have a server pid, is it responsive? */ diff --git a/pseudo_client.h b/pseudo_client.h index 3958f6d..716c441 100644 --- a/pseudo_client.h +++ b/pseudo_client.h @@ -29,6 +29,7 @@ extern int pseudo_client_shutdown(int); extern int pseudo_fd(int fd, int how); extern int pseudo_client_path_lock(int shared); extern void pseudo_client_path_unlock(int fd); +extern int pseudo_client_path_matches_fd(const char *path, int fd, int nofollow); #define MOVE_FD 0 #define COPY_FD 1 #define PSEUDO_MIN_FD 20 From patchwork Wed Nov 11 04:20:32 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103628 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.2481.1794370865121181845 for ; Tue, 10 Nov 2026 20:21:05 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3lo2428068 for ; Tue, 10 Nov 2026 22:21:04 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 04/34] test-parallel-ops: Add a parallel create/rename/link/chmod test Date: Tue, 10 Nov 2026 22:20:32 -0600 Message-Id: <1794370862-30908-5-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:15 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5264 MIME-Version: 1.0 From: Mark Hatle Four workers repeatedly create, rename onto, chmod, chown, symlink, hard link and remove the same names in one directory. The test fails if any of the operations fail, or if the server logged an inode or path mismatch while it ran. Without the path lock in the create, metadata and open wrappers, the server reports mismatches and occasionally aborts a client. AI-Generated: Uses GitHub Copilot Signed-off-by: Mark Hatle --- test/test-parallel-ops.sh | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100755 test/test-parallel-ops.sh diff --git a/test/test-parallel-ops.sh b/test/test-parallel-ops.sh new file mode 100755 index 0000000..2fccd30 --- /dev/null +++ b/test/test-parallel-ops.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# +# SPDX-License-Identifier: LGPL-2.1-only +# +# Several clients create, rename, link, chmod and remove the same names in +# one directory at once. pseudo must not let one client's operation land in +# the middle of another client's change to the same name, or the server +# finds the database out of step with the filesystem. It then logs an +# "inode mismatch" or "path mismatch" error, and may abort a client. +# +# The temporary tree has to live below $PWD so that pseudo tracks it. + +tmpdir="$(mktemp -d -p "$PWD" parallel_ops_XXXXXX)" +trap "rm -rf '$tmpdir'" EXIT TERM INT + +log="${PSEUDO_LOCALSTATEDIR:-$PWD/var/pseudo}/pseudo.log" +mismatches() { + grep -c "mismatch" "$log" 2>/dev/null || true +} +before=$(mismatches) + +cd "$tmpdir" || exit 1 +: > target + +worker() { + for i in $(seq 50); do + echo "$1 $i" > "tmp.$1" || echo "FAILED: create tmp.$1" >> failed + mv -f "tmp.$1" target || echo "FAILED: mv tmp.$1 target" >> failed + chmod 640 target 2>/dev/null + chown 0:0 target 2>/dev/null + ln -sf target lnk || echo "FAILED: ln -sf target lnk" >> failed + mkdir dir 2>/dev/null; rmdir dir 2>/dev/null + mkfifo "f.$1" && mv -f "f.$1" fifo || echo "FAILED: mkfifo/mv f.$1" >> failed + ln -f target "hard.$1" 2>/dev/null && rm -f "hard.$1" + done +} + +for w in 1 2 3 4; do + worker $w & +done +wait + +rc=0 +if [ -e failed ]; then + sort failed | uniq -c + rc=1 +fi +after=$(mismatches) +if [ "${after:-0}" -ne "${before:-0}" ]; then + echo "FAILED: $((after - before)) mismatch error(s) in $log" + rc=1 +fi +exit $rc From patchwork Wed Nov 11 04:20:33 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103634 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.2568.1794370865285605254 for ; Tue, 10 Nov 2026 20:21:05 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3lp2428068 for ; Tue, 10 Nov 2026 22:21:04 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 05/34] ports: Remove PSEUDO_NO_REAL_AT_FUNCTIONS Date: Tue, 10 Nov 2026 22:20:33 -0600 Message-Id: <1794370862-30908-6-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:15 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5265 MIME-Version: 1.0 From: Mark Hatle PSEUDO_NO_REAL_AT_FUNCTIONS was defined, along with substitute AT_FDCWD and AT_SYMLINK_* values, when did not provide AT_FDCWD. The guts then used non-*at() fallbacks, or failed with ENOSYS when given a directory descriptor. Linux always provides the *at() functions, and pseudo.h includes , so the macro is never defined and none of the fallbacks are ever built. Remove them, keeping the *at() code, and make a missing AT_FDCWD a build error instead. AI-Generated: Uses GitHub Copilot Signed-off-by: Mark Hatle --- ports/linux/guts/__fxstatat.c | 12 ------------ ports/linux/guts/__fxstatat64.c | 14 -------------- ports/linux/guts/__xmknodat.c | 17 ----------------- ports/linux/guts/openat.c | 35 ----------------------------------- ports/linux/openat2/guts/openat2.c | 28 ++-------------------------- ports/unix/guts/faccessat2.c | 12 ------------ ports/unix/guts/fchmodat.c | 18 ------------------ ports/unix/guts/fchownat.c | 12 ------------ ports/unix/guts/linkat.c | 11 ----------- ports/unix/guts/mkdirat.c | 17 ----------------- ports/unix/guts/mkfifoat.c | 22 ---------------------- ports/unix/guts/mknodat.c | 17 ----------------- ports/unix/guts/readlinkat.c | 8 -------- ports/unix/guts/renameat.c | 12 ------------ ports/unix/guts/symlinkat.c | 12 ------------ ports/unix/guts/unlinkat.c | 25 ------------------------- pseudo.h | 19 ++++++------------- 17 files changed, 8 insertions(+), 283 deletions(-) diff --git a/ports/linux/guts/__fxstatat.c b/ports/linux/guts/__fxstatat.c index 23dfa73..99198f7 100644 --- a/ports/linux/guts/__fxstatat.c +++ b/ports/linux/guts/__fxstatat.c @@ -11,19 +11,7 @@ struct stat64 buf64; /* populate buffer with complete data */ -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (dirfd != AT_FDCWD) { - errno = ENOSYS; - return -1; - } - if (flags & AT_SYMLINK_NOFOLLOW) { - rc = real___lxstat(ver, path, buf); - } else { - rc = real___xstat(ver, path, buf); - } -#else real___fxstatat(ver, dirfd, path, buf, flags); -#endif /* obtain fake data */ rc = wrap___fxstatat64(ver, dirfd, path, &buf64, flags); /* overwrite */ diff --git a/ports/linux/guts/__fxstatat64.c b/ports/linux/guts/__fxstatat64.c index ecf2952..6f58aa2 100644 --- a/ports/linux/guts/__fxstatat64.c +++ b/ports/linux/guts/__fxstatat64.c @@ -11,27 +11,13 @@ pseudo_msg_t *msg; int save_errno; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (dirfd != AT_FDCWD) { - errno = ENOSYS; - return -1; - } -#endif if (flags & AT_SYMLINK_NOFOLLOW) { -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - rc = real___lxstat64(ver, path, buf); -#else rc = real___fxstatat64(ver, dirfd, path, buf, flags); -#endif if (rc == -1) { return rc; } } else { -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - rc = real___xstat64(ver, path, buf); -#else rc = real___fxstatat64(ver, dirfd, path, buf, flags); -#endif if (rc == -1) { return rc; } diff --git a/ports/linux/guts/__xmknodat.c b/ports/linux/guts/__xmknodat.c index 8c34427..84d535b 100644 --- a/ports/linux/guts/__xmknodat.c +++ b/ports/linux/guts/__xmknodat.c @@ -23,15 +23,7 @@ /* we don't use underlying call, so _ver is irrelevant to us */ (void) ver; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (dirfd != AT_FDCWD) { - errno = ENOSYS; - return -1; - } - rc = real___xstat64(_STAT_VER, path, &buf); -#else rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, AT_SYMLINK_NOFOLLOW); -#endif if (rc != -1) { /* if we can stat the file, you can't mknod it */ errno = EEXIST; @@ -41,13 +33,8 @@ errno = EINVAL; return -1; } -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - rc = real_open(path, O_CREAT | O_WRONLY | O_EXCL, - PSEUDO_FS_MODE(mode, 0)); -#else rc = real_openat(dirfd, path, O_CREAT | O_WRONLY | O_EXCL, PSEUDO_FS_MODE(mode, 0)); -#endif if (rc == -1) { return -1; } @@ -73,11 +60,7 @@ } if (rc == -1) { int save_errno = errno; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - real_unlink(path); -#else real_unlinkat(dirfd, path, AT_SYMLINK_NOFOLLOW); -#endif errno = save_errno; } diff --git a/ports/linux/guts/openat.c b/ports/linux/guts/openat.c index 4fd83dd..f5469e6 100644 --- a/ports/linux/guts/openat.c +++ b/ports/linux/guts/openat.c @@ -17,13 +17,6 @@ /* mask out mode bits appropriately */ mode = mode & ~pseudo_umask; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (dirfd != AT_FDCWD) { - errno = ENOSYS; - return -1; - } -#endif - #ifdef PSEUDO_FORCE_ASYNC /* Yes, I'm aware that every Linux system I've seen has * DSYNC and RSYNC being the same value as SYNC. @@ -54,15 +47,7 @@ /* note "else" in #ifdef O_TMPFILE above */ if (flags & O_CREAT) { save_errno = errno; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (flags & O_NOFOLLOW) { - rc = real___lxstat64(_STAT_VER, path, &buf); - } else { - rc = real___xstat64(_STAT_VER, path, &buf); - } -#else rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, (flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); -#endif existed = (rc != -1); if (!existed) pseudo_debug(PDBGF_FILE, "openat_creat: %s -> 0%o\n", path, mode); @@ -75,15 +60,7 @@ */ if (!(flags & O_NONBLOCK) && ((flags & (O_WRONLY | O_RDONLY | O_RDWR)) != O_RDWR)) { save_errno = errno; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (flags & O_NOFOLLOW) { - rc = real___lxstat64(_STAT_VER, path, &buf); - } else { - rc = real___xstat64(_STAT_VER, path, &buf); - } -#else rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, (flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); -#endif if (rc != -1 && S_ISFIFO(buf.st_mode)) { overly_magic_nonblocking = 1; } @@ -99,11 +76,7 @@ * is if a file is going to be created, in which case it's * not a directory. */ -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - rc = real_open(path, flags, PSEUDO_FS_MODE(mode, 0)); -#else rc = real_openat(dirfd, path, flags, PSEUDO_FS_MODE(mode, 0)); -#endif if (overly_magic_nonblocking) { save_errno = errno; sigprocmask(SIG_SETMASK, &local_saved_sigmask, NULL); @@ -132,15 +105,7 @@ return rc; } #endif -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (flags & O_NOFOLLOW) { - stat_rc = real___lxstat64(_STAT_VER, path, &buf); - } else { - stat_rc = real___xstat64(_STAT_VER, path, &buf); - } -#else stat_rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, (flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); -#endif pseudo_debug(PDBGF_FILE, "openat(path %s), flags %o, stat rc %d, stat mode %o\n", path, flags, stat_rc, buf.st_mode); diff --git a/ports/linux/openat2/guts/openat2.c b/ports/linux/openat2/guts/openat2.c index 06fcec8..5171647 100644 --- a/ports/linux/openat2/guts/openat2.c +++ b/ports/linux/openat2/guts/openat2.c @@ -44,7 +44,7 @@ /* mask out mode bits appropriately */ my_how.mode = my_how.mode & ~pseudo_umask; -#if defined(PSEUDO_NO_REAL_AT_FUNCTIONS) || ! defined(SYS_openat2) +#ifndef SYS_openat2 if (dirfd != AT_FDCWD) { errno = ENOSYS; return -1; @@ -81,15 +81,7 @@ /* note "else" in #ifdef O_TMPFILE above */ if (my_how.flags & O_CREAT) { save_errno = errno; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (my_how.flags & O_NOFOLLOW) { - rc = real___lxstat64(_STAT_VER, pseudo_path, &buf); - } else { - rc = real___xstat64(_STAT_VER, pseudo_path, &buf); - } -#else rc = real___fxstatat64(_STAT_VER, dirfd, pseudo_path, &buf, (my_how.flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); -#endif existed = (rc != -1); if (!existed) pseudo_debug(PDBGF_FILE, "openat2_creat: %s -> 0%lld\n", pseudo_path, my_how.mode); @@ -102,15 +94,7 @@ */ if (!(my_how.flags & O_NONBLOCK) && ((my_how.flags & (O_WRONLY | O_RDONLY | O_RDWR)) != O_RDWR)) { save_errno = errno; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (my_how.flags & O_NOFOLLOW) { - rc = real___lxstat64(_STAT_VER, pseudo_path, &buf); - } else { - rc = real___xstat64(_STAT_VER, pseudo_path, &buf); - } -#else rc = real___fxstatat64(_STAT_VER, dirfd, pseudo_path, &buf, (my_how.flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); -#endif if (rc != -1 && S_ISFIFO(buf.st_mode)) { overly_magic_nonblocking = 1; } @@ -126,7 +110,7 @@ * is if a file is going to be created, in which case it's * not a directory. */ -#if defined(PSEUDO_NO_REAL_AT_FUNCTIONS) || ! defined(SYS_openat2) +#ifndef SYS_openat2 pseudo_debug(PDBGF_SYSCALL, "openat2, calling open.\n"); rc = real_open(path, my_how.flags, PSEUDO_FS_MODE(my_how.mode, 0)); #else @@ -167,15 +151,7 @@ return rc; } #endif -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (my_how.flags & O_NOFOLLOW) { - stat_rc = real___lxstat64(_STAT_VER, pseudo_path, &buf); - } else { - stat_rc = real___xstat64(_STAT_VER, pseudo_path, &buf); - } -#else stat_rc = real___fxstatat64(_STAT_VER, dirfd, pseudo_path, &buf, (my_how.flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); -#endif pseudo_debug(PDBGF_FILE, "openat2(path %s), flags %lld, stat rc %d, stat mode %o\n", pseudo_path, my_how.flags, stat_rc, buf.st_mode); diff --git a/ports/unix/guts/faccessat2.c b/ports/unix/guts/faccessat2.c index 1283cc6..d4a2a42 100644 --- a/ports/unix/guts/faccessat2.c +++ b/ports/unix/guts/faccessat2.c @@ -10,19 +10,7 @@ */ PSEUDO_STATBUF buf; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (dirfd != AT_FDCWD) { - errno = ENOSYS; - return -1; - } - if (flags & AT_SYMLINK_NOFOLLOW) { - rc = base_lstat(path, &buf); - } else { - rc = base_stat(path, &buf); - } -#else rc = base_fstatat(dirfd, path, &buf, flags & AT_SYMLINK_NOFOLLOW); -#endif if (rc == -1) return rc; diff --git a/ports/unix/guts/fchmodat.c b/ports/unix/guts/fchmodat.c index 5a31151..12dc9e3 100644 --- a/ports/unix/guts/fchmodat.c +++ b/ports/unix/guts/fchmodat.c @@ -11,19 +11,7 @@ PSEUDO_STATBUF buf; int save_errno = errno; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (dirfd != AT_FDCWD) { - errno = ENOSYS; - return -1; - } - if (flags & AT_SYMLINK_NOFOLLOW) { - rc = base_lstat(path, &buf); - } else { - rc = base_stat(path, &buf); - } -#else rc = base_fstatat(dirfd, path, &buf, flags); -#endif if (rc == -1) { return rc; } @@ -50,13 +38,7 @@ #endif /* user bits added so "root" can always access files. */ -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - /* note: if path was a symlink, and AT_SYMLINK_NOFOLLOW was - * specified, we already bailed previously. */ - real_chmod(path, PSEUDO_FS_MODE(mode, S_ISDIR(buf.st_mode))); -#else rc = real_fchmodat(dirfd, path, PSEUDO_FS_MODE(mode, S_ISDIR(buf.st_mode)), flags); -#endif /* we otherwise ignore failures from underlying fchmod, because pseudo * may believe you are permitted to change modes that the filesystem * doesn't. Note that we also don't need to know whether the diff --git a/ports/unix/guts/fchownat.c b/ports/unix/guts/fchownat.c index 76d276a..3af774f 100644 --- a/ports/unix/guts/fchownat.c +++ b/ports/unix/guts/fchownat.c @@ -12,19 +12,7 @@ PSEUDO_STATBUF buf; int save_errno = errno; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (dirfd != AT_FDCWD) { - errno = ENOSYS; - return -1; - } - if (flags & AT_SYMLINK_NOFOLLOW) { - rc = base_lstat(path, &buf); - } else { - rc = base_stat(path, &buf); - } -#else rc = base_fstatat(dirfd, path, &buf, flags); -#endif if (rc == -1) { return rc; } diff --git a/ports/unix/guts/linkat.c b/ports/unix/guts/linkat.c index f0cedd4..e22d66a 100644 --- a/ports/unix/guts/linkat.c +++ b/ports/unix/guts/linkat.c @@ -27,12 +27,6 @@ rflags = (flags & AT_SYMLINK_FOLLOW) ? 0 : AT_SYMLINK_NOFOLLOW; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (olddirfd != AT_FDCWD || newdirfd != AT_FDCWD) { - errno = ENOSYS; - return -1; - } -#endif oldpath = oldname; if (oldpath && *oldpath && pseudo_chroot_len && !strncmp(oldpath, pseudo_chroot, pseudo_chroot_len) && oldpath[pseudo_chroot_len] == '/') { @@ -54,11 +48,6 @@ */ #ifdef O_TMPFILE if (!strncmp(oldpath, "/proc/self/fd/", 14) && (flags & AT_SYMLINK_FOLLOW)) { -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - /* only linkat() lets you do this horrible thing */ - errno = ENOSYS; - return -1; -#endif tmpfile_fd = pseudo_atoi_wrapper(oldpath + 14); // call actual link rc = real_linkat(AT_FDCWD, oldpath, AT_FDCWD, newpath, AT_SYMLINK_FOLLOW); diff --git a/ports/unix/guts/mkdirat.c b/ports/unix/guts/mkdirat.c index f66e99a..4db3826 100644 --- a/ports/unix/guts/mkdirat.c +++ b/ports/unix/guts/mkdirat.c @@ -10,34 +10,17 @@ */ /* mask out mode bits appropriately */ mode = mode & ~pseudo_umask; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (dirfd != AT_FDCWD) { - errno = ENOSYS; - return -1; - } - - rc = real_mkdir(path, PSEUDO_FS_MODE(mode, 1)); -#else rc = real_mkdirat(dirfd, path, PSEUDO_FS_MODE(mode, 1)); -#endif if (rc != -1) { PSEUDO_STATBUF buf; int stat_rc; int save_errno = errno; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - stat_rc = base_lstat(path, &buf); -#else stat_rc = base_fstatat(dirfd, path, &buf, AT_SYMLINK_NOFOLLOW); -#endif if (stat_rc != -1) { buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, mode); pseudo_client_op(OP_MKDIR, 0, -1, dirfd, path, &buf); -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - real_fchmod(path, PSEUDO_FS_MODE(mode, 1)); -#else real_fchmodat(dirfd, path, PSEUDO_FS_MODE(mode, 1), 0); -#endif } else { pseudo_debug(PDBGF_OP, "mkdir of %s succeeded, but stat failed: %s\n", path, strerror(errno)); diff --git a/ports/unix/guts/mkfifoat.c b/ports/unix/guts/mkfifoat.c index bc547eb..c068841 100644 --- a/ports/unix/guts/mkfifoat.c +++ b/ports/unix/guts/mkfifoat.c @@ -16,29 +16,12 @@ /* mask out mode bits appropriately */ mode = mode & ~pseudo_umask; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (dirfd != AT_FDCWD) { - errno = ENOSYS; - return -1; - } - rc = base_stat(path, &buf); -#else rc = base_fstatat(dirfd, path, &buf, AT_SYMLINK_NOFOLLOW); -#endif if (rc != -1) { /* if we can stat the file, you can't mkfifo it */ errno = EEXIST; return -1; } -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - rc = real_mkfifo(path, PSEUDO_FS_MODE(mode, 0)); - if (rc == -1) { - return -1; - } - save_errno = errno; - rc = base_stat(path, &buf); - real_chmod(path, PSEUDO_FS_MODE(mode, 0)); -#else rc = real_mkfifoat(dirfd, path, PSEUDO_FS_MODE(mode, 0)); if (rc == -1) { return -1; @@ -46,7 +29,6 @@ save_errno = errno; rc = base_fstatat(dirfd, path, &buf, AT_SYMLINK_NOFOLLOW); real_fchmodat(dirfd, path, PSEUDO_FS_MODE(mode, 0), 0); -#endif /* if the stat failed, we are going to give up and nuke * any file we may have created, and hope for the best. */ @@ -67,11 +49,7 @@ } if (rc == -1) { save_errno = errno; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - real_unlink(path); -#else real_unlinkat(dirfd, path, AT_SYMLINK_NOFOLLOW); -#endif errno = save_errno; } diff --git a/ports/unix/guts/mknodat.c b/ports/unix/guts/mknodat.c index 9e86c93..3e8b207 100644 --- a/ports/unix/guts/mknodat.c +++ b/ports/unix/guts/mknodat.c @@ -21,27 +21,14 @@ pseudo_debug(PDBGF_FILE, "mknodat creating '%s', mode 0%o\n", path ? path : "", (int) mode); -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (dirfd != AT_FDCWD) { - errno = ENOSYS; - return -1; - } - rc = base_stat(path, &buf); -#else rc = base_fstatat(dirfd, path, &buf, AT_SYMLINK_NOFOLLOW); -#endif if (rc != -1) { /* if we can stat the file, you can't mknod it */ errno = EEXIST; return -1; } -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - rc = real_open(path, O_CREAT | O_WRONLY | O_EXCL, - PSEUDO_FS_MODE(mode, 0)); -#else rc = real_openat(dirfd, path, O_CREAT | O_WRONLY | O_EXCL, PSEUDO_FS_MODE(mode, 0)); -#endif if (rc == -1) { return -1; } @@ -68,11 +55,7 @@ } if (rc == -1) { save_errno = errno; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - real_unlink(path); -#else real_unlinkat(dirfd, path, AT_SYMLINK_NOFOLLOW); -#endif errno = save_errno; } diff --git a/ports/unix/guts/readlinkat.c b/ports/unix/guts/readlinkat.c index 86832da..2d1bce0 100644 --- a/ports/unix/guts/readlinkat.c +++ b/ports/unix/guts/readlinkat.c @@ -8,15 +8,7 @@ * wrap_readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) { * ssize_t rc = -1; */ -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (dirfd != AT_FDCWD) { - errno = ENOSYS; - return -1; - } - rc = real_readlink(path, buf, bufsiz); -#else rc = real_readlinkat(dirfd, path, buf, bufsiz); -#endif if (rc > 0) { rc = pseudo_dechroot(buf, rc); diff --git a/ports/unix/guts/renameat.c b/ports/unix/guts/renameat.c index 69130c6..98391dd 100644 --- a/ports/unix/guts/renameat.c +++ b/ports/unix/guts/renameat.c @@ -20,13 +20,6 @@ olddirfd, oldpath ? oldpath : "", newdirfd, newpath ? newpath : ""); -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (olddirfd != AT_FDCWD || newdirfd != AT_FDCWD) { - errno = ENOSYS; - return -1; - } -#endif - if (!oldpath || !newpath) { errno = EFAULT; return -1; @@ -34,13 +27,8 @@ save_errno = errno; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - newrc = base_lstat(newpath, &newbuf); - oldrc = base_lstat(oldpath, &oldbuf); -#else oldrc = base_fstatat(olddirfd, oldpath, &oldbuf, AT_SYMLINK_NOFOLLOW); newrc = base_fstatat(newdirfd, newpath, &newbuf, AT_SYMLINK_NOFOLLOW); -#endif /* nothing to do for a "rename" of a link to itself */ if (newrc != -1 && oldrc != -1 && diff --git a/ports/unix/guts/symlinkat.c b/ports/unix/guts/symlinkat.c index 5673912..0efe4d1 100644 --- a/ports/unix/guts/symlinkat.c +++ b/ports/unix/guts/symlinkat.c @@ -17,25 +17,13 @@ snprintf(roldname, len, "%s%s", pseudo_chroot, oldname); } -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (dirfd != AT_FDCWD) { - errno = ENOSYS; - return -1; - } - rc = real_symlink(roldname ? roldname : oldname, newpath); -#else rc = real_symlinkat(roldname ? roldname : oldname, dirfd, newpath); -#endif if (rc == -1) { free(roldname); return rc; } -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - rc = base_lstat(newpath, &buf); -#else rc = base_fstatat(dirfd, newpath, &buf, AT_SYMLINK_NOFOLLOW); -#endif if (rc == -1) { int save_errno = errno; pseudo_warning("symlinkat: couldn't stat '%s' even though symlink creation succeeded (%s).\n", diff --git a/ports/unix/guts/unlinkat.c b/ports/unix/guts/unlinkat.c index 9889b2e..d0f795b 100644 --- a/ports/unix/guts/unlinkat.c +++ b/ports/unix/guts/unlinkat.c @@ -13,39 +13,14 @@ PSEUDO_STATBUF buf; int old_db_entry = 0; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - if (dirfd != AT_FDCWD) { - errno = ENOSYS; - return -1; - } - if (rflags) { - /* the only supported flag is AT_REMOVEDIR. We'd never call - * with that flag unless the real AT functions exist, so - * something must have gone horribly wrong.... - */ - pseudo_info("wrap_unlinkat called with flags (0x%x), path '%s'\n", - rflags, path ? path : ""); - errno = ENOSYS; - return -1; - } -#endif - -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - rc = base_lstat(path, &buf); -#else rc = base_fstatat(dirfd, path, &buf, AT_SYMLINK_NOFOLLOW); -#endif if (rc == -1) { return rc; } msg = pseudo_client_op(OP_MAY_UNLINK, 0, -1, dirfd, path, &buf); if (msg && msg->result == RESULT_SUCCEED) old_db_entry = 1; -#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS - rc = real_unlink(path); -#else rc = real_unlinkat(dirfd, path, rflags); -#endif if (old_db_entry) { if (rc == -1) { save_errno = errno; diff --git a/pseudo.h b/pseudo.h index 6d50b77..53bf13f 100644 --- a/pseudo.h +++ b/pseudo.h @@ -135,23 +135,16 @@ extern char *pseudo_version; #define PSEUDO_SEVERITY_DEFAULT "info,warn,error" -/* some systems might not have *at(). We like to define operations in - * terms of each other, and for instance, open(...) is the same as - * openat(AT_FDCWD, ...). If no AT_FDCWD is provided, any value that can't - * be a valid file descriptor will do. Using -2 because -1 could be - * mistaken for a failed syscall return. AT_SYMLINK_NOFOLLOW has to be - * non-zero; AT_SYMLINK_FOLLOW has to be non-zero and different. Finally, - * if this happened, we set our own flag we can use to indicate that dummy - * implementations of the _at functions are needed. +/* pseudo defines operations in terms of each other; for instance, + * open(...) is the same as openat(AT_FDCWD, ...). This requires the *at() + * functions and AT_FDCWD, AT_SYMLINK_NOFOLLOW and AT_SYMLINK_FOLLOW from + * . */ #ifndef AT_FDCWD -#define AT_FDCWD -2 -#define AT_SYMLINK_NOFOLLOW 1 -#define AT_SYMLINK_FOLLOW 2 -#define PSEUDO_NO_REAL_AT_FUNCTIONS +#error "pseudo requires *at() function support (AT_FDCWD, AT_SYMLINK_NOFOLLOW, AT_SYMLINK_FOLLOW)" #endif -/* Likewise, someone might not have O_LARGEFILE (the flag equivalent to +/* Someone might not have O_LARGEFILE (the flag equivalent to * using open64()). Since open64() is the same as O_LARGEFILE in flags, * we implement it that way... If the system has no O_LARGEFILE, we'll * just call open() with nothing special. From patchwork Wed Nov 11 04:20:34 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103635 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.2569.1794370865443395494 for ; Tue, 10 Nov 2026 20:21:05 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3lq2428068 for ; Tue, 10 Nov 2026 22:21:04 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 06/34] ports/unix: Remove guts overridden by the Linux port Date: Tue, 10 Nov 2026 22:20:34 -0600 Message-Id: <1794370862-30908-7-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:16 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5266 MIME-Version: 1.0 From: Mark Hatle lchown, lutimes, mknod, mknodat, tmpnam and utimes are wrapped by both ports/unix and ports/linux. makewrappers always uses the Linux versions, reporting "Info: from linux overriding unix", so the ports/unix guts and wrapfuncs.in entries are never used now that Linux is the only port. They were removed as a single commit, so if necessary they can be restored via a git revert if a future port requires them. AI-Generated: Uses GitHub Copilot Signed-off-by: Mark Hatle --- README.security | 2 +- ports/unix/guts/lchown.c | 15 ----------- ports/unix/guts/lutimes.c | 16 ------------ ports/unix/guts/mknod.c | 15 ----------- ports/unix/guts/mknodat.c | 64 ----------------------------------------------- ports/unix/guts/tmpnam.c | 20 --------------- ports/unix/guts/utimes.c | 15 ----------- ports/unix/wrapfuncs.in | 6 ----- 8 files changed, 1 insertion(+), 152 deletions(-) delete mode 100644 ports/unix/guts/lchown.c delete mode 100644 ports/unix/guts/lutimes.c delete mode 100644 ports/unix/guts/mknod.c delete mode 100644 ports/unix/guts/mknodat.c delete mode 100644 ports/unix/guts/tmpnam.c delete mode 100644 ports/unix/guts/utimes.c diff --git a/README.security b/README.security index f4378c3..8817450 100644 --- a/README.security +++ b/README.security @@ -92,7 +92,7 @@ that protection in several ways: * Several wrappers check a path and then act on it by path in a second step. For example, fchmodat (ports/unix/guts/fchmodat.c) stats the path and later calls the real chmod on the same path, and mknodat - (ports/unix/guts/mknodat.c) removes the path again by name if the + (ports/linux/guts/__xmknodat.c) removes the path again by name if the database update fails. If anything else can change the tree while pseudo is working on it, an diff --git a/ports/unix/guts/lchown.c b/ports/unix/guts/lchown.c deleted file mode 100644 index 3a14e2f..0000000 --- a/ports/unix/guts/lchown.c +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2008,2011 Wind River Systems; see - * guts/COPYRIGHT for information. - * - * SPDX-License-Identifier: LGPL-2.1-only - * - * int lchown(const char *path, uid_t owner, gid_t group) - * int rc = -1; - */ - - rc = wrap_chown(path, owner, group); - -/* return rc; - * } - */ diff --git a/ports/unix/guts/lutimes.c b/ports/unix/guts/lutimes.c deleted file mode 100644 index 07cab66..0000000 --- a/ports/unix/guts/lutimes.c +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2010 Wind River Systems; see - * guts/COPYRIGHT for information. - * - * SPDX-License-Identifier: LGPL-2.1-only - * - * static int - * wrap_lutimes(const char *path, const struct timeval *tv) { - * int rc = -1; - */ - - rc = real_lutimes(path, tv); - -/* return rc; - * } - */ diff --git a/ports/unix/guts/mknod.c b/ports/unix/guts/mknod.c deleted file mode 100644 index aff3a0b..0000000 --- a/ports/unix/guts/mknod.c +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2011,2014 Wind River Systems; see - * guts/COPYRIGHT for information. - * - * SPDX-License-Identifier: LGPL-2.1-only - * - * int mknod(const char *path, mode_t mode, dev_t dev) - * int rc = -1; - */ - - rc = wrap_mknodat(AT_FDCWD, path, mode, dev); - -/* return rc; - * } - */ diff --git a/ports/unix/guts/mknodat.c b/ports/unix/guts/mknodat.c deleted file mode 100644 index 3e8b207..0000000 --- a/ports/unix/guts/mknodat.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2011 Wind River Systems; see - * guts/COPYRIGHT for information. - * - * SPDX-License-Identifier: LGPL-2.1-only - * - * int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev) - * int rc = -1; - */ - - pseudo_msg_t *msg; - PSEUDO_STATBUF buf; - int save_errno = errno; - - /* mask out mode bits appropriately */ - mode = mode & ~pseudo_umask; - /* if you don't specify a type, assume regular file */ - if (!(mode & S_IFMT)) { - mode |= S_IFREG; - } - pseudo_debug(PDBGF_FILE, "mknodat creating '%s', mode 0%o\n", - path ? path : "", (int) mode); - - rc = base_fstatat(dirfd, path, &buf, AT_SYMLINK_NOFOLLOW); - if (rc != -1) { - /* if we can stat the file, you can't mknod it */ - errno = EEXIST; - return -1; - } - rc = real_openat(dirfd, path, O_CREAT | O_WRONLY | O_EXCL, - PSEUDO_FS_MODE(mode, 0)); - if (rc == -1) { - return -1; - } - real_fchmod(rc, PSEUDO_FS_MODE(mode, 0)); - base_fstat(rc, &buf); - /* mknod does not really open the file. We don't have - * to use wrap_close because we've never exposed this file - * descriptor to the client code. - */ - real_close(rc); - - /* mask in the mode type bits again */ - buf.st_mode = (PSEUDO_DB_MODE(buf.st_mode, mode) & 07777) | - (mode & ~07777); - buf.st_rdev = dev; - msg = pseudo_client_op(OP_MKNOD, 0, -1, dirfd, path, &buf); - if (msg && msg->result != RESULT_SUCCEED) { - errno = EPERM; - rc = -1; - } else { - /* just pretend we worked */ - errno = save_errno; - rc = 0; - } - if (rc == -1) { - save_errno = errno; - real_unlinkat(dirfd, path, AT_SYMLINK_NOFOLLOW); - errno = save_errno; - } - -/* return rc; - * } - */ diff --git a/ports/unix/guts/tmpnam.c b/ports/unix/guts/tmpnam.c deleted file mode 100644 index 60b66e0..0000000 --- a/ports/unix/guts/tmpnam.c +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2010 Wind River Systems; see - * guts/COPYRIGHT for information. - * - * SPDX-License-Identifier: LGPL-2.1-only - * - * static char * - * wrap_tmpnam(char *s) { - * char * rc = NULL; - */ - - /* let gcc know we're ignoring this */ - (void) s; - pseudo_warning("tmpnam() is so ludicrously insecure as to defy implementation."); - errno = ENOMEM; - rc = NULL; - -/* return rc; - * } - */ diff --git a/ports/unix/guts/utimes.c b/ports/unix/guts/utimes.c deleted file mode 100644 index ec49f02..0000000 --- a/ports/unix/guts/utimes.c +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2010 Wind River Systems; see - * guts/COPYRIGHT for information. - * - * SPDX-License-Identifier: LGPL-2.1-only - * - * static int - * wrap_utimes(const char *path, const struct timeval *times) { - * int rc = -1; - */ - rc = real_utimes(path, times); - -/* return rc; - * } - */ diff --git a/ports/unix/wrapfuncs.in b/ports/unix/wrapfuncs.in index 5ffee83..95fa89c 100644 --- a/ports/unix/wrapfuncs.in +++ b/ports/unix/wrapfuncs.in @@ -4,7 +4,6 @@ char *getwd(char *buf); int close(int fd); /* noignore_path=1 */ int fchmod(int fd, mode_t mode); int fchown(int fd, uid_t owner, gid_t group); -int lchown(const char *path, uid_t owner, gid_t group); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=shared */ int dup2(int oldfd, int newfd); /* noignore_path=1 */ int dup(int fd); /* noignore_path=1 */ int chdir(const char *path); /* noignore_path=1 */ @@ -18,7 +17,6 @@ FTSENT *fts_children(FTS *ftsp, int options); /* noignore_path=1, path_lock=shar int ftw(const char *path, int (*fn)(const char *, const struct stat *, int), int nopenfd); int nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int nopenfd, int flag); int glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob); -int lutimes(const char *path, const struct timeval *tv); /* flags=AT_SYMLINK_NOFOLLOW */ char *mkdtemp(char *template); /* path_lock=1 */ char *mktemp(char *template); long pathconf(const char *path, int name); @@ -27,10 +25,8 @@ int remove(const char *path); /* flags=AT_SYMLINK_NOFOLLOW */ DIR *opendir(const char *path); /* noignore_path=1 */ int closedir(DIR *dirp); /* noignore_path=1 */ char *tempnam(const char *template, const char *pfx); -char *tmpnam(char *s); int truncate(const char *path, off_t length); int utime(const char *path, const struct utimbuf *buf); -int utimes(const char *path, const struct timeval *times); # needed because libc stdio does horrible things with inline asm syscalls FILE *fopen(const char *path, const char *mode); /* noignore_path=1 */ int fclose(FILE *fp); /* noignore_path=1 */ @@ -47,8 +43,6 @@ int mkdir(const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW, path_loc int mkdirat(int dirfd, const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ int mkfifo(const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ int mkfifoat(int dirfd, const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ -int mknod(const char *path, mode_t mode, dev_t dev); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ -int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ int mkstemp(char *template); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ int mkostemp(char *template, int oflags); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ int mkstemps(char *template, int suffixlen); /* flags=AT_SYMLINK_NOFOLLOW, path_lock=1 */ From patchwork Wed Nov 11 04:20:35 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103636 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.2570.1794370865575399059 for ; Tue, 10 Nov 2026 20:21:05 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3lr2428068 for ; Tue, 10 Nov 2026 22:21:05 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 07/34] ports: Let the port define how pseudo stats files Date: Tue, 10 Nov 2026 22:20:35 -0600 Message-Id: <1794370862-30908-8-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:16 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5267 MIME-Version: 1.0 From: Mark Hatle stat64(), lstat64() and fstat64() are not POSIX. They come from the Large File Summit transitional interface, which glibc provides but the BSDs and macOS don't, and musl is phasing out. The __xstat64() family called with _STAT_VER is glibc internal, and only kept for binary compatibility since glibc 2.33. pseudo_client.h chose between two sets of base_*stat() definitions with PSEUDO_STATBUF_64, which the Linux port always set, while several guts called real___xstat64(_STAT_VER, ...) and friends directly, and shared code used struct stat64. Move the base_stat(), base_lstat(), base_fstat() and base_fstatat() definitions into ports/linux/portdefs.h, next to PSEUDO_STATBUF, and make pseudo_client.h fail with #error if a port doesn't define them. Use the base_*() macros instead of the direct calls in the guts, except in the __fxstat64 and __fxstatat64 wrappers, which pass through the caller's version. Use PSEUDO_STATBUF instead of struct stat64 in shared code and for local buffers in the guts. real_stat64() and friends are pseudo_stat64() and friends, which call real___xstat64(_STAT_VER, ...), so the behaviour is unchanged. AI-Generated: Uses GitHub Copilot Signed-off-by: Mark Hatle --- ports/linux/guts/__xmknodat.c | 6 +++--- ports/linux/guts/fopen64.c | 6 +++--- ports/linux/guts/freopen64.c | 6 +++--- ports/linux/guts/mkostemp64.c | 4 ++-- ports/linux/guts/open_tree.c | 4 ++-- ports/linux/guts/open_tree_attr.c | 4 ++-- ports/linux/guts/openat.c | 10 +++++----- ports/linux/openat2/guts/openat2.c | 10 +++++----- ports/linux/portdefs.h | 10 +++++++++- pseudo.h | 6 ++++-- pseudo_client.c | 6 +++--- pseudo_client.h | 13 +++---------- pseudo_util.c | 4 ++-- 13 files changed, 46 insertions(+), 43 deletions(-) diff --git a/ports/linux/guts/__xmknodat.c b/ports/linux/guts/__xmknodat.c index 84d535b..21ad641 100644 --- a/ports/linux/guts/__xmknodat.c +++ b/ports/linux/guts/__xmknodat.c @@ -9,7 +9,7 @@ * int rc = -1; */ pseudo_msg_t *msg; - struct stat64 buf; + PSEUDO_STATBUF buf; /* mask out mode bits appropriately */ mode = mode & ~pseudo_umask; @@ -23,7 +23,7 @@ /* we don't use underlying call, so _ver is irrelevant to us */ (void) ver; - rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, AT_SYMLINK_NOFOLLOW); + rc = base_fstatat(dirfd, path, &buf, AT_SYMLINK_NOFOLLOW); if (rc != -1) { /* if we can stat the file, you can't mknod it */ errno = EEXIST; @@ -39,7 +39,7 @@ return -1; } real_fchmod(rc, PSEUDO_FS_MODE(mode, 0)); - real___fxstat64(_STAT_VER, rc, &buf); + base_fstat(rc, &buf); /* mknod does not really open the file. We don't have * to use wrap_close because we've never exposed this file * descriptor to the client code. diff --git a/ports/linux/guts/fopen64.c b/ports/linux/guts/fopen64.c index a6efd18..6355334 100644 --- a/ports/linux/guts/fopen64.c +++ b/ports/linux/guts/fopen64.c @@ -8,10 +8,10 @@ * wrap_fopen64(const char *path, const char *mode) { * FILE * rc = 0; */ - struct stat64 buf; + PSEUDO_STATBUF buf; int save_errno; - int existed = (real___xstat64(_STAT_VER, path, &buf) != -1); + int existed = (base_stat(path, &buf) != -1); rc = real_fopen64(path, mode); save_errno = errno; @@ -20,7 +20,7 @@ int fd = fileno(rc); pseudo_debug(PDBGF_FILE, "fopen64 '%s': fd %d \n", path, fd, (void *) rc); - if (real___fxstat64(_STAT_VER, fd, &buf) != -1) { + if (base_fstat(fd, &buf) != -1) { if (!existed) { int path_lock_fd = pseudo_client_path_lock(0); real_fchmod(fd, PSEUDO_FS_MODE(0666 & ~pseudo_umask, 0)); diff --git a/ports/linux/guts/freopen64.c b/ports/linux/guts/freopen64.c index 6057eda..99ca9cf 100644 --- a/ports/linux/guts/freopen64.c +++ b/ports/linux/guts/freopen64.c @@ -8,9 +8,9 @@ * wrap_freopen64(const char *path, const char *mode, FILE *stream) { * FILE * rc = NULL; */ - struct stat64 buf; + PSEUDO_STATBUF buf; int save_errno; - int existed = (real___xstat64(_STAT_VER, path, &buf) != -1); + int existed = (base_stat(path, &buf) != -1); rc = real_freopen64(path, mode, stream); save_errno = errno; @@ -19,7 +19,7 @@ int fd = fileno(rc); pseudo_debug(PDBGF_FILE, "freopen64 '%s': fd %d\n", path, fd); - if (real___fxstat64(_STAT_VER, fd, &buf) != -1) { + if (base_fstat(fd, &buf) != -1) { if (!existed) { int path_lock_fd = pseudo_client_path_lock(0); real_fchmod(fd, PSEUDO_FS_MODE(0666 & ~pseudo_umask, 0)); diff --git a/ports/linux/guts/mkostemp64.c b/ports/linux/guts/mkostemp64.c index 502211b..23c30c4 100644 --- a/ports/linux/guts/mkostemp64.c +++ b/ports/linux/guts/mkostemp64.c @@ -8,7 +8,7 @@ * wrap_mkstemp64(char *template, int oflags) { * int rc = -1; */ - struct stat64 buf; + PSEUDO_STATBUF buf; int save_errno; size_t len; char *tmp_template; @@ -35,7 +35,7 @@ if (rc != -1) { save_errno = errno; - if (real___fxstat64(_STAT_VER, rc, &buf) != -1) { + if (base_fstat(rc, &buf) != -1) { real_fchmod(rc, PSEUDO_FS_MODE(0600, 0)); pseudo_client_op(OP_CREAT, 0, -1, -1, tmp_template, &buf); pseudo_client_op(OP_OPEN, PSA_READ | PSA_WRITE, rc, -1, tmp_template, &buf); diff --git a/ports/linux/guts/open_tree.c b/ports/linux/guts/open_tree.c index f2e39ad..01d3dfc 100644 --- a/ports/linux/guts/open_tree.c +++ b/ports/linux/guts/open_tree.c @@ -8,7 +8,7 @@ * int rc = -1; */ - struct stat64 buf; + PSEUDO_STATBUF buf; int save_errno; char *pseudo_path; @@ -46,7 +46,7 @@ save_errno = errno; int stat_rc; - stat_rc = real___fxstatat64(_STAT_VER, dirfd, pseudo_path, &buf, (flags & AT_SYMLINK_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); + stat_rc = base_fstatat(dirfd, pseudo_path, &buf, (flags & AT_SYMLINK_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); pseudo_debug(PDBGF_FILE, "open_tree(path %s), flags %o, stat rc %d, stat mode %o\n", pseudo_path, flags, stat_rc, buf.st_mode); diff --git a/ports/linux/guts/open_tree_attr.c b/ports/linux/guts/open_tree_attr.c index 8e00c8c..96f32bb 100644 --- a/ports/linux/guts/open_tree_attr.c +++ b/ports/linux/guts/open_tree_attr.c @@ -10,7 +10,7 @@ /* Mostly based on open_tree wrapper */ - struct stat64 buf; + PSEUDO_STATBUF buf; int save_errno; char *pseudo_path; @@ -44,7 +44,7 @@ save_errno = errno; int stat_rc; - stat_rc = real___fxstatat64(_STAT_VER, dirfd, pseudo_path, &buf, (flags & AT_SYMLINK_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); + stat_rc = base_fstatat(dirfd, pseudo_path, &buf, (flags & AT_SYMLINK_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); pseudo_debug(PDBGF_FILE, "open_tree_attr(path %s), flags %o, stat rc %d, stat mode %o\n", pseudo_path, flags, stat_rc, buf.st_mode); diff --git a/ports/linux/guts/openat.c b/ports/linux/guts/openat.c index f5469e6..9d32283 100644 --- a/ports/linux/guts/openat.c +++ b/ports/linux/guts/openat.c @@ -8,7 +8,7 @@ * wrap_openat(int dirfd, const char *path, int flags, ...mode_t mode) { * int rc = -1; */ - struct stat64 buf; + PSEUDO_STATBUF buf; int overly_magic_nonblocking = 0; int existed = 1; int save_errno; @@ -47,7 +47,7 @@ /* note "else" in #ifdef O_TMPFILE above */ if (flags & O_CREAT) { save_errno = errno; - rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, (flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); + rc = base_fstatat(dirfd, path, &buf, (flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); existed = (rc != -1); if (!existed) pseudo_debug(PDBGF_FILE, "openat_creat: %s -> 0%o\n", path, mode); @@ -60,7 +60,7 @@ */ if (!(flags & O_NONBLOCK) && ((flags & (O_WRONLY | O_RDONLY | O_RDWR)) != O_RDWR)) { save_errno = errno; - rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, (flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); + rc = base_fstatat(dirfd, path, &buf, (flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); if (rc != -1 && S_ISFIFO(buf.st_mode)) { overly_magic_nonblocking = 1; } @@ -105,7 +105,7 @@ return rc; } #endif - stat_rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, (flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); + stat_rc = base_fstatat(dirfd, path, &buf, (flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); pseudo_debug(PDBGF_FILE, "openat(path %s), flags %o, stat rc %d, stat mode %o\n", path, flags, stat_rc, buf.st_mode); @@ -116,7 +116,7 @@ real_fchmod(rc, PSEUDO_FS_MODE(mode, 0)); // file has no path, but has been created if (pseudo_client_path_matches_fd(path, rc, flags & O_NOFOLLOW) && - real___fxstat64(_STAT_VER, rc, &buf) != -1) { + base_fstat(rc, &buf) != -1) { buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, mode); pseudo_client_op(OP_CREAT, 0, -1, dirfd, path, &buf); } diff --git a/ports/linux/openat2/guts/openat2.c b/ports/linux/openat2/guts/openat2.c index 5171647..bb4a125 100644 --- a/ports/linux/openat2/guts/openat2.c +++ b/ports/linux/openat2/guts/openat2.c @@ -8,7 +8,7 @@ * int openat2(int dirfd, const char *path, const struct open_how *how, size_t size) * int rc = -1; */ - struct stat64 buf; + PSEUDO_STATBUF buf; int overly_magic_nonblocking = 0; int existed = 1; int save_errno; @@ -81,7 +81,7 @@ /* note "else" in #ifdef O_TMPFILE above */ if (my_how.flags & O_CREAT) { save_errno = errno; - rc = real___fxstatat64(_STAT_VER, dirfd, pseudo_path, &buf, (my_how.flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); + rc = base_fstatat(dirfd, pseudo_path, &buf, (my_how.flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); existed = (rc != -1); if (!existed) pseudo_debug(PDBGF_FILE, "openat2_creat: %s -> 0%lld\n", pseudo_path, my_how.mode); @@ -94,7 +94,7 @@ */ if (!(my_how.flags & O_NONBLOCK) && ((my_how.flags & (O_WRONLY | O_RDONLY | O_RDWR)) != O_RDWR)) { save_errno = errno; - rc = real___fxstatat64(_STAT_VER, dirfd, pseudo_path, &buf, (my_how.flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); + rc = base_fstatat(dirfd, pseudo_path, &buf, (my_how.flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); if (rc != -1 && S_ISFIFO(buf.st_mode)) { overly_magic_nonblocking = 1; } @@ -151,7 +151,7 @@ return rc; } #endif - stat_rc = real___fxstatat64(_STAT_VER, dirfd, pseudo_path, &buf, (my_how.flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); + stat_rc = base_fstatat(dirfd, pseudo_path, &buf, (my_how.flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); pseudo_debug(PDBGF_FILE, "openat2(path %s), flags %lld, stat rc %d, stat mode %o\n", pseudo_path, my_how.flags, stat_rc, buf.st_mode); @@ -162,7 +162,7 @@ real_fchmod(rc, PSEUDO_FS_MODE(my_how.mode, 0)); // file has no path, but has been created if (pseudo_client_path_matches_fd(pseudo_path, rc, my_how.flags & O_NOFOLLOW) && - real___fxstat64(_STAT_VER, rc, &buf) != -1) { + base_fstat(rc, &buf) != -1) { buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, my_how.mode); pseudo_client_op(OP_CREAT, 0, -1, dirfd, pseudo_path, &buf); } diff --git a/ports/linux/portdefs.h b/ports/linux/portdefs.h index ac1d971..3ac9466 100644 --- a/ports/linux/portdefs.h +++ b/ports/linux/portdefs.h @@ -4,8 +4,16 @@ */ #define PRELINK_LIBRARIES "LD_PRELOAD" #define PRELINK_PATH "LD_LIBRARY_PATH" -#define PSEUDO_STATBUF_64 1 #define PSEUDO_STATBUF struct stat64 +/* How pseudo stats files itself, bypassing its own wrappers. glibc's + * stat64() and friends were inline wrappers around __xstat64() and + * friends before glibc 2.33, and pseudo still uses those to work with + * binaries built against older glibc. + */ +#define base_lstat real_lstat64 +#define base_fstat real_fstat64 +#define base_stat real_stat64 +#define base_fstatat(dirfd, path, buf, flags) real___fxstatat64(_STAT_VER, dirfd, path, buf, flags) #define PSEUDO_LINKPATH_SEPARATOR " " /* Linux NEVER follows symlinks for link(2)... except on old kernels * I don't care about. diff --git a/pseudo.h b/pseudo.h index 53bf13f..d83281c 100644 --- a/pseudo.h +++ b/pseudo.h @@ -104,8 +104,6 @@ extern ssize_t pseudo_sys_path_max(void); extern ssize_t pseudo_path_max(void); #define PSEUDO_PWD_MAX 4096 extern int pseudo_etc_file(const char *filename, char *realname, int flags, const char **search_dirs, int dircount); -extern void pseudo_stat32_from64(struct stat *, const struct stat64 *); -extern void pseudo_stat64_from32(struct stat64 *, const struct stat *); extern long pseudo_strtol_wrapper(const char *, char **, int); extern long long pseudo_strtoll_wrapper(const char *, char **, int); @@ -180,3 +178,7 @@ typedef struct { } pseudo_profile_t; #endif #include "pseudo_ports.h" + +/* convert between struct stat and the port's PSEUDO_STATBUF */ +extern void pseudo_stat32_from64(struct stat *, const PSEUDO_STATBUF *); +extern void pseudo_stat64_from32(PSEUDO_STATBUF *, const struct stat *); diff --git a/pseudo_client.c b/pseudo_client.c index a3d461e..4c9c2e8 100644 --- a/pseudo_client.c +++ b/pseudo_client.c @@ -218,7 +218,7 @@ typedef struct { static pseudo_msg_t xattrdb_data; pseudo_msg_t * -pseudo_xattrdb_save(int fd, const char *path, const struct stat64 *buf) { +pseudo_xattrdb_save(int fd, const char *path, const PSEUDO_STATBUF *buf) { int rc = -1; if (!path && fd < 0) return NULL; @@ -249,7 +249,7 @@ pseudo_xattrdb_save(int fd, const char *path, const struct stat64 *buf) { } pseudo_msg_t * -pseudo_xattrdb_load(int fd, const char *path, const struct stat64 *buf) { +pseudo_xattrdb_load(int fd, const char *path, const PSEUDO_STATBUF *buf) { int rc = -1, retryrc = -1; if (!path && fd < 0) return NULL; @@ -1845,7 +1845,7 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path #ifdef PSEUDO_XATTRDB if (buf) { - struct stat64 bufcopy = *buf; + PSEUDO_STATBUF bufcopy = *buf; int do_save = 0; /* maybe use xattr instead */ /* note: if we use xattr, logging won't work reliably diff --git a/pseudo_client.h b/pseudo_client.h index 716c441..ef959e6 100644 --- a/pseudo_client.h +++ b/pseudo_client.h @@ -10,16 +10,9 @@ extern pseudo_msg_t *pseudo_client_op(pseudo_op_t op, int access, int fd, int di extern int pseudo_client_ignore_path(const char *path); extern int pseudo_client_ignore_fd(int fd); extern void pseudo_client_linked_paths(const char *oldpath, const char *newpath); -#if PSEUDO_STATBUF_64 -#define base_lstat real_lstat64 -#define base_fstat real_fstat64 -#define base_stat real_stat64 -#define base_fstatat(dirfd, path, buf, flags) real___fxstatat64(_STAT_VER, dirfd, path, buf, flags) -#else -#define base_lstat real_lstat -#define base_fstat real_fstat -#define base_stat real_stat -#define base_fstatat(dirfd, path, buf, flags) real___fxstatat(_STAT_VER, dirfd, path, buf, flags) +/* The port defines how pseudo stats files itself, on PSEUDO_STATBUF */ +#if !defined(base_lstat) || !defined(base_fstat) || !defined(base_stat) || !defined(base_fstatat) +#error "pseudo: the port must define base_lstat, base_fstat, base_stat and base_fstatat" #endif extern void pseudo_antimagic(void); extern void pseudo_magic(void); diff --git a/pseudo_util.c b/pseudo_util.c index 9845bd2..25f8bc0 100644 --- a/pseudo_util.c +++ b/pseudo_util.c @@ -1776,7 +1776,7 @@ pseudo_evlog_logfile(char *defname, int prefer_fd) { } void -pseudo_stat32_from64(struct stat *buf32, const struct stat64 *buf) { +pseudo_stat32_from64(struct stat *buf32, const PSEUDO_STATBUF *buf) { buf32->st_dev = buf->st_dev; buf32->st_ino = buf->st_ino; buf32->st_mode = buf->st_mode; @@ -1793,7 +1793,7 @@ pseudo_stat32_from64(struct stat *buf32, const struct stat64 *buf) { } void -pseudo_stat64_from32(struct stat64 *buf64, const struct stat *buf) { +pseudo_stat64_from32(PSEUDO_STATBUF *buf64, const struct stat *buf) { buf64->st_dev = buf->st_dev; buf64->st_ino = buf->st_ino; buf64->st_mode = buf->st_mode; From patchwork Wed Nov 11 04:20:36 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103637 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.2571.1794370865753443246 for ; Tue, 10 Nov 2026 20:21:05 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3ls2428068 for ; Tue, 10 Nov 2026 22:21:05 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 08/34] patches/yocto-older-glibc-symbols.patch: Update to latest YP version Date: Tue, 10 Nov 2026 22:20:36 -0600 Message-Id: <1794370862-30908-9-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:16 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5268 MIME-Version: 1.0 From: Mark Hatle As of openembedded-core: commit 994e508b2a0ede8b5cc4fe39444cf25dc9a53faf Author: Richard Purdie Date: Tue May 6 10:01:21 2025 +0100 pseudo: Upgrade to version 1.9.1 This brings in: * nftw, nftw64: add wrapper * ftw, nftw, ftw64, nftw64: add tests * Move ftw and ftw64 to calling ntfw and nftw64 * makewrappers: Introduce 'array' support * pseudo_util.c: Avoid warning when we intentionally discard const * pseudo_client.c: Fix warning * yocto-older-glibc-symbols.path: Add as a reference patch * pseudo/pseudo_client: Add wrapper functions to operate correctly with glibc 2.38 onwards * configure: Prune PIE flags * test/test-parallel-rename.sh: Add parallel rename test * test/test-parallel-symlinks.sh: Add parallel symlink test * ports/linux/guts: Add .gitignore to ignore generated files Signed-off-by: Richard Purdie Signed-off-by: Mark Hatle --- patches/yocto-older-glibc-symbols.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patches/yocto-older-glibc-symbols.patch b/patches/yocto-older-glibc-symbols.patch index c453b5f..f42b32b 100644 --- a/patches/yocto-older-glibc-symbols.patch +++ b/patches/yocto-older-glibc-symbols.patch @@ -28,10 +28,10 @@ diff --git a/Makefile.in b/Makefile.in @@ -120,7 +120,7 @@ $(PSEUDODB): pseudodb.o $(SHOBJS) $(DBOBJS) pseudo_ipc.o | $(BIN) libpseudo: $(LIBPSEUDO) - $(LIBPSEUDO): $(WRAPOBJS) pseudo_client.o pseudo_ipc.o $(SHOBJS) | $(LIB) + $(LIBPSEUDO): $(WRAPOBJS) pseudo_client.o pseudo_client_scanf.o pseudo_ipc.o $(SHOBJS) | $(LIB) - $(CC) $(CFLAGS) $(CFLAGS_PSEUDO) -shared -o $(LIBPSEUDO) \ + $(CC) $(CFLAGS) -Lprebuilt/$(shell uname -m)-linux/lib/ $(CFLAGS_PSEUDO) -shared -o $(LIBPSEUDO) \ - pseudo_client.o pseudo_ipc.o \ + pseudo_client.o pseudo_client_scanf.o pseudo_ipc.o \ $(WRAPOBJS) $(SHOBJS) $(LDFLAGS) $(CLIENT_LDFLAGS) diff --git a/pseudo_wrappers.c b/pseudo_wrappers.c From patchwork Wed Nov 11 04:20:37 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103638 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.2572.1794370865918820642 for ; Tue, 10 Nov 2026 20:21:06 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3lt2428068 for ; Tue, 10 Nov 2026 22:21:05 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 09/34] Remove unused #if 0 debug code Date: Tue, 10 Nov 2026 22:20:37 -0600 Message-Id: <1794370862-30908-10-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:16 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5269 MIME-Version: 1.0 From: Mark Hatle Remove an abort() check for clients named "pseudo" in pseudo_client_request(), and dump_env() in pseudo_util.c, which have been disabled with #if 0 and are not referred to. AI-Generated: Uses GitHub Copilot Signed-off-by: Mark Hatle --- pseudo_client.c | 5 ----- pseudo_util.c | 16 ---------------- 2 files changed, 21 deletions(-) diff --git a/pseudo_client.c b/pseudo_client.c index 4c9c2e8..bbe0829 100644 --- a/pseudo_client.c +++ b/pseudo_client.c @@ -1399,11 +1399,6 @@ pseudo_client_request(pseudo_msg_t *msg, size_t len, const char *path) { pseudo_msg_t *response = 0; int tries = 0; int rc; - #if 0 - extern char *program_invocation_short_name; - if (!strcmp(program_invocation_short_name, "pseudo")) - abort(); - #endif if (!msg) return 0; diff --git a/pseudo_util.c b/pseudo_util.c index 25f8bc0..78c2e1b 100644 --- a/pseudo_util.c +++ b/pseudo_util.c @@ -106,22 +106,6 @@ int (*pseudo_real_unsetenv)(const char *) = unsetenv; char * (*pseudo_real_getenv)(const char *) = getenv; int (*pseudo_real_setenv)(const char *, const char *, int) = setenv; -#if 0 -static void -dump_env(char **envp) { - size_t i = 0; - for (i = 0; envp[i]; i++) { - pseudo_debug(PDBGF_ENV, "dump_envp: [%d]%s\n", (int) i, envp[i]); - } - - for (i = 0; pseudo_env[i].key; i++) { - pseudo_debug(PDBGF_ENV, "dump_envp: {%d}%s=%s\n", (int) i, pseudo_env[i].key, pseudo_env[i].value); - } - - pseudo_debug(PDBGF_ENV, "dump_envp: _in_init %d\n", pseudo_util_initted); -} -#endif - int pseudo_has_unload(char * const *envp) { static const char unload[] = "PSEUDO_UNLOAD"; From patchwork Wed Nov 11 04:20:38 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103614 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.2573.1794370866072427250 for ; Tue, 10 Nov 2026 20:21:06 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3lu2428068 for ; Tue, 10 Nov 2026 22:21:05 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 10/34] Replace #if 0 strerror() debug messages with a comment Date: Tue, 10 Nov 2026 22:20:38 -0600 Message-Id: <1794370862-30908-11-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:14 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5270 MIME-Version: 1.0 From: Mark Hatle The wrapper template and popen() kept a disabled debug message using strerror(), with a comment that strerror() can hang on systems using locale data for it. Keep the explanation, as a comment by the message that prints errno as a number, and drop the disabled code. AI-Generated: Uses GitHub Copilot Signed-off-by: Mark Hatle --- ports/unix/pseudo_wrappers.c | 7 +------ templates/wrapfuncs.c | 7 +------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/ports/unix/pseudo_wrappers.c b/ports/unix/pseudo_wrappers.c index 41398e4..b0faca8 100644 --- a/ports/unix/pseudo_wrappers.c +++ b/ports/unix/pseudo_wrappers.c @@ -30,12 +30,7 @@ popen(const char *command, const char *mode) { save_errno = errno; pseudo_droplock(); sigprocmask(SIG_SETMASK, &saved, NULL); -#if 0 -/* This can cause hangs on some recentish systems which use locale - * stuff for strerror... - */ - pseudo_debug(PDBGF_WRAPPER, "completed: popen (maybe: %s)\n", strerror(save_errno)); -#endif + /* not strerror(), which can hang on systems using locale data for it */ pseudo_debug(PDBGF_WRAPPER, "completed: popen (errno: %d)\n", save_errno); errno = save_errno; return rc; diff --git a/templates/wrapfuncs.c b/templates/wrapfuncs.c index a57c997..d7fb548 100644 --- a/templates/wrapfuncs.c +++ b/templates/wrapfuncs.c @@ -78,12 +78,7 @@ quick_exit: pseudo_droplock(); sigprocmask(SIG_SETMASK, &saved, NULL); pseudo_debug(PDBGF_WRAPPER | PDBGF_VERBOSE, "${name} - yielded lock, restored signals\n"); -#if 0 -/* This can cause hangs on some recentish systems which use locale - * stuff for strerror... - */ - pseudo_debug(PDBGF_WRAPPER, "wrapper completed: ${name} returns ${rc_format} (errno: %s)\n", ${rc_value}, strerror(save_errno)); -#endif + /* not strerror(), which can hang on systems using locale data for it */ pseudo_debug(PDBGF_WRAPPER, "wrapper completed: ${name} returns ${rc_format} (errno: %d)\n", ${rc_value}, save_errno); errno = save_errno; PROFILE_DONE; From patchwork Wed Nov 11 04:20:39 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103616 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.2574.1794370866270942627 for ; Tue, 10 Nov 2026 20:21:06 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3lv2428068 for ; Tue, 10 Nov 2026 22:21:05 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 11/34] pseudo_util: Remove stale with_libpseudo() comment Date: Tue, 10 Nov 2026 22:20:39 -0600 Message-Id: <1794370862-30908-12-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:14 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5271 MIME-Version: 1.0 From: Mark Hatle pseudo_setupenv() kept libdir_path until the end "because with_libpseudo might need it". That was only true for the Darwin port, which passed it to with_libpseudo(); the parameter was removed with the port. AI-Generated: Uses GitHub Copilot Signed-off-by: Mark Hatle --- pseudo_util.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/pseudo_util.c b/pseudo_util.c index 78c2e1b..351cd09 100644 --- a/pseudo_util.c +++ b/pseudo_util.c @@ -1123,9 +1123,6 @@ pseudo_setupenv() { } } - /* we kept libdir path until now because with_libpseudo might - * need it - */ free(libdir_path); } From patchwork Wed Nov 11 04:20:40 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103618 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.2482.1794370866468141084 for ; Tue, 10 Nov 2026 20:21:06 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3lw2428068 for ; Tue, 10 Nov 2026 22:21:05 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 12/34] ports/common/subports: Show the unknown OS name in the error Date: Tue, 10 Nov 2026 22:20:40 -0600 Message-Id: <1794370862-30908-13-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:14 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5272 MIME-Version: 1.0 From: Mark Hatle The error for an unsupported host used %(uname -s) instead of $(uname -s), so it printed that text instead of the name of the OS. AI-Generated: Uses GitHub Copilot Signed-off-by: Mark Hatle --- ports/common/subports | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/common/subports b/ports/common/subports index d5f9292..486c7a2 100755 --- a/ports/common/subports +++ b/ports/common/subports @@ -5,7 +5,7 @@ case $(uname -s) in Linux) echo "linux";; -*) echo >&2 "Unknown result from uname -s: %(uname -s). Aborting." +*) echo >&2 "Unknown result from uname -s: $(uname -s). Aborting." exit 1 ;; esac From patchwork Wed Nov 11 04:20:41 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103623 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.2575.1794370866636441874 for ; Tue, 10 Nov 2026 20:21:06 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3lx2428068 for ; Tue, 10 Nov 2026 22:21:06 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 13/34] ports/linux: Drop redundant PSEUDO_LINK_SYMLINK_BEHAVIOR override Date: Tue, 10 Nov 2026 22:20:41 -0600 Message-Id: <1794370862-30908-14-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:14 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5273 MIME-Version: 1.0 From: Mark Hatle ports/linux/portdefs.h redefined PSEUDO_LINK_SYMLINK_BEHAVIOR to 0, the default already set in pseudo.h. Remove it, and move its note that 0 means not following symlinks, because linkat() rejects AT_SYMLINK_NOFOLLOW, into the comment in pseudo.h. AI-Generated: Uses GitHub Copilot Signed-off-by: Mark Hatle --- ports/linux/portdefs.h | 9 --------- pseudo.h | 3 ++- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/ports/linux/portdefs.h b/ports/linux/portdefs.h index 3ac9466..97ddcb6 100644 --- a/ports/linux/portdefs.h +++ b/ports/linux/portdefs.h @@ -15,15 +15,6 @@ #define base_stat real_stat64 #define base_fstatat(dirfd, path, buf, flags) real___fxstatat64(_STAT_VER, dirfd, path, buf, flags) #define PSEUDO_LINKPATH_SEPARATOR " " -/* Linux NEVER follows symlinks for link(2)... except on old kernels - * I don't care about. - */ -#undef PSEUDO_LINK_SYMLINK_BEHAVIOR -/* Note: 0, here, really means AT_SYMLINK_NOFOLLOW, but specifying that - * causes errors; you have to leave it empty or specify AT_SYMLINK_FOLLOW. - */ -#define PSEUDO_LINK_SYMLINK_BEHAVIOR 0 - /* There were symbol changes that can cause the linker to request * newer versions of glibc, which causes problems occasionally on * older hosts if pseudo is built against a newer glibc and then diff --git a/pseudo.h b/pseudo.h index d83281c..13249bf 100644 --- a/pseudo.h +++ b/pseudo.h @@ -155,7 +155,8 @@ extern char *pseudo_version; * would ever do that? Well, Linux did, and possibly as a result, linkat() * does by default too; if you are on a host with the historical Unix * behavior of following symlinks to find the link target, you will want - * to set this to AT_SYMLINK_FOLLOW. + * to set this to AT_SYMLINK_FOLLOW. 0 means not following them; it can't + * be AT_SYMLINK_NOFOLLOW, as linkat() rejects that flag. */ #define PSEUDO_LINK_SYMLINK_BEHAVIOR 0 From patchwork Wed Nov 11 04:20:42 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103625 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.2483.1794370867075099635 for ; Tue, 10 Nov 2026 20:21:07 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3m02428068 for ; Tue, 10 Nov 2026 22:21:06 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 14/34] pseudo_server: Skip events for clients closed earlier in the batch Date: Tue, 10 Nov 2026 22:20:42 -0600 Message-Id: <1794370862-30908-15-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:14 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5274 MIME-Version: 1.0 From: Mark Hatle The epoll loop handles up to EPOLL_MAX_EVENTS events per wakeup. If a client's slot was already closed earlier in the same batch, its event was treated as an empty read, and close_client() was called again, decrementing active_clients a second time. The server starts its idle shutdown when active_clients reaches 1, so it could exit while clients were still connected. Skip events for slots that are no longer in use, as the select() loop does. AI-Generated: Uses GitHub Copilot Signed-off-by: Mark Hatle --- pseudo_server.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pseudo_server.c b/pseudo_server.c index b983121..fc5c2c1 100644 --- a/pseudo_server.c +++ b/pseudo_server.c @@ -728,7 +728,9 @@ static void pseudo_server_loop_epoll(void) pseudo_debug(PDBGF_SERVER, "Hit max open files.\n"); } } - } else { + } else if (clients[client_id].fd != -1) { + /* the client may have been closed earlier in + * this batch of events */ int n = 0; ioctl(clients[client_id].fd, FIONREAD, &n); if (n == 0) { From patchwork Wed Nov 11 04:20:43 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103644 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.2491.1794370872143418442 for ; Tue, 10 Nov 2026 20:21:12 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3m12428068 for ; Tue, 10 Nov 2026 22:21:06 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 15/34] pseudo_server: Drop an idle client when out of file descriptors Date: Tue, 10 Nov 2026 22:20:43 -0600 Message-Id: <1794370862-30908-16-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:16 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5293 MIME-Version: 1.0 From: Mark Hatle When accept() failed with EMFILE, the epoll loop only logged it. The listening socket stays readable while the connection is pending, so epoll_wait() returned immediately again, and the server spun until a client went away. Like the select() loop does, drop one client to make room: one that had no event in the current batch, so it can't have one pending in it either. AI-Generated: Uses GitHub Copilot Signed-off-by: Mark Hatle --- pseudo_server.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/pseudo_server.c b/pseudo_server.c index fc5c2c1..77d1c39 100644 --- a/pseudo_server.c +++ b/pseudo_server.c @@ -626,6 +626,7 @@ static void pseudo_server_loop_epoll(void) struct sigaction eat_usr2 = { .sa_handler = set_do_list_clients }; + int hitmaxfiles = 0; clients = malloc(16 * sizeof(*clients)); if (!clients) { @@ -724,8 +725,8 @@ static void pseudo_server_loop_epoll(void) exit(PSEUDO_EXIT_EPOLL_CTL); } } else if (errno == EMFILE) { - // select() loop would drop a client here, we do nothing (for now) - pseudo_debug(PDBGF_SERVER, "Hit max open files.\n"); + hitmaxfiles = 1; + pseudo_debug(PDBGF_SERVER, "Hit max open files, dropping a client.\n"); } } } else if (clients[client_id].fd != -1) { @@ -742,6 +743,27 @@ static void pseudo_server_loop_epoll(void) if (die_forcefully) break; } + /* The listening socket stays readable while accept() + * fails, so make room by dropping one client that had + * no event in this batch, and so can't have one pending + * in it either. + */ + if (hitmaxfiles && !die_forcefully) { + hitmaxfiles = 0; + for (i = 1; i <= highest_client; ++i) { + int j; + if (clients[i].fd == -1) + continue; + for (j = 0; j < rc; ++j) { + if ((int) events[j].data.u64 == i) + break; + } + if (j == rc) { + close_client(i); + break; + } + } + } pseudo_debug(PDBGF_SERVER, "server loop complete [%d clients left]\n", active_clients); } else { pseudo_error("epoll_wait failed: %s\n", strerror(errno)); From patchwork Wed Nov 11 04:20:44 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103626 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.2484.1794370867469091959 for ; Tue, 10 Nov 2026 20:21:07 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3m22428068 for ; Tue, 10 Nov 2026 22:21:06 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 16/34] pseudo_server: Always use epoll Date: Tue, 10 Nov 2026 22:20:44 -0600 Message-Id: <1794370862-30908-17-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:15 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5275 MIME-Version: 1.0 From: Mark Hatle The server had two main loops: a select() based loop, used by default, and an epoll loop, used with --enable-epoll. Every change to client handling had to be made in both, and select() can't handle descriptors numbered 1024 or higher, which a server with many clients can reach. Now that Linux is the only port, always use the epoll loop, as pseudo_server_loop(), and remove the select() loop and the PSEUDO_EPOLL conditionals. pseudo_server.c fails with #error for a port other than Linux. configure accepts --enable-epoll for compatibility and ignores it, and fails with an error if epoll is disabled. AI-Generated: Uses GitHub Copilot Signed-off-by: Mark Hatle --- Makefile.in | 3 +- configure | 18 +---- pseudo_server.c | 231 ++++---------------------------------------------------- 3 files changed, 17 insertions(+), 235 deletions(-) diff --git a/Makefile.in b/Makefile.in index e875864..84e3ee8 100644 --- a/Makefile.in +++ b/Makefile.in @@ -14,7 +14,6 @@ SQLITE=@SQLITE@ SQLITE_LIB=@SQLITE_LIB@ SQLITE_MEMORY=@SQLITE_MEMORY@ FORCE_ASYNC=@FORCE_ASYNC@ -EPOLL=@EPOLL@ XATTR=@XATTR@ XATTRDB=@XATTRDB@ PROFILING=@PROFILING@ @@ -45,7 +44,7 @@ LOCALSTATEDIR=$(PREFIX)/$(LOCALSTATE) CFLAGS_BASE=-pipe -std=gnu99 -Wall -W -Wextra -Wno-deprecated-declarations CFLAGS_CODE=-fPIC -D_LARGEFILE64_SOURCE -D_ATFILE_SOURCE $(ARCH_FLAGS) -CFLAGS_DEFS=-DPSEUDO_PREFIX='"$(PREFIX)"' -DPSEUDO_SUFFIX='"$(SUFFIX)"' -DPSEUDO_BINDIR='"$(BIN)"' -DPSEUDO_LIBDIR='"$(LIB)"' -DPSEUDO_LOCALSTATEDIR='"$(LOCALSTATE)"' -DPSEUDO_VERSION='"$(VERSION)"' $(SQLITE_MEMORY) $(FORCE_ASYNC) -DPSEUDO_PASSWD_FALLBACK='$(PASSWD_FALLBACK)' $(OPTDEFS) $(EPOLL) +CFLAGS_DEFS=-DPSEUDO_PREFIX='"$(PREFIX)"' -DPSEUDO_SUFFIX='"$(SUFFIX)"' -DPSEUDO_BINDIR='"$(BIN)"' -DPSEUDO_LIBDIR='"$(LIB)"' -DPSEUDO_LOCALSTATEDIR='"$(LOCALSTATE)"' -DPSEUDO_VERSION='"$(VERSION)"' $(SQLITE_MEMORY) $(FORCE_ASYNC) -DPSEUDO_PASSWD_FALLBACK='$(PASSWD_FALLBACK)' $(OPTDEFS) CFLAGS_DEBUG=-O2 -g @DEFAULT_SQLITE@CFLAGS_SQL=-L$(SQLITE)/$(SQLITE_LIB) -I$(SQLITE)/include $(RPATH) CFLAGS_PSEUDO=$(CFLAGS_BASE) $(CFLAGS_CODE) $(CFLAGS_DEFS) \ diff --git a/configure b/configure index b43809e..315bcd0 100755 --- a/configure +++ b/configure @@ -15,7 +15,6 @@ opt_arch=x86 opt_bits= opt_sqlite=/usr opt_rpath= -opt_epoll= opt_memory= opt_async= opt_xattr= @@ -35,7 +34,6 @@ usage() echo >&2 " [--suffix=...]" echo >&2 " [--target=...]" echo >&2 " [--enable-memory-db]" - echo >&2 " [--enable-epoll]" echo >&2 " [--enable-xattr]" echo >&2 " [--enable-xattrdb]" echo >&2 " [--enable-profiling]" @@ -110,10 +108,11 @@ do opt_profiling=true ;; --enable-epoll=no) - opt_epoll=false + echo >&2 "Error: disabling epoll is no longer supported: the pseudo server always uses epoll." + exit 1 ;; --enable-epoll=yes | --enable-epoll) - opt_epoll=true + # epoll is always used; accepted for compatibility ;; --enable-xattr=no) opt_xattr=false @@ -261,10 +260,6 @@ if [ -z "$opt_async" ]; then opt_async=false fi -if [ -z "$opt_epoll" ]; then - opt_epoll=false -fi - if $opt_async; then FORCE_ASYNC="-DPSEUDO_FORCE_ASYNC" else @@ -293,12 +288,6 @@ if [ -z "$opt_xattr" ]; then fi fi -if $opt_epoll; then - EPOLL="-DPSEUDO_EPOLL" -else - EPOLL="" -fi - if $opt_xattr || $opt_xattrdb; then if ! $xattr_runs; then echo >&2 "WARNING: getfattr doesn't work, but xattr-related features requested." @@ -339,7 +328,6 @@ touch func_deps.mk rm -f Makefile sed -e ' s,@PREFIX@,'"$opt_prefix"',g - s,@EPOLL@,'"$EPOLL"',g s,@XATTR@,'"$opt_xattr"',g s,@XATTRDB@,'"$opt_xattrdb"',g s,@PROFILING@,'"$opt_profiling"',g diff --git a/pseudo_server.c b/pseudo_server.c index 77d1c39..c54c0d2 100644 --- a/pseudo_server.c +++ b/pseudo_server.c @@ -15,16 +15,12 @@ #include #include #include -#include #include #include #include #include #include #include -#ifdef PSEUDO_EPOLL -#include -#endif #include #include "pseudo.h" @@ -33,6 +29,11 @@ #include "pseudo_client.h" #include "pseudo_db.h" +#if !PSEUDO_PORT_LINUX +#error "pseudo: the server uses epoll(7), which is only implemented for Linux" +#endif +#include + static int listen_fd = -1; typedef struct { @@ -80,11 +81,7 @@ set_do_list_clients(int sig) { static int messages = 0, responses = 0; static struct timeval message_time = { .tv_sec = 0 }; -#ifdef PSEUDO_EPOLL -static void pseudo_server_loop_epoll(void); -#else static void pseudo_server_loop(void); -#endif /* helper function to make a directory, just like mkdir -p. * Can't use system() because the child shell would end up trying @@ -369,11 +366,7 @@ pseudo_server_start(int daemonize) { kill(ppid, SIGUSR1); } } -#ifdef PSEUDO_EPOLL - pseudo_server_loop_epoll(); -#else pseudo_server_loop(); -#endif return 0; } @@ -589,8 +582,8 @@ serve_client(int i) { } free(response_path); } else { - /* this should not be happening, but the exceptions aren't - * being detected in select() for some reason. + /* this should not be happening, as the server loop + * closes clients with nothing to read. */ pseudo_debug(PDBGF_SERVER, "client %d: no message\n", (int) clients[i].pid); close_client(i); @@ -612,9 +605,12 @@ serve_client(int i) { return rc; } -#ifdef PSEUDO_EPOLL -static void pseudo_server_loop_epoll(void) -{ +/* get clients, handle messages, shut down. + * This doesn't actually do any work, it just calls a ton of things which + * do work. + */ +static void +pseudo_server_loop(void) { struct sockaddr_un client; socklen_t len; int i; @@ -803,204 +799,3 @@ static void pseudo_server_loop_epoll(void) } } -#else - -/* get clients, handle messages, shut down. - * This doesn't actually do any work, it just calls a ton of things which - * do work. - */ -static void -pseudo_server_loop(void) { - struct sockaddr_un client; - socklen_t len; - fd_set reads, writes, events; - int max_fd, current_clients; - struct timeval timeout; - int i; - int rc; - int fd; - int loop_timeout = pseudo_server_timeout; - struct sigaction eat_usr2 = { - .sa_handler = set_do_list_clients - }; - int hitmaxfiles; - - clients = malloc(16 * sizeof(*clients)); - if (!clients) { - pseudo_error("out of memory allocating client table.\n"); - exit(PSEUDO_EXIT_LISTEN_FD); - } - - sigaction(SIGUSR2, &eat_usr2, NULL); - - clients[0].fd = listen_fd; - clients[0].pid = getpid(); - - for (i = 1; i < 16; ++i) { - clients[i].fd = -1; - clients[i].pid = 0; - clients[i].tag = NULL; - clients[i].program = NULL; - } - - active_clients = 1; - max_clients = 16; - highest_client = 0; - hitmaxfiles = 0; - - pseudo_debug(PDBGF_SERVER, "server loop started.\n"); - if (listen_fd < 0) { - pseudo_error("got into loop with no valid listen fd.\n"); - exit(PSEUDO_EXIT_LISTEN_FD); - } - pdb_log_msg(SEVERITY_INFO, NULL, NULL, NULL, "server started (pid %d)", getpid()); - - FD_ZERO(&reads); - FD_ZERO(&events); - FD_ZERO(&writes); - FD_SET(clients[0].fd, &reads); - FD_SET(clients[0].fd, &events); - max_fd = clients[0].fd; - timeout = (struct timeval) { .tv_sec = LOOP_DELAY, .tv_usec = 0 }; - - /* EINTR tends to come from profiling, so it is not a good reason to - * exit; other signals are caught and set the flag causing a graceful - * exit. */ - sigset_t maskusr2; - sigemptyset(&maskusr2); - sigaddset(&maskusr2, SIGUSR2); - sigprocmask(SIG_BLOCK, &maskusr2, NULL); - while ((rc = select(max_fd + 1, &reads, &writes, &events, &timeout)) >= 0 || (errno == EINTR)) { - sigprocmask(SIG_UNBLOCK, &maskusr2, NULL); - if (rc == 0 || (rc == -1 && errno == EINTR)) { - /* If there's no clients, start timing out. If there - * are active clients, never time out. - */ - if (active_clients == 1) { - loop_timeout -= LOOP_DELAY; - /* maybe flush database to disk */ - pdb_maybe_backup(); - if (loop_timeout <= 0) { - pseudo_debug(PDBGF_SERVER, "no more clients, got bored.\n"); - die_peacefully = 1; - } else { - /* display this if not exiting */ - pseudo_debug(PDBGF_SERVER | PDBGF_BENCHMARK, "%d messages handled in %.4f seconds, %d responses\n", - messages, - (double) message_time.tv_sec + - (double) message_time.tv_usec / 1000000.0, - responses); - } - } - } else if (rc > 0) { - loop_timeout = pseudo_server_timeout; - for (i = 1; i <= highest_client; ++i) { - if (clients[i].fd == -1) { - continue; - } else if (FD_ISSET(clients[i].fd, &reads)) { - int n = 0; - ioctl(clients[i].fd, FIONREAD, &n); - if (n == 0) { - close_client(i); - } else { - serve_client(i); - } - } else if (hitmaxfiles) { - /* Only close one per loop iteration in the interests of caution */ - close_client(i); - hitmaxfiles = 0; - } - if (die_forcefully) - break; - } - hitmaxfiles = 0; - if (!die_forcefully && - (FD_ISSET(clients[0].fd, &events) || - FD_ISSET(clients[0].fd, &reads))) { - len = sizeof(client); - if ((fd = accept(listen_fd, (struct sockaddr *) &client, &len)) != -1) { - /* Don't allow clients to end up on fd 2, because glibc's - * malloc debug uses that fd unconditionally. - */ - if (fd == 2) { - int newfd = fcntl(fd, F_DUPFD, 3); - close(fd); - fd = newfd; - } - pseudo_debug(PDBGF_SERVER, "new client fd %d\n", fd); - open_client(fd); - /* A new client implicitly cancels any - * previous shutdown request, or a - * shutdown for lack of clients. - */ - pseudo_server_timeout = DEFAULT_PSEUDO_SERVER_TIMEOUT; - die_peacefully = 0; - } else if (errno == EMFILE) { - hitmaxfiles = 1; - pseudo_debug(PDBGF_SERVER, "Hit max open files, dropping a client.\n"); - } - } - pseudo_debug(PDBGF_SERVER, "server loop complete [%d clients left]\n", active_clients); - } - if (do_list_clients) { - do_list_clients = 0; - pseudo_info("listing clients [1 through %d]:\n", highest_client); - for (i = 1; i <= highest_client; ++i) { - if (clients[i].fd == -1) { - pseudo_info("client %4d: inactive.\n", i); - continue; - } - pseudo_info("client %4d: fd %4d, pid %5d, state %s, program %s\n", - i, clients[i].fd, clients[i].pid, - FD_ISSET(clients[i].fd, &reads) ? "R" : "-", - clients[i].program ? clients[i].program : ""); - } - pseudo_info("done.\n"); - } - if (die_peacefully || die_forcefully) { - pseudo_debug(PDBGF_SERVER, "quitting.\n"); - pseudo_debug(PDBGF_SERVER | PDBGF_BENCHMARK, "server %d exiting: handled %d messages in %.4f seconds\n", - getpid(), messages, - (double) message_time.tv_sec + - (double) message_time.tv_usec / 1000000.0); - pdb_log_msg(SEVERITY_INFO, NULL, NULL, NULL, "server %d exiting: handled %d messages in %.4f seconds", - getpid(), messages, - (double) message_time.tv_sec + - (double) message_time.tv_usec / 1000000.0); - /* and at this point, we'll start refusing connections */ - close(clients[0].fd); - /* This is a good place to insert a delay for - * debugging race conditions during startup. */ - /* usleep(300000); */ - exit(0); - } - FD_ZERO(&reads); - FD_ZERO(&writes); - FD_ZERO(&events); - FD_SET(clients[0].fd, &reads); - FD_SET(clients[0].fd, &events); - max_fd = clients[0].fd; - /* current_clients is a sanity check; note that for - * purposes of select(), the server is one of the fds, - * and thus, "a client". - */ - current_clients = 1; - for (i = 1; i <= highest_client; ++i) { - if (clients[i].fd != -1) { - ++current_clients; - FD_SET(clients[i].fd, &reads); - if (clients[i].fd > max_fd) - max_fd = clients[i].fd; - } - } - if (current_clients != active_clients) { - pseudo_debug(PDBGF_SERVER, "miscount of current clients (%d) against active_clients (%d)?\n", - current_clients, active_clients); - } - /* reinitialize timeout because Linux select alters it */ - timeout = (struct timeval) { .tv_sec = LOOP_DELAY, .tv_usec = 0 }; - sigprocmask(SIG_BLOCK, &maskusr2, NULL); - } - pseudo_error("select failed: %s\n", strerror(errno)); -} -#endif /* this is the else of #ifdef PSEUDO_EPOLL */ From patchwork Wed Nov 11 04:20:45 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 103624 Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.2576.1794370867623770736 for ; Tue, 10 Nov 2026 20:21:07 -0800 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 6AB4L3m32428068 for ; Tue, 10 Nov 2026 22:21:07 -0600 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org Subject: [pseudo][PATCH 17/34] configure: Fix --disable-* options Date: Tue, 10 Nov 2026 22:20:45 -0600 Message-Id: <1794370862-30908-18-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Wed, 11 Nov 2026 04:21:14 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5276 MIME-Version: 1.0 From: Mark Hatle configure turns --disable-