Commit 520c8b16 authored by Miklos Szeredi's avatar Miklos Szeredi

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: default avatarMiklos Szeredi <mszeredi@suse.cz>
Reviewed-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent bc27027a
...@@ -47,6 +47,8 @@ prototypes: ...@@ -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 ...@@ -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 ...@@ -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.
......
...@@ -347,6 +347,8 @@ struct inode_operations { ...@@ -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. ...@@ -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
......
...@@ -322,6 +322,7 @@ ...@@ -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
......
...@@ -105,8 +105,8 @@ static inline void ll_set_fs_pwd(struct fs_struct *fs, struct vfsmount *mnt, ...@@ -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) \ #define ll_vfs_rename(old, old_dir, mnt, new, new_dir, mnt1) \
vfs_rename(old,old_dir,new,new_dir,delegated_inode) 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))
......
...@@ -223,7 +223,7 @@ int lustre_rename(struct dentry *dir, struct vfsmount *mnt, ...@@ -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:
......
...@@ -396,7 +396,7 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache, ...@@ -396,7 +396,7 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache,
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);
......
...@@ -641,7 +641,7 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -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)
......
...@@ -3980,6 +3980,7 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname ...@@ -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 ...@@ -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, ...@@ -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, ...@@ -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 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -4118,8 +4128,8 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
return error; return error;
} }
SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname, SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
int, newdfd, const char __user *, newname) 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, ...@@ -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 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname, ...@@ -4202,8 +4216,8 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
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, new_dir->d_inode, new_dentry,
&delegated_inode); &delegated_inode, flags);
exit5: exit5:
dput(new_dentry); dput(new_dentry);
exit4: exit4:
...@@ -4233,9 +4247,15 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname, ...@@ -4233,9 +4247,15 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
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)
......
...@@ -1694,7 +1694,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, ...@@ -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)
......
...@@ -1460,7 +1460,7 @@ extern int vfs_symlink(struct inode *, struct dentry *, const char *); ...@@ -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 { ...@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment