diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2014-04-01 11:08:42 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2014-04-01 11:08:42 -0400 |
commit | bc27027a73e8b80376b51a1583ad1c7445605e8a (patch) | |
tree | 31ae955e5cf6bcf1fee02b4605dd0e3ef9eff5f5 | |
parent | de22a4c3720a96f1c2ebf12b0857b6db6a991f2c (diff) |
vfs: rename: use common code for dir and non-dir
There's actually very little difference between vfs_rename_dir() and
vfs_rename_other() so move both inline into vfs_rename() which still stays
reasonably readable.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r-- | fs/namei.c | 187 |
1 files changed, 75 insertions, 112 deletions
diff --git a/fs/namei.c b/fs/namei.c index 2e86d2c4ec8a..12b8f56ba942 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -3973,7 +3973,27 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname | |||
3973 | return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0); | 3973 | return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0); |
3974 | } | 3974 | } |
3975 | 3975 | ||
3976 | /* | 3976 | /** |
3977 | * vfs_rename - rename a filesystem object | ||
3978 | * @old_dir: parent of source | ||
3979 | * @old_dentry: source | ||
3980 | * @new_dir: parent of destination | ||
3981 | * @new_dentry: destination | ||
3982 | * @delegated_inode: returns an inode needing a delegation break | ||
3983 | * | ||
3984 | * The caller must hold multiple mutexes--see lock_rename()). | ||
3985 | * | ||
3986 | * If vfs_rename discovers a delegation in need of breaking at either | ||
3987 | * the source or destination, it will return -EWOULDBLOCK and return a | ||
3988 | * reference to the inode in delegated_inode. The caller should then | ||
3989 | * break the delegation and retry. Because breaking a delegation may | ||
3990 | * take a long time, the caller should drop all locks before doing | ||
3991 | * so. | ||
3992 | * | ||
3993 | * Alternatively, a caller may pass NULL for delegated_inode. This may | ||
3994 | * be appropriate for callers that expect the underlying filesystem not | ||
3995 | * to be NFS exported. | ||
3996 | * | ||
3977 | * The worst of all namespace operations - renaming directory. "Perverted" | 3997 | * The worst of all namespace operations - renaming directory. "Perverted" |
3978 | * doesn't even start to describe it. Somebody in UCB had a heck of a trip... | 3998 | * doesn't even start to describe it. Somebody in UCB had a heck of a trip... |
3979 | * Problems: | 3999 | * Problems: |
@@ -4001,19 +4021,39 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname | |||
4001 | * ->i_mutex on parents, which works but leads to some truly excessive | 4021 | * ->i_mutex on parents, which works but leads to some truly excessive |
4002 | * locking]. | 4022 | * locking]. |
4003 | */ | 4023 | */ |
4004 | static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, | 4024 | int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, |
4005 | struct inode *new_dir, struct dentry *new_dentry) | 4025 | struct inode *new_dir, struct dentry *new_dentry, |
4026 | struct inode **delegated_inode) | ||
4006 | { | 4027 | { |
4007 | int error = 0; | 4028 | int error; |
4029 | bool is_dir = d_is_dir(old_dentry); | ||
4030 | const unsigned char *old_name; | ||
4031 | struct inode *source = old_dentry->d_inode; | ||
4008 | struct inode *target = new_dentry->d_inode; | 4032 | struct inode *target = new_dentry->d_inode; |
4009 | unsigned max_links = new_dir->i_sb->s_max_links; | 4033 | |
4034 | if (source == target) | ||
4035 | return 0; | ||
4036 | |||
4037 | error = may_delete(old_dir, old_dentry, is_dir); | ||
4038 | if (error) | ||
4039 | return error; | ||
4040 | |||
4041 | if (!target) | ||
4042 | error = may_create(new_dir, new_dentry); | ||
4043 | else | ||
4044 | error = may_delete(new_dir, new_dentry, is_dir); | ||
4045 | if (error) | ||
4046 | return error; | ||
4047 | |||
4048 | if (!old_dir->i_op->rename) | ||
4049 | return -EPERM; | ||
4010 | 4050 | ||
4011 | /* | 4051 | /* |
4012 | * If we are going to change the parent - check write permissions, | 4052 | * If we are going to change the parent - check write permissions, |
4013 | * we'll need to flip '..'. | 4053 | * we'll need to flip '..'. |
4014 | */ | 4054 | */ |
4015 | if (new_dir != old_dir) { | 4055 | if (is_dir && new_dir != old_dir) { |
4016 | error = inode_permission(old_dentry->d_inode, MAY_WRITE); | 4056 | error = inode_permission(source, MAY_WRITE); |
4017 | if (error) | 4057 | if (error) |
4018 | return error; | 4058 | return error; |
4019 | } | 4059 | } |
@@ -4022,134 +4062,57 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, | |||
4022 | if (error) | 4062 | if (error) |
4023 | return error; | 4063 | return error; |
4024 | 4064 | ||
4065 | old_name = fsnotify_oldname_init(old_dentry->d_name.name); | ||
4025 | dget(new_dentry); | 4066 | dget(new_dentry); |
4026 | if (target) | 4067 | if (!is_dir) |
4068 | lock_two_nondirectories(source, target); | ||
4069 | else if (target) | ||
4027 | mutex_lock(&target->i_mutex); | 4070 | mutex_lock(&target->i_mutex); |
4028 | 4071 | ||
4029 | error = -EBUSY; | 4072 | error = -EBUSY; |
4030 | if (d_mountpoint(old_dentry) || d_mountpoint(new_dentry)) | 4073 | if (d_mountpoint(old_dentry) || d_mountpoint(new_dentry)) |
4031 | goto out; | 4074 | goto out; |
4032 | 4075 | ||
4033 | error = -EMLINK; | 4076 | if (is_dir) { |
4034 | if (max_links && !target && new_dir != old_dir && | 4077 | unsigned max_links = new_dir->i_sb->s_max_links; |
4035 | new_dir->i_nlink >= max_links) | ||
4036 | goto out; | ||
4037 | |||
4038 | if (target) | ||
4039 | shrink_dcache_parent(new_dentry); | ||
4040 | error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); | ||
4041 | if (error) | ||
4042 | goto out; | ||
4043 | |||
4044 | if (target) { | ||
4045 | target->i_flags |= S_DEAD; | ||
4046 | dont_mount(new_dentry); | ||
4047 | } | ||
4048 | if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE)) | ||
4049 | d_move(old_dentry, new_dentry); | ||
4050 | out: | ||
4051 | if (target) | ||
4052 | mutex_unlock(&target->i_mutex); | ||
4053 | dput(new_dentry); | ||
4054 | return error; | ||
4055 | } | ||
4056 | |||
4057 | static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, | ||
4058 | struct inode *new_dir, struct dentry *new_dentry, | ||
4059 | struct inode **delegated_inode) | ||
4060 | { | ||
4061 | struct inode *target = new_dentry->d_inode; | ||
4062 | struct inode *source = old_dentry->d_inode; | ||
4063 | int error; | ||
4064 | |||
4065 | error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry); | ||
4066 | if (error) | ||
4067 | return error; | ||
4068 | |||
4069 | dget(new_dentry); | ||
4070 | lock_two_nondirectories(source, target); | ||
4071 | 4078 | ||
4072 | error = -EBUSY; | 4079 | error = -EMLINK; |
4073 | if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) | 4080 | if (max_links && !target && new_dir != old_dir && |
4074 | goto out; | 4081 | new_dir->i_nlink >= max_links) |
4082 | goto out; | ||
4075 | 4083 | ||
4076 | error = try_break_deleg(source, delegated_inode); | 4084 | if (target) |
4077 | if (error) | 4085 | shrink_dcache_parent(new_dentry); |
4078 | goto out; | 4086 | } else { |
4079 | if (target) { | 4087 | error = try_break_deleg(source, delegated_inode); |
4080 | error = try_break_deleg(target, delegated_inode); | ||
4081 | if (error) | 4088 | if (error) |
4082 | goto out; | 4089 | goto out; |
4090 | if (target) { | ||
4091 | error = try_break_deleg(target, delegated_inode); | ||
4092 | if (error) | ||
4093 | goto out; | ||
4094 | } | ||
4083 | } | 4095 | } |
4084 | error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); | 4096 | error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); |
4085 | if (error) | 4097 | if (error) |
4086 | goto out; | 4098 | goto out; |
4087 | 4099 | ||
4088 | if (target) | 4100 | if (target) { |
4101 | if (is_dir) | ||
4102 | target->i_flags |= S_DEAD; | ||
4089 | dont_mount(new_dentry); | 4103 | dont_mount(new_dentry); |
4104 | } | ||
4090 | if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE)) | 4105 | if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE)) |
4091 | d_move(old_dentry, new_dentry); | 4106 | d_move(old_dentry, new_dentry); |
4092 | out: | 4107 | out: |
4093 | unlock_two_nondirectories(source, target); | 4108 | if (!is_dir) |
4109 | unlock_two_nondirectories(source, target); | ||
4110 | else if (target) | ||
4111 | mutex_unlock(&target->i_mutex); | ||
4094 | dput(new_dentry); | 4112 | dput(new_dentry); |
4095 | return error; | ||
4096 | } | ||
4097 | |||
4098 | /** | ||
4099 | * vfs_rename - rename a filesystem object | ||
4100 | * @old_dir: parent of source | ||
4101 | * @old_dentry: source | ||
4102 | * @new_dir: parent of destination | ||
4103 | * @new_dentry: destination | ||
4104 | * @delegated_inode: returns an inode needing a delegation break | ||
4105 | * | ||
4106 | * The caller must hold multiple mutexes--see lock_rename()). | ||
4107 | * | ||
4108 | * If vfs_rename discovers a delegation in need of breaking at either | ||
4109 | * the source or destination, it will return -EWOULDBLOCK and return a | ||
4110 | * reference to the inode in delegated_inode. The caller should then | ||
4111 | * break the delegation and retry. Because breaking a delegation may | ||
4112 | * take a long time, the caller should drop all locks before doing | ||
4113 | * so. | ||
4114 | * | ||
4115 | * Alternatively, a caller may pass NULL for delegated_inode. This may | ||
4116 | * be appropriate for callers that expect the underlying filesystem not | ||
4117 | * to be NFS exported. | ||
4118 | */ | ||
4119 | int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, | ||
4120 | struct inode *new_dir, struct dentry *new_dentry, | ||
4121 | struct inode **delegated_inode) | ||
4122 | { | ||
4123 | int error; | ||
4124 | int is_dir = d_is_dir(old_dentry); | ||
4125 | const unsigned char *old_name; | ||
4126 | |||
4127 | if (old_dentry->d_inode == new_dentry->d_inode) | ||
4128 | return 0; | ||
4129 | |||
4130 | error = may_delete(old_dir, old_dentry, is_dir); | ||
4131 | if (error) | ||
4132 | return error; | ||
4133 | |||
4134 | if (!new_dentry->d_inode) | ||
4135 | error = may_create(new_dir, new_dentry); | ||
4136 | else | ||
4137 | error = may_delete(new_dir, new_dentry, is_dir); | ||
4138 | if (error) | ||
4139 | return error; | ||
4140 | |||
4141 | if (!old_dir->i_op->rename) | ||
4142 | return -EPERM; | ||
4143 | |||
4144 | old_name = fsnotify_oldname_init(old_dentry->d_name.name); | ||
4145 | |||
4146 | if (is_dir) | ||
4147 | error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry); | ||
4148 | else | ||
4149 | error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry,delegated_inode); | ||
4150 | if (!error) | 4113 | if (!error) |
4151 | fsnotify_move(old_dir, new_dir, old_name, is_dir, | 4114 | fsnotify_move(old_dir, new_dir, old_name, is_dir, |
4152 | new_dentry->d_inode, old_dentry); | 4115 | target, old_dentry); |
4153 | fsnotify_oldname_free(old_name); | 4116 | fsnotify_oldname_free(old_name); |
4154 | 4117 | ||
4155 | return error; | 4118 | return error; |