From: Omar Sandoval <osandov@osandov.com> To: linux-fsdevel@vger.kernel.org, Al Viro <viro@zeniv.linux.org.uk> Cc: kernel-team@fb.com, Xi Wang <xi@cs.washington.edu> Subject: [RFC PATCH v4 2/4] fs: add AT_LINK_REPLACE flag for linkat() which replaces the target Date: Tue, 28 Jan 2020 15:19:01 -0800 [thread overview] Message-ID: <1f5a197a2fdb0668f6dce8b9a4403481bc957a7c.1580251857.git.osandov@fb.com> (raw) In-Reply-To: <cover.1580251857.git.osandov@fb.com> From: Omar Sandoval <osandov@fb.com> One of the most common uses of temporary files is the classic atomic replacement pattern, i.e., - write temporary file - fsync temporary file - rename temporary file over real file - fsync parent directory Now, we have O_TMPFILE, which gives us a much better way to create temporary files, but it's not possible to use it for this pattern. This patch introduces an AT_LINK_REPLACE flag which allows linkat() to replace the target file. Now, the temporary file in the pattern above can be a proper O_TMPFILE. Even without O_TMPFILE, this is a new primitive which might be useful in other contexts. The implementation on the VFS side mimics sys_renameat2(). Cc: Xi Wang <xi@cs.washington.edu> Signed-off-by: Omar Sandoval <osandov@fb.com> --- fs/ecryptfs/inode.c | 2 +- fs/namei.c | 166 +++++++++++++++++++++++++++++-------- fs/nfsd/vfs.c | 2 +- fs/overlayfs/overlayfs.h | 2 +- include/linux/fs.h | 2 +- include/uapi/linux/fcntl.h | 1 + 6 files changed, 135 insertions(+), 40 deletions(-) diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index eeb351b220b2..2f36b7a61a2f 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -440,7 +440,7 @@ static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir, dget(lower_new_dentry); lower_dir_dentry = lock_parent(lower_new_dentry); rc = vfs_link(lower_old_dentry, d_inode(lower_dir_dentry), - lower_new_dentry, NULL); + lower_new_dentry, NULL, 0); if (rc || d_really_is_negative(lower_new_dentry)) goto out_lock; rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb); diff --git a/fs/namei.c b/fs/namei.c index 9d690df17aed..78d364e99dca 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4122,6 +4122,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn * @dir: new parent * @new_dentry: where to create the new link * @delegated_inode: returns inode needing a delegation break + * @flags: link flags * * The caller must hold dir->i_mutex * @@ -4135,16 +4136,25 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn * be appropriate for callers that expect the underlying filesystem not * to be NFS exported. */ -int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode) +int vfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry, struct inode **delegated_inode, + int flags) { struct inode *inode = old_dentry->d_inode; + struct inode *target = new_dentry->d_inode; unsigned max_links = dir->i_sb->s_max_links; int error; if (!inode) return -ENOENT; - error = may_create(dir, new_dentry); + if (target) { + if (inode == target) + return 0; + error = may_delete(dir, new_dentry, false); + } else { + error = may_create(dir, new_dentry); + } if (error) return error; @@ -4172,26 +4182,55 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de if (error) return error; - inode_lock(inode); + dget(new_dentry); + lock_two_nondirectories(inode, target); + + if (is_local_mountpoint(new_dentry)) { + error = -EBUSY; + goto out; + } + /* Make sure we don't allow creating hardlink to an unlinked file */ - if (inode->i_nlink == 0 && !(inode->i_state & I_LINKABLE)) + if (inode->i_nlink == 0 && !(inode->i_state & I_LINKABLE)) { error = -ENOENT; - else if (max_links && inode->i_nlink >= max_links) + goto out; + } + if (max_links && inode->i_nlink >= max_links) { error = -EMLINK; - else { - error = try_break_deleg(inode, delegated_inode); - if (!error) - error = dir->i_op->link(old_dentry, dir, new_dentry, 0); + goto out; + } + + error = try_break_deleg(inode, delegated_inode); + if (error) + goto out; + if (target) { + error = try_break_deleg(target, delegated_inode); + if (error) + goto out; + } + + error = dir->i_op->link(old_dentry, dir, new_dentry, flags); + if (error) + goto out; + + if (target) { + dont_mount(new_dentry); + detach_mounts(new_dentry); } - if (!error && (inode->i_state & I_LINKABLE)) { + if (inode->i_state & I_LINKABLE) { spin_lock(&inode->i_lock); inode->i_state &= ~I_LINKABLE; spin_unlock(&inode->i_lock); } - inode_unlock(inode); - if (!error) +out: + unlock_two_nondirectories(inode, target); + dput(new_dentry); + if (!error) { + if (target) + fsnotify_link_count(target); fsnotify_link(dir, inode, new_dentry); + } return error; } EXPORT_SYMBOL(vfs_link); @@ -4210,11 +4249,16 @@ int do_linkat(int olddfd, const char __user *oldname, int newdfd, { struct dentry *new_dentry; struct path old_path, new_path; + struct qstr new_last; + int new_type; struct inode *delegated_inode = NULL; - int how = 0; + struct filename *to; + unsigned int how = 0, target_flags; + bool should_retry = false; int error; - if ((flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0) + if ((flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH | + AT_LINK_REPLACE)) != 0) return -EINVAL; /* * To use null names we require CAP_DAC_READ_SEARCH @@ -4229,44 +4273,94 @@ int do_linkat(int olddfd, const char __user *oldname, int newdfd, if (flags & AT_SYMLINK_FOLLOW) how |= LOOKUP_FOLLOW; + + if (flags & AT_LINK_REPLACE) + target_flags = LOOKUP_RENAME_TARGET; + else + target_flags = LOOKUP_CREATE | LOOKUP_EXCL; retry: error = user_path_at(olddfd, oldname, how, &old_path); if (error) return error; - new_dentry = user_path_create(newdfd, newname, &new_path, - (how & LOOKUP_REVAL)); - error = PTR_ERR(new_dentry); - if (IS_ERR(new_dentry)) - goto out; + to = filename_parentat(newdfd, getname(newname), how & LOOKUP_REVAL, + &new_path, &new_last, &new_type); + if (IS_ERR(to)) { + error = PTR_ERR(to); + goto exit1; + } + + if (old_path.mnt != new_path.mnt) { + error = -EXDEV; + goto exit2; + } + + if (new_type != LAST_NORM) { + if (flags & AT_LINK_REPLACE) + error = -EISDIR; + else + error = -EEXIST; + goto exit2; + } + + error = mnt_want_write(old_path.mnt); + if (error) + goto exit2; + +retry_deleg: + inode_lock_nested(new_path.dentry->d_inode, I_MUTEX_PARENT); + + new_dentry = __lookup_hash(&new_last, new_path.dentry, + (how & LOOKUP_REVAL) | target_flags); + if (IS_ERR(new_dentry)) { + error = PTR_ERR(new_dentry); + goto exit3; + } + if (!(flags & AT_LINK_REPLACE) && d_is_positive(new_dentry)) { + error = -EEXIST; + goto exit4; + } + if (new_last.name[new_last.len]) { + if (d_is_negative(new_dentry)) { + error = -ENOENT; + goto exit4; + } + if (!d_is_dir(old_path.dentry)) { + error = -ENOTDIR; + goto exit4; + } + } - error = -EXDEV; - if (old_path.mnt != new_path.mnt) - goto out_dput; error = may_linkat(&old_path); if (unlikely(error)) - goto out_dput; + goto exit4; error = security_path_link(old_path.dentry, &new_path, new_dentry); if (error) - goto out_dput; - error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode); -out_dput: - done_path_create(&new_path, new_dentry); + goto exit4; + error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, + &delegated_inode, flags & AT_LINK_REPLACE); +exit4: + dput(new_dentry); +exit3: + inode_unlock(new_path.dentry->d_inode); if (delegated_inode) { error = break_deleg_wait(&delegated_inode); - if (!error) { - path_put(&old_path); - goto retry; - } + if (!error) + goto retry_deleg; } - if (retry_estale(error, how)) { - path_put(&old_path); + mnt_drop_write(old_path.mnt); +exit2: + if (retry_estale(error, how)) + should_retry = true; + path_put(&new_path); + putname(to); +exit1: + path_put(&old_path); + if (should_retry) { + should_retry = false; how |= LOOKUP_REVAL; goto retry; } -out: - path_put(&old_path); - return error; } diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index c0dc491537a6..3f9291e76b99 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1598,7 +1598,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, err = nfserr_noent; if (d_really_is_negative(dold)) goto out_dput; - host_err = vfs_link(dold, dirp, dnew, NULL); + host_err = vfs_link(dold, dirp, dnew, NULL, 0); if (!host_err) { err = nfserrno(commit_metadata(ffhp)); if (!err) diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index f283b1d69a9e..b199fc03c891 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -120,7 +120,7 @@ static inline int ovl_do_unlink(struct inode *dir, struct dentry *dentry) static inline int ovl_do_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { - int err = vfs_link(old_dentry, dir, new_dentry, NULL); + int err = vfs_link(old_dentry, dir, new_dentry, NULL, 0); pr_debug("link(%pd2, %pd2) = %i\n", old_dentry, new_dentry, err); return err; diff --git a/include/linux/fs.h b/include/linux/fs.h index 3bdb71c97e8f..93eb90eb1fdb 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1712,7 +1712,7 @@ extern int vfs_create(struct inode *, struct dentry *, umode_t, bool); extern int vfs_mkdir(struct inode *, struct dentry *, umode_t); extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t); extern int vfs_symlink(struct inode *, struct dentry *, const char *); -extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **); +extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **, int); extern int vfs_rmdir(struct inode *, struct dentry *); extern int vfs_unlink(struct inode *, struct dentry *, struct inode **); extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int); diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h index 1f97b33c840e..3704793cd5ab 100644 --- a/include/uapi/linux/fcntl.h +++ b/include/uapi/linux/fcntl.h @@ -99,6 +99,7 @@ #define AT_STATX_DONT_SYNC 0x4000 /* - Don't sync attributes with the server */ #define AT_RECURSIVE 0x8000 /* Apply to the entire subtree */ +#define AT_LINK_REPLACE 0x10000 /* Replace link() target */ #endif /* _UAPI_LINUX_FCNTL_H */ -- 2.25.0
WARNING: multiple messages have this Message-ID (diff)
From: Omar Sandoval <osandov@osandov.com> To: linux-fsdevel@vger.kernel.org, Al Viro <viro@zeniv.linux.org.uk> Cc: kernel-team@fb.com, linux-api@vger.kernel.org, David Howells <dhowells@redhat.com>, Amir Goldstein <amir73il@gmail.com>, Xi Wang <xi@cs.washington.edu> Subject: [RFC PATCH v4 2/4] fs: add AT_LINK_REPLACE flag for linkat() which replaces the target Date: Wed, 29 Jan 2020 00:58:32 -0800 [thread overview] Message-ID: <1f5a197a2fdb0668f6dce8b9a4403481bc957a7c.1580251857.git.osandov@fb.com> (raw) Message-ID: <20200129085832.Y5Auf2LNF836b5T3m1eempvyax3uB7WTw4zWJpEAtRI@z> (raw) In-Reply-To: <cover.1580251857.git.osandov@fb.com> From: Omar Sandoval <osandov@fb.com> One of the most common uses of temporary files is the classic atomic replacement pattern, i.e., - write temporary file - fsync temporary file - rename temporary file over real file - fsync parent directory Now, we have O_TMPFILE, which gives us a much better way to create temporary files, but it's not possible to use it for this pattern. This patch introduces an AT_LINK_REPLACE flag which allows linkat() to replace the target file. Now, the temporary file in the pattern above can be a proper O_TMPFILE. Even without O_TMPFILE, this is a new primitive which might be useful in other contexts. The implementation on the VFS side mimics sys_renameat2(). Cc: Xi Wang <xi@cs.washington.edu> Signed-off-by: Omar Sandoval <osandov@fb.com> --- fs/ecryptfs/inode.c | 2 +- fs/namei.c | 166 +++++++++++++++++++++++++++++-------- fs/nfsd/vfs.c | 2 +- fs/overlayfs/overlayfs.h | 2 +- include/linux/fs.h | 2 +- include/uapi/linux/fcntl.h | 1 + 6 files changed, 135 insertions(+), 40 deletions(-) diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index eeb351b220b2..2f36b7a61a2f 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -440,7 +440,7 @@ static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir, dget(lower_new_dentry); lower_dir_dentry = lock_parent(lower_new_dentry); rc = vfs_link(lower_old_dentry, d_inode(lower_dir_dentry), - lower_new_dentry, NULL); + lower_new_dentry, NULL, 0); if (rc || d_really_is_negative(lower_new_dentry)) goto out_lock; rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb); diff --git a/fs/namei.c b/fs/namei.c index 9d690df17aed..78d364e99dca 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4122,6 +4122,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn * @dir: new parent * @new_dentry: where to create the new link * @delegated_inode: returns inode needing a delegation break + * @flags: link flags * * The caller must hold dir->i_mutex * @@ -4135,16 +4136,25 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn * be appropriate for callers that expect the underlying filesystem not * to be NFS exported. */ -int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode) +int vfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry, struct inode **delegated_inode, + int flags) { struct inode *inode = old_dentry->d_inode; + struct inode *target = new_dentry->d_inode; unsigned max_links = dir->i_sb->s_max_links; int error; if (!inode) return -ENOENT; - error = may_create(dir, new_dentry); + if (target) { + if (inode == target) + return 0; + error = may_delete(dir, new_dentry, false); + } else { + error = may_create(dir, new_dentry); + } if (error) return error; @@ -4172,26 +4182,55 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de if (error) return error; - inode_lock(inode); + dget(new_dentry); + lock_two_nondirectories(inode, target); + + if (is_local_mountpoint(new_dentry)) { + error = -EBUSY; + goto out; + } + /* Make sure we don't allow creating hardlink to an unlinked file */ - if (inode->i_nlink == 0 && !(inode->i_state & I_LINKABLE)) + if (inode->i_nlink == 0 && !(inode->i_state & I_LINKABLE)) { error = -ENOENT; - else if (max_links && inode->i_nlink >= max_links) + goto out; + } + if (max_links && inode->i_nlink >= max_links) { error = -EMLINK; - else { - error = try_break_deleg(inode, delegated_inode); - if (!error) - error = dir->i_op->link(old_dentry, dir, new_dentry, 0); + goto out; + } + + error = try_break_deleg(inode, delegated_inode); + if (error) + goto out; + if (target) { + error = try_break_deleg(target, delegated_inode); + if (error) + goto out; + } + + error = dir->i_op->link(old_dentry, dir, new_dentry, flags); + if (error) + goto out; + + if (target) { + dont_mount(new_dentry); + detach_mounts(new_dentry); } - if (!error && (inode->i_state & I_LINKABLE)) { + if (inode->i_state & I_LINKABLE) { spin_lock(&inode->i_lock); inode->i_state &= ~I_LINKABLE; spin_unlock(&inode->i_lock); } - inode_unlock(inode); - if (!error) +out: + unlock_two_nondirectories(inode, target); + dput(new_dentry); + if (!error) { + if (target) + fsnotify_link_count(target); fsnotify_link(dir, inode, new_dentry); + } return error; } EXPORT_SYMBOL(vfs_link); @@ -4210,11 +4249,16 @@ int do_linkat(int olddfd, const char __user *oldname, int newdfd, { struct dentry *new_dentry; struct path old_path, new_path; + struct qstr new_last; + int new_type; struct inode *delegated_inode = NULL; - int how = 0; + struct filename *to; + unsigned int how = 0, target_flags; + bool should_retry = false; int error; - if ((flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0) + if ((flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH | + AT_LINK_REPLACE)) != 0) return -EINVAL; /* * To use null names we require CAP_DAC_READ_SEARCH @@ -4229,44 +4273,94 @@ int do_linkat(int olddfd, const char __user *oldname, int newdfd, if (flags & AT_SYMLINK_FOLLOW) how |= LOOKUP_FOLLOW; + + if (flags & AT_LINK_REPLACE) + target_flags = LOOKUP_RENAME_TARGET; + else + target_flags = LOOKUP_CREATE | LOOKUP_EXCL; retry: error = user_path_at(olddfd, oldname, how, &old_path); if (error) return error; - new_dentry = user_path_create(newdfd, newname, &new_path, - (how & LOOKUP_REVAL)); - error = PTR_ERR(new_dentry); - if (IS_ERR(new_dentry)) - goto out; + to = filename_parentat(newdfd, getname(newname), how & LOOKUP_REVAL, + &new_path, &new_last, &new_type); + if (IS_ERR(to)) { + error = PTR_ERR(to); + goto exit1; + } + + if (old_path.mnt != new_path.mnt) { + error = -EXDEV; + goto exit2; + } + + if (new_type != LAST_NORM) { + if (flags & AT_LINK_REPLACE) + error = -EISDIR; + else + error = -EEXIST; + goto exit2; + } + + error = mnt_want_write(old_path.mnt); + if (error) + goto exit2; + +retry_deleg: + inode_lock_nested(new_path.dentry->d_inode, I_MUTEX_PARENT); + + new_dentry = __lookup_hash(&new_last, new_path.dentry, + (how & LOOKUP_REVAL) | target_flags); + if (IS_ERR(new_dentry)) { + error = PTR_ERR(new_dentry); + goto exit3; + } + if (!(flags & AT_LINK_REPLACE) && d_is_positive(new_dentry)) { + error = -EEXIST; + goto exit4; + } + if (new_last.name[new_last.len]) { + if (d_is_negative(new_dentry)) { + error = -ENOENT; + goto exit4; + } + if (!d_is_dir(old_path.dentry)) { + error = -ENOTDIR; + goto exit4; + } + } - error = -EXDEV; - if (old_path.mnt != new_path.mnt) - goto out_dput; error = may_linkat(&old_path); if (unlikely(error)) - goto out_dput; + goto exit4; error = security_path_link(old_path.dentry, &new_path, new_dentry); if (error) - goto out_dput; - error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode); -out_dput: - done_path_create(&new_path, new_dentry); + goto exit4; + error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, + &delegated_inode, flags & AT_LINK_REPLACE); +exit4: + dput(new_dentry); +exit3: + inode_unlock(new_path.dentry->d_inode); if (delegated_inode) { error = break_deleg_wait(&delegated_inode); - if (!error) { - path_put(&old_path); - goto retry; - } + if (!error) + goto retry_deleg; } - if (retry_estale(error, how)) { - path_put(&old_path); + mnt_drop_write(old_path.mnt); +exit2: + if (retry_estale(error, how)) + should_retry = true; + path_put(&new_path); + putname(to); +exit1: + path_put(&old_path); + if (should_retry) { + should_retry = false; how |= LOOKUP_REVAL; goto retry; } -out: - path_put(&old_path); - return error; } diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index c0dc491537a6..3f9291e76b99 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1598,7 +1598,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, err = nfserr_noent; if (d_really_is_negative(dold)) goto out_dput; - host_err = vfs_link(dold, dirp, dnew, NULL); + host_err = vfs_link(dold, dirp, dnew, NULL, 0); if (!host_err) { err = nfserrno(commit_metadata(ffhp)); if (!err) diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index f283b1d69a9e..b199fc03c891 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -120,7 +120,7 @@ static inline int ovl_do_unlink(struct inode *dir, struct dentry *dentry) static inline int ovl_do_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { - int err = vfs_link(old_dentry, dir, new_dentry, NULL); + int err = vfs_link(old_dentry, dir, new_dentry, NULL, 0); pr_debug("link(%pd2, %pd2) = %i\n", old_dentry, new_dentry, err); return err; diff --git a/include/linux/fs.h b/include/linux/fs.h index 3bdb71c97e8f..93eb90eb1fdb 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1712,7 +1712,7 @@ extern int vfs_create(struct inode *, struct dentry *, umode_t, bool); extern int vfs_mkdir(struct inode *, struct dentry *, umode_t); extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t); extern int vfs_symlink(struct inode *, struct dentry *, const char *); -extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **); +extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **, int); extern int vfs_rmdir(struct inode *, struct dentry *); extern int vfs_unlink(struct inode *, struct dentry *, struct inode **); extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int); diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h index 1f97b33c840e..3704793cd5ab 100644 --- a/include/uapi/linux/fcntl.h +++ b/include/uapi/linux/fcntl.h @@ -99,6 +99,7 @@ #define AT_STATX_DONT_SYNC 0x4000 /* - Don't sync attributes with the server */ #define AT_RECURSIVE 0x8000 /* Apply to the entire subtree */ +#define AT_LINK_REPLACE 0x10000 /* Replace link() target */ #endif /* _UAPI_LINUX_FCNTL_H */ -- 2.25.0
next prev parent reply other threads:[~2020-01-28 23:19 UTC|newest] Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top 2020-01-28 23:18 [RFC PATCH v4 0/4] fs: add flag to linkat() for replacing destination Omar Sandoval 2020-01-29 8:58 ` Omar Sandoval 2020-01-28 23:18 ` [RFC PATCH xfstests] generic: add smoke test for AT_LINK_REPLACE Omar Sandoval 2020-01-29 8:58 ` Omar Sandoval 2020-01-29 7:02 ` Zorro Lang 2020-02-23 14:46 ` Eryu Guan 2020-01-28 23:18 ` [RFC PATCH man-pages] link.2: Document new AT_LINK_REPLACE flag Omar Sandoval 2020-01-29 8:58 ` Omar Sandoval 2020-01-28 23:18 ` [RFC PATCH xfsprogs] xfs_io: add support for linkat() AT_LINK_REPLACE Omar Sandoval 2020-01-29 8:58 ` Omar Sandoval 2020-01-30 4:42 ` Zorro Lang 2020-01-28 23:19 ` [RFC PATCH v4 1/4] fs: add flags argument to i_op->link() Omar Sandoval 2020-01-29 8:58 ` Omar Sandoval 2020-01-28 23:19 ` Omar Sandoval [this message] 2020-01-29 8:58 ` [RFC PATCH v4 2/4] fs: add AT_LINK_REPLACE flag for linkat() which replaces the target Omar Sandoval 2020-01-28 23:19 ` [RFC PATCH v4 3/4] Btrfs: fix inode reference count leak in btrfs_link() error path Omar Sandoval 2020-01-29 8:58 ` Omar Sandoval 2020-01-28 23:19 ` [RFC PATCH v4 4/4] Btrfs: add support for linkat() AT_REPLACE Omar Sandoval 2020-01-29 8:58 ` Omar Sandoval 2020-01-29 8:58 ` [RFC PATCH xfstests] generic: add smoke test for AT_LINK_REPLACE Omar Sandoval [not found] ` <cover.1580251857.git.osandov-b10kYP2dOMg@public.gmane.org> 2020-01-29 8:58 ` Omar Sandoval 2020-01-29 8:58 ` [RFC PATCH man-pages] link.2: Document new AT_LINK_REPLACE flag Omar Sandoval 2020-01-29 8:58 ` [RFC PATCH xfsprogs] xfs_io: add support for linkat() AT_LINK_REPLACE Omar Sandoval 2020-01-29 8:58 ` [RFC PATCH v4 0/4] fs: add flag to linkat() for replacing destination Omar Sandoval 2020-01-29 8:58 ` [RFC PATCH v4 1/4] fs: add flags argument to i_op->link() Omar Sandoval 2020-01-29 8:58 ` [RFC PATCH v4 2/4] fs: add AT_LINK_REPLACE flag for linkat() which replaces the target Omar Sandoval 2020-01-29 8:58 ` [RFC PATCH v4 3/4] Btrfs: fix inode reference count leak in btrfs_link() error path Omar Sandoval 2020-01-29 8:58 ` [RFC PATCH v4 4/4] Btrfs: add support for linkat() AT_REPLACE Omar Sandoval 2020-01-31 13:48 ` [RFC PATCH v4 1/4] fs: add flags argument to i_op->link() David Howells 2020-01-31 20:24 ` Omar Sandoval 2020-01-31 20:24 ` Omar Sandoval
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=1f5a197a2fdb0668f6dce8b9a4403481bc957a7c.1580251857.git.osandov@fb.com \ --to=osandov@osandov.com \ --cc=kernel-team@fb.com \ --cc=linux-fsdevel@vger.kernel.org \ --cc=viro@zeniv.linux.org.uk \ --cc=xi@cs.washington.edu \ /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: linkBe 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.