forked from Minki/linux
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
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -322,6 +322,7 @@
|
||||
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
|
||||
|
@ -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))
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
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_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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user