vfs: add renameat2 syscall
authorMiklos Szeredi <mszeredi@suse.cz>
Tue, 1 Apr 2014 15:08:42 +0000 (17:08 +0200)
committerMiklos Szeredi <mszeredi@suse.cz>
Tue, 1 Apr 2014 15:08:42 +0000 (17:08 +0200)
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>
Documentation/filesystems/Locking
Documentation/filesystems/vfs.txt
arch/x86/syscalls/syscall_64.tbl
drivers/staging/lustre/lustre/include/linux/lustre_compat25.h
drivers/staging/lustre/lustre/lvfs/lvfs_linux.c
fs/cachefiles/namei.c
fs/ecryptfs/inode.c
fs/namei.c
fs/nfsd/vfs.c
include/linux/fs.h

index 5b0c083d7c0e98eccfb315cf6b5cd72f8e73d7e5..f424e0e5b46bb4b2668f33a74ebf5624a92fb6ee 100644 (file)
@@ -47,6 +47,8 @@ prototypes:
        int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
        int (*rename) (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);
        void * (*follow_link) (struct dentry *, struct nameidata *);
        void (*put_link) (struct dentry *, struct nameidata *, void *);
@@ -78,6 +80,7 @@ mkdir:                yes
 unlink:                yes (both)
 rmdir:         yes (both)      (see below)
 rename:                yes (all)       (see below)
+rename2:       yes (all)       (see below)
 readlink:      no
 follow_link:   no
 put_link:      no
@@ -96,7 +99,8 @@ tmpfile:      no
 
        Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on
 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
 of the locking scheme for directory operations.
index c53784c119c8ea29a532c4e0d9460dd4801690fd..94eb86287bcb08f3ebc0fa826438fed1af8ded1a 100644 (file)
@@ -347,6 +347,8 @@ struct inode_operations {
        int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
        int (*rename) (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);
         void * (*follow_link) (struct dentry *, struct nameidata *);
         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
        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
        you want to support reading symbolic links
 
index a12bddc7ccea11edf9f7125d8353f04759d2cfb0..04376ac3d9efb9f8d73f4d096bea692d891c635b 100644 (file)
 313    common  finit_module            sys_finit_module
 314    common  sched_setattr           sys_sched_setattr
 315    common  sched_getattr           sys_sched_getattr
+316    common  renameat2               sys_renameat2
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
index eefdb8d061b16f6505fc9179665354d7cac000c8..81cc7a0134bb6693e7c5fd9a97ef7085d88943dd 100644 (file)
@@ -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_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_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_endio(a,b,c)    bio_endio((a),(c))
index 428ffd8c37b7360f19a24e42bdca16969c7642a7..d50822be32309d30a828c54ab685ca6a4bee023f 100644 (file)
@@ -223,7 +223,7 @@ int lustre_rename(struct dentry *dir, struct vfsmount *mnt,
                GOTO(put_old, err = PTR_ERR(dchild_new));
 
        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);
 put_old:
index ca65f39dc8dc38823bb4a6070c717671acffba72..31088a9693519a7944fb950eed0c951a8b94023a 100644 (file)
@@ -396,7 +396,7 @@ try_again:
                cachefiles_io_error(cache, "Rename security error %d", ret);
        } else {
                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)
                        cachefiles_io_error(cache,
                                            "Rename failed with error %d", ret);
index b167ca48b8eef4984dec79a609644012421e6fea..d4a9431ec73ce0cb215fc3b6e52727b4059f9528 100644 (file)
@@ -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,
                        lower_new_dir_dentry->d_inode, lower_new_dentry,
-                       NULL);
+                       NULL, 0);
        if (rc)
                goto out_lock;
        if (target_inode)
index 12b8f56ba94282b779618f2706d48095df85d814..ab4e48c4a80a74765910b244b8fad395d770514b 100644 (file)
@@ -3980,6 +3980,7 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
  * @new_dir:   parent of destination
  * @new_dentry:        destination
  * @delegated_inode: returns an inode needing a delegation break
+ * @flags:     rename flags
  *
  * 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,
               struct inode *new_dir, struct dentry *new_dentry,
-              struct inode **delegated_inode)
+              struct inode **delegated_inode, unsigned int flags)
 {
        int error;
        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)
                return -EPERM;
 
+       if (flags && !old_dir->i_op->rename2)
+               return -EINVAL;
+
        /*
         * If we are going to change the parent - check write permissions,
         * we'll need to flip '..'.
@@ -4093,7 +4097,13 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                                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)
                goto out;
 
@@ -4118,8 +4128,8 @@ out:
        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_dentry, *new_dentry;
@@ -4131,6 +4141,10 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
        unsigned int lookup_flags = 0;
        bool should_retry = false;
        int error;
+
+       if (flags)
+               return -EINVAL;
+
 retry:
        from = user_path_parent(olddfd, oldname, &oldnd, lookup_flags);
        if (IS_ERR(from)) {
@@ -4202,8 +4216,8 @@ retry_deleg:
        if (error)
                goto exit5;
        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:
        dput(new_dentry);
 exit4:
@@ -4233,9 +4247,15 @@ exit:
        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)
 {
-       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)
index 6d7be3f8035631cb207a5733c71dbfc2f800fa4c..915808b36df76142634478b02832c90676202275 100644 (file)
@@ -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)
                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) {
                host_err = commit_metadata(tfhp);
                if (!host_err)
index 23b2a35d712efbec3e31df0cae70b69a17b81616..3b3670e97da6911a5843c6055ee09097721a351d 100644 (file)
@@ -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_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 **);
+extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
 
 /*
  * VFS dentry helper functions.
@@ -1571,6 +1571,8 @@ struct inode_operations {
        int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
        int (*rename) (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 (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
        int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);