浏览代码

vfs: add renameat2 syscall

Add new renameat2 syscall, which is the same as renameat with an added
flags argument.

Pass flags to vfs_rename() and to i_op->rename() as well.

Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
Miklos Szeredi 11 年之前
父节点
当前提交
520c8b1650

+ 5 - 1
Documentation/filesystems/Locking

@@ -47,6 +47,8 @@ prototypes:
 	int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
 	int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
 	int (*rename) (struct inode *, struct dentry *,
 	int (*rename) (struct inode *, struct dentry *,
 			struct inode *, struct dentry *);
 			struct inode *, struct dentry *);
+	int (*rename2) (struct inode *, struct dentry *,
+			struct inode *, struct dentry *, unsigned int);
 	int (*readlink) (struct dentry *, char __user *,int);
 	int (*readlink) (struct dentry *, char __user *,int);
 	void * (*follow_link) (struct dentry *, struct nameidata *);
 	void * (*follow_link) (struct dentry *, struct nameidata *);
 	void (*put_link) (struct dentry *, struct nameidata *, void *);
 	void (*put_link) (struct dentry *, struct nameidata *, void *);
@@ -78,6 +80,7 @@ mkdir:		yes
 unlink:		yes (both)
 unlink:		yes (both)
 rmdir:		yes (both)	(see below)
 rmdir:		yes (both)	(see below)
 rename:		yes (all)	(see below)
 rename:		yes (all)	(see below)
+rename2:	yes (all)	(see below)
 readlink:	no
 readlink:	no
 follow_link:	no
 follow_link:	no
 put_link:	no
 put_link:	no
@@ -96,7 +99,8 @@ tmpfile:	no
 
 
 	Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on
 	Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on
 victim.
 victim.
-	cross-directory ->rename() has (per-superblock) ->s_vfs_rename_sem.
+	cross-directory ->rename() and rename2() has (per-superblock)
+->s_vfs_rename_sem.
 
 
 See Documentation/filesystems/directory-locking for more detailed discussion
 See Documentation/filesystems/directory-locking for more detailed discussion
 of the locking scheme for directory operations.
 of the locking scheme for directory operations.

+ 16 - 0
Documentation/filesystems/vfs.txt

@@ -347,6 +347,8 @@ struct inode_operations {
 	int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
 	int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
 	int (*rename) (struct inode *, struct dentry *,
 	int (*rename) (struct inode *, struct dentry *,
 			struct inode *, struct dentry *);
 			struct inode *, struct dentry *);
+	int (*rename2) (struct inode *, struct dentry *,
+			struct inode *, struct dentry *, unsigned int);
 	int (*readlink) (struct dentry *, char __user *,int);
 	int (*readlink) (struct dentry *, char __user *,int);
         void * (*follow_link) (struct dentry *, struct nameidata *);
         void * (*follow_link) (struct dentry *, struct nameidata *);
         void (*put_link) (struct dentry *, struct nameidata *, void *);
         void (*put_link) (struct dentry *, struct nameidata *, void *);
@@ -414,6 +416,20 @@ otherwise noted.
   rename: called by the rename(2) system call to rename the object to
   rename: called by the rename(2) system call to rename the object to
 	have the parent and name given by the second inode and dentry.
 	have the parent and name given by the second inode and dentry.
 
 
+  rename2: this has an additional flags argument compared to rename.
+	If no flags are supported by the filesystem then this method
+	need not be implemented.  If some flags are supported then the
+	filesystem must return -EINVAL for any unsupported or unknown
+	flags.  Currently the following flags are implemented:
+	(1) RENAME_NOREPLACE: this flag indicates that if the target
+	of the rename exists the rename should fail with -EEXIST
+	instead of replacing the target.  The VFS already checks for
+	existence, so for local filesystems the RENAME_NOREPLACE
+	implementation is equivalent to plain rename.
+	(2) RENAME_EXCHANGE: exchange source and target.  Both must
+	exist; this is checked by the VFS.  Unlike plain rename,
+	source and target may be of different type.
+
   readlink: called by the readlink(2) system call. Only required if
   readlink: called by the readlink(2) system call. Only required if
 	you want to support reading symbolic links
 	you want to support reading symbolic links
 
 

+ 1 - 0
arch/x86/syscalls/syscall_64.tbl

@@ -322,6 +322,7 @@
 313	common	finit_module		sys_finit_module
 313	common	finit_module		sys_finit_module
 314	common	sched_setattr		sys_sched_setattr
 314	common	sched_setattr		sys_sched_setattr
 315	common	sched_getattr		sys_sched_getattr
 315	common	sched_getattr		sys_sched_getattr
+316	common	renameat2		sys_renameat2
 
 
 #
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
 # x32-specific system call numbers start at 512 to avoid cache impact

+ 2 - 2
drivers/staging/lustre/lustre/include/linux/lustre_compat25.h

@@ -105,8 +105,8 @@ static inline void ll_set_fs_pwd(struct fs_struct *fs, struct vfsmount *mnt,
 #define ll_vfs_unlink(inode,entry,mnt)	  vfs_unlink(inode,entry)
 #define ll_vfs_unlink(inode,entry,mnt)	  vfs_unlink(inode,entry)
 #define ll_vfs_mknod(dir,entry,mnt,mode,dev)    vfs_mknod(dir,entry,mode,dev)
 #define ll_vfs_mknod(dir,entry,mnt,mode,dev)    vfs_mknod(dir,entry,mode,dev)
 #define ll_security_inode_unlink(dir,entry,mnt) security_inode_unlink(dir,entry)
 #define ll_security_inode_unlink(dir,entry,mnt) security_inode_unlink(dir,entry)
-#define ll_vfs_rename(old,old_dir,mnt,new,new_dir,mnt1,delegated_inode) \
-		vfs_rename(old,old_dir,new,new_dir,delegated_inode)
+#define ll_vfs_rename(old, old_dir, mnt, new, new_dir, mnt1) \
+		vfs_rename(old, old_dir, new, new_dir, NULL, 0)
 
 
 #define cfs_bio_io_error(a,b)   bio_io_error((a))
 #define cfs_bio_io_error(a,b)   bio_io_error((a))
 #define cfs_bio_endio(a,b,c)    bio_endio((a),(c))
 #define cfs_bio_endio(a,b,c)    bio_endio((a),(c))

+ 1 - 1
drivers/staging/lustre/lustre/lvfs/lvfs_linux.c

@@ -223,7 +223,7 @@ int lustre_rename(struct dentry *dir, struct vfsmount *mnt,
 		GOTO(put_old, err = PTR_ERR(dchild_new));
 		GOTO(put_old, err = PTR_ERR(dchild_new));
 
 
 	err = ll_vfs_rename(dir->d_inode, dchild_old, mnt,
 	err = ll_vfs_rename(dir->d_inode, dchild_old, mnt,
-			    dir->d_inode, dchild_new, mnt, NULL);
+			    dir->d_inode, dchild_new, mnt);
 
 
 	dput(dchild_new);
 	dput(dchild_new);
 put_old:
 put_old:

+ 1 - 1
fs/cachefiles/namei.c

@@ -396,7 +396,7 @@ try_again:
 		cachefiles_io_error(cache, "Rename security error %d", ret);
 		cachefiles_io_error(cache, "Rename security error %d", ret);
 	} else {
 	} else {
 		ret = vfs_rename(dir->d_inode, rep,
 		ret = vfs_rename(dir->d_inode, rep,
-				 cache->graveyard->d_inode, grave, NULL);
+				 cache->graveyard->d_inode, grave, NULL, 0);
 		if (ret != 0 && ret != -ENOMEM)
 		if (ret != 0 && ret != -ENOMEM)
 			cachefiles_io_error(cache,
 			cachefiles_io_error(cache,
 					    "Rename failed with error %d", ret);
 					    "Rename failed with error %d", ret);

+ 1 - 1
fs/ecryptfs/inode.c

@@ -641,7 +641,7 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 	}
 	}
 	rc = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
 	rc = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
 			lower_new_dir_dentry->d_inode, lower_new_dentry,
 			lower_new_dir_dentry->d_inode, lower_new_dentry,
-			NULL);
+			NULL, 0);
 	if (rc)
 	if (rc)
 		goto out_lock;
 		goto out_lock;
 	if (target_inode)
 	if (target_inode)

+ 27 - 7
fs/namei.c

@@ -3980,6 +3980,7 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
  * @new_dir:	parent of destination
  * @new_dir:	parent of destination
  * @new_dentry:	destination
  * @new_dentry:	destination
  * @delegated_inode: returns an inode needing a delegation break
  * @delegated_inode: returns an inode needing a delegation break
+ * @flags:	rename flags
  *
  *
  * The caller must hold multiple mutexes--see lock_rename()).
  * The caller must hold multiple mutexes--see lock_rename()).
  *
  *
@@ -4023,7 +4024,7 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
  */
  */
 int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 	       struct inode *new_dir, struct dentry *new_dentry,
 	       struct inode *new_dir, struct dentry *new_dentry,
-	       struct inode **delegated_inode)
+	       struct inode **delegated_inode, unsigned int flags)
 {
 {
 	int error;
 	int error;
 	bool is_dir = d_is_dir(old_dentry);
 	bool is_dir = d_is_dir(old_dentry);
@@ -4048,6 +4049,9 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 	if (!old_dir->i_op->rename)
 	if (!old_dir->i_op->rename)
 		return -EPERM;
 		return -EPERM;
 
 
+	if (flags && !old_dir->i_op->rename2)
+		return -EINVAL;
+
 	/*
 	/*
 	 * If we are going to change the parent - check write permissions,
 	 * If we are going to change the parent - check write permissions,
 	 * we'll need to flip '..'.
 	 * we'll need to flip '..'.
@@ -4093,7 +4097,13 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 				goto out;
 				goto out;
 		}
 		}
 	}
 	}
-	error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+	if (!flags) {
+		error = old_dir->i_op->rename(old_dir, old_dentry,
+					      new_dir, new_dentry);
+	} else {
+		error = old_dir->i_op->rename2(old_dir, old_dentry,
+					       new_dir, new_dentry, flags);
+	}
 	if (error)
 	if (error)
 		goto out;
 		goto out;
 
 
@@ -4118,8 +4128,8 @@ out:
 	return error;
 	return error;
 }
 }
 
 
-SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
-		int, newdfd, const char __user *, newname)
+SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
+		int, newdfd, const char __user *, newname, unsigned int, flags)
 {
 {
 	struct dentry *old_dir, *new_dir;
 	struct dentry *old_dir, *new_dir;
 	struct dentry *old_dentry, *new_dentry;
 	struct dentry *old_dentry, *new_dentry;
@@ -4131,6 +4141,10 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
 	unsigned int lookup_flags = 0;
 	unsigned int lookup_flags = 0;
 	bool should_retry = false;
 	bool should_retry = false;
 	int error;
 	int error;
+
+	if (flags)
+		return -EINVAL;
+
 retry:
 retry:
 	from = user_path_parent(olddfd, oldname, &oldnd, lookup_flags);
 	from = user_path_parent(olddfd, oldname, &oldnd, lookup_flags);
 	if (IS_ERR(from)) {
 	if (IS_ERR(from)) {
@@ -4202,8 +4216,8 @@ retry_deleg:
 	if (error)
 	if (error)
 		goto exit5;
 		goto exit5;
 	error = vfs_rename(old_dir->d_inode, old_dentry,
 	error = vfs_rename(old_dir->d_inode, old_dentry,
-				   new_dir->d_inode, new_dentry,
-				   &delegated_inode);
+			   new_dir->d_inode, new_dentry,
+			   &delegated_inode, flags);
 exit5:
 exit5:
 	dput(new_dentry);
 	dput(new_dentry);
 exit4:
 exit4:
@@ -4233,9 +4247,15 @@ exit:
 	return error;
 	return error;
 }
 }
 
 
+SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
+		int, newdfd, const char __user *, newname)
+{
+	return sys_renameat2(olddfd, oldname, newdfd, newname, 0);
+}
+
 SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newname)
 SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newname)
 {
 {
-	return sys_renameat(AT_FDCWD, oldname, AT_FDCWD, newname);
+	return sys_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
 }
 }
 
 
 int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen, const char *link)
 int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen, const char *link)

+ 1 - 1
fs/nfsd/vfs.c

@@ -1694,7 +1694,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
 	if (ffhp->fh_export->ex_path.dentry != tfhp->fh_export->ex_path.dentry)
 	if (ffhp->fh_export->ex_path.dentry != tfhp->fh_export->ex_path.dentry)
 		goto out_dput_new;
 		goto out_dput_new;
 
 
-	host_err = vfs_rename(fdir, odentry, tdir, ndentry, NULL);
+	host_err = vfs_rename(fdir, odentry, tdir, ndentry, NULL, 0);
 	if (!host_err) {
 	if (!host_err) {
 		host_err = commit_metadata(tfhp);
 		host_err = commit_metadata(tfhp);
 		if (!host_err)
 		if (!host_err)

+ 3 - 1
include/linux/fs.h

@@ -1460,7 +1460,7 @@ 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 **);
 extern int vfs_rmdir(struct inode *, struct dentry *);
 extern int vfs_rmdir(struct inode *, struct dentry *);
 extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
 extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
-extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **);
+extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
 
 
 /*
 /*
  * VFS dentry helper functions.
  * VFS dentry helper functions.
@@ -1571,6 +1571,8 @@ struct inode_operations {
 	int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
 	int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
 	int (*rename) (struct inode *, struct dentry *,
 	int (*rename) (struct inode *, struct dentry *,
 			struct inode *, struct dentry *);
 			struct inode *, struct dentry *);
+	int (*rename2) (struct inode *, struct dentry *,
+			struct inode *, struct dentry *, unsigned int);
 	int (*setattr) (struct dentry *, struct iattr *);
 	int (*setattr) (struct dentry *, struct iattr *);
 	int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
 	int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
 	int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
 	int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);