All of lore.kernel.org
 help / color / mirror / Atom feed
From: Mark Hatle <mark.hatle@kernel.crashing.org>
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	[thread overview]
Message-ID: <1794370862-30908-3-git-send-email-mark.hatle@kernel.crashing.org> (raw)
In-Reply-To: <1794370862-30908-1-git-send-email-mark.hatle@kernel.crashing.org>

From: Mark Hatle <mark.hatle@amd.com>

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 <mark.hatle@amd.com>
---
 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 */
-- 
1.8.3.1


  parent reply	other threads:[~2026-11-11  4:21 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-11  4:20 [pseudo][PATCH 00/34] Various fixes Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 01/34] pseudo_client: serialize path changes across clients Mark Hatle
2026-11-11  4:20 ` Mark Hatle [this message]
2026-11-11  4:20 ` [pseudo][PATCH 03/34] ports: check created files under the path lock before recording them Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 04/34] test-parallel-ops: Add a parallel create/rename/link/chmod test Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 05/34] ports: Remove PSEUDO_NO_REAL_AT_FUNCTIONS Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 06/34] ports/unix: Remove guts overridden by the Linux port Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 07/34] ports: Let the port define how pseudo stats files Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 08/34] patches/yocto-older-glibc-symbols.patch: Update to latest YP version Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 09/34] Remove unused #if 0 debug code Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 10/34] Replace #if 0 strerror() debug messages with a comment Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 11/34] pseudo_util: Remove stale with_libpseudo() comment Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 12/34] ports/common/subports: Show the unknown OS name in the error Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 13/34] ports/linux: Drop redundant PSEUDO_LINK_SYMLINK_BEHAVIOR override Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 14/34] pseudo_server: Skip events for clients closed earlier in the batch Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 15/34] pseudo_server: Drop an idle client when out of file descriptors Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 16/34] pseudo_server: Always use epoll Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 17/34] configure: Fix --disable-* options Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 18/34] pseudo_server: Lower highest_client when the highest client closes Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 19/34] pseudo_server: Set highest_client to the new client when growing Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 20/34] pseudo_util: Store PSEUDO_SEVERITY as severity names Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 21/34] pseudo: Fix the -k severity option Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 22/34] pseudo_util: Make -v and numeric PSEUDO_DEBUG add debug flags Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 23/34] AGENTS.md: Add to assist future developers Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 24/34] pseudo_util: Fix the date format in the event log dump Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 25/34] pseudo: List -p in the usage message Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 26/34] pseudo_util: Write the event log to PSEUDO_EVLOG_FILE Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 27/34] pseudo.1: Update to match the current code Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 28/34] ports/linux: Don't require <linux/mount.h> Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 29/34] configure: Require GCC 5 or later Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 30/34] doc: Update README and doc/ to match the code Mark Hatle
2026-11-11  4:20 ` [pseudo][PATCH 31/34] configure: Fix the --enable-profiling default Mark Hatle
2026-11-11  4:21 ` [pseudo][PATCH 32/34] pseudo_util: Skip unneeded lstat calls when canonicalizing paths Mark Hatle
2026-11-11  4:21 ` [pseudo][PATCH 33/34] wrappers: Don't re-block signals in nested wrapper calls Mark Hatle
2026-11-11  4:21 ` [pseudo][PATCH 34/34] Wrap execvpe(), fexecve() and execveat() Mark Hatle

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1794370862-30908-3-git-send-email-mark.hatle@kernel.crashing.org \
    --to=mark.hatle@kernel.crashing.org \
    --cc=yocto-patches@lists.yoctoproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.