mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-18 22:14:16 +00:00
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>
This commit is contained in:
parent
bc27027a73
commit
520c8b1650
10 changed files with 58 additions and 15 deletions
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
#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,
|
||||||
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 @@ 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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
34
fs/namei.c
34
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,
|
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,
|
||||||
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,
|
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 @@ 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)
|
||||||
|
|
|
@ -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 *);
|
||||||
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);
|
||||||
|
|
Loading…
Add table
Reference in a new issue