diff options
author | Tao Ma <tao.ma@oracle.com> | 2009-09-20 23:25:14 -0400 |
---|---|---|
committer | Joel Becker <joel.becker@oracle.com> | 2009-09-22 23:09:51 -0400 |
commit | bd50873dc725a9fa72592ecc986c58805e823051 (patch) | |
tree | 8d2d4d514f73b6425bf0bf5bd1806949b84a2489 /fs/ocfs2 | |
parent | 64871b8d62570fabec3b0959d494f8e0b87f5c4b (diff) |
ocfs2: Add ioctl for reflink.
The ioctl will take 3 parameters: old_path, new_path and
preserve and call vfs_reflink. It is useful when we backport
reflink features to old kernels.
Signed-off-by: Tao Ma <tao.ma@oracle.com>
Diffstat (limited to 'fs/ocfs2')
-rw-r--r-- | fs/ocfs2/ioctl.c | 14 | ||||
-rw-r--r-- | fs/ocfs2/ocfs2_fs.h | 9 | ||||
-rw-r--r-- | fs/ocfs2/refcounttree.c | 166 | ||||
-rw-r--r-- | fs/ocfs2/refcounttree.h | 4 |
4 files changed, 193 insertions, 0 deletions
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c index 9fcd36dcc9a0..a68d0e4ca6dc 100644 --- a/fs/ocfs2/ioctl.c +++ b/fs/ocfs2/ioctl.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include "ocfs2_fs.h" | 22 | #include "ocfs2_fs.h" |
23 | #include "ioctl.h" | 23 | #include "ioctl.h" |
24 | #include "resize.h" | 24 | #include "resize.h" |
25 | #include "refcounttree.h" | ||
25 | 26 | ||
26 | #include <linux/ext2_fs.h> | 27 | #include <linux/ext2_fs.h> |
27 | 28 | ||
@@ -116,6 +117,9 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
116 | int status; | 117 | int status; |
117 | struct ocfs2_space_resv sr; | 118 | struct ocfs2_space_resv sr; |
118 | struct ocfs2_new_group_input input; | 119 | struct ocfs2_new_group_input input; |
120 | struct reflink_arguments args; | ||
121 | const char *old_path, *new_path; | ||
122 | bool preserve; | ||
119 | 123 | ||
120 | switch (cmd) { | 124 | switch (cmd) { |
121 | case OCFS2_IOC_GETFLAGS: | 125 | case OCFS2_IOC_GETFLAGS: |
@@ -161,6 +165,15 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
161 | return -EFAULT; | 165 | return -EFAULT; |
162 | 166 | ||
163 | return ocfs2_group_add(inode, &input); | 167 | return ocfs2_group_add(inode, &input); |
168 | case OCFS2_IOC_REFLINK: | ||
169 | if (copy_from_user(&args, (struct reflink_arguments *)arg, | ||
170 | sizeof(args))) | ||
171 | return -EFAULT; | ||
172 | old_path = (const char *)(unsigned long)args.old_path; | ||
173 | new_path = (const char *)(unsigned long)args.new_path; | ||
174 | preserve = (args.preserve != 0); | ||
175 | |||
176 | return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve); | ||
164 | default: | 177 | default: |
165 | return -ENOTTY; | 178 | return -ENOTTY; |
166 | } | 179 | } |
@@ -183,6 +196,7 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg) | |||
183 | case OCFS2_IOC_GROUP_EXTEND: | 196 | case OCFS2_IOC_GROUP_EXTEND: |
184 | case OCFS2_IOC_GROUP_ADD: | 197 | case OCFS2_IOC_GROUP_ADD: |
185 | case OCFS2_IOC_GROUP_ADD64: | 198 | case OCFS2_IOC_GROUP_ADD64: |
199 | case OCFS2_IOC_REFLINK: | ||
186 | break; | 200 | break; |
187 | default: | 201 | default: |
188 | return -ENOIOCTLCMD; | 202 | return -ENOIOCTLCMD; |
diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index 4a4565b7bc14..e9431e4a5e7c 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h | |||
@@ -301,6 +301,15 @@ struct ocfs2_new_group_input { | |||
301 | #define OCFS2_IOC_GROUP_ADD _IOW('o', 2,struct ocfs2_new_group_input) | 301 | #define OCFS2_IOC_GROUP_ADD _IOW('o', 2,struct ocfs2_new_group_input) |
302 | #define OCFS2_IOC_GROUP_ADD64 _IOW('o', 3,struct ocfs2_new_group_input) | 302 | #define OCFS2_IOC_GROUP_ADD64 _IOW('o', 3,struct ocfs2_new_group_input) |
303 | 303 | ||
304 | /* Used to pass 2 file names to reflink. */ | ||
305 | struct reflink_arguments { | ||
306 | __u64 old_path; | ||
307 | __u64 new_path; | ||
308 | __u64 preserve; | ||
309 | }; | ||
310 | #define OCFS2_IOC_REFLINK _IOW('o', 4, struct reflink_arguments) | ||
311 | |||
312 | |||
304 | /* | 313 | /* |
305 | * Journal Flags (ocfs2_dinode.id1.journal1.i_flags) | 314 | * Journal Flags (ocfs2_dinode.id1.journal1.i_flags) |
306 | */ | 315 | */ |
diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 7a8a384d8ad1..60287fc56bcb 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c | |||
@@ -42,6 +42,11 @@ | |||
42 | #include <linux/writeback.h> | 42 | #include <linux/writeback.h> |
43 | #include <linux/pagevec.h> | 43 | #include <linux/pagevec.h> |
44 | #include <linux/swap.h> | 44 | #include <linux/swap.h> |
45 | #include <linux/security.h> | ||
46 | #include <linux/fsnotify.h> | ||
47 | #include <linux/quotaops.h> | ||
48 | #include <linux/namei.h> | ||
49 | #include <linux/mount.h> | ||
45 | 50 | ||
46 | struct ocfs2_cow_context { | 51 | struct ocfs2_cow_context { |
47 | struct inode *inode; | 52 | struct inode *inode; |
@@ -4145,3 +4150,164 @@ out: | |||
4145 | 4150 | ||
4146 | return error; | 4151 | return error; |
4147 | } | 4152 | } |
4153 | |||
4154 | /* | ||
4155 | * Below here are the bits used by OCFS2_IOC_REFLINK() to fake | ||
4156 | * sys_reflink(). This will go away when vfs_reflink() exists in | ||
4157 | * fs/namei.c. | ||
4158 | */ | ||
4159 | |||
4160 | /* copied from may_create in VFS. */ | ||
4161 | static inline int ocfs2_may_create(struct inode *dir, struct dentry *child) | ||
4162 | { | ||
4163 | if (child->d_inode) | ||
4164 | return -EEXIST; | ||
4165 | if (IS_DEADDIR(dir)) | ||
4166 | return -ENOENT; | ||
4167 | return inode_permission(dir, MAY_WRITE | MAY_EXEC); | ||
4168 | } | ||
4169 | |||
4170 | /* copied from user_path_parent. */ | ||
4171 | static int ocfs2_user_path_parent(const char __user *path, | ||
4172 | struct nameidata *nd, char **name) | ||
4173 | { | ||
4174 | char *s = getname(path); | ||
4175 | int error; | ||
4176 | |||
4177 | if (IS_ERR(s)) | ||
4178 | return PTR_ERR(s); | ||
4179 | |||
4180 | error = path_lookup(s, LOOKUP_PARENT, nd); | ||
4181 | if (error) | ||
4182 | putname(s); | ||
4183 | else | ||
4184 | *name = s; | ||
4185 | |||
4186 | return error; | ||
4187 | } | ||
4188 | |||
4189 | /** | ||
4190 | * ocfs2_vfs_reflink - Create a reference-counted link | ||
4191 | * | ||
4192 | * @old_dentry: source dentry + inode | ||
4193 | * @dir: directory to create the target | ||
4194 | * @new_dentry: target dentry | ||
4195 | * @preserve: if true, preserve all file attributes | ||
4196 | */ | ||
4197 | int ocfs2_vfs_reflink(struct dentry *old_dentry, struct inode *dir, | ||
4198 | struct dentry *new_dentry, bool preserve) | ||
4199 | { | ||
4200 | struct inode *inode = old_dentry->d_inode; | ||
4201 | int error; | ||
4202 | |||
4203 | if (!inode) | ||
4204 | return -ENOENT; | ||
4205 | |||
4206 | error = ocfs2_may_create(dir, new_dentry); | ||
4207 | if (error) | ||
4208 | return error; | ||
4209 | |||
4210 | if (dir->i_sb != inode->i_sb) | ||
4211 | return -EXDEV; | ||
4212 | |||
4213 | /* | ||
4214 | * A reflink to an append-only or immutable file cannot be created. | ||
4215 | */ | ||
4216 | if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) | ||
4217 | return -EPERM; | ||
4218 | |||
4219 | /* Only regular files can be reflinked. */ | ||
4220 | if (!S_ISREG(inode->i_mode)) | ||
4221 | return -EPERM; | ||
4222 | |||
4223 | /* | ||
4224 | * If the caller wants to preserve ownership, they require the | ||
4225 | * rights to do so. | ||
4226 | */ | ||
4227 | if (preserve) { | ||
4228 | if ((current_fsuid() != inode->i_uid) && !capable(CAP_CHOWN)) | ||
4229 | return -EPERM; | ||
4230 | if (!in_group_p(inode->i_gid) && !capable(CAP_CHOWN)) | ||
4231 | return -EPERM; | ||
4232 | } | ||
4233 | |||
4234 | /* | ||
4235 | * If the caller is modifying any aspect of the attributes, they | ||
4236 | * are not creating a snapshot. They need read permission on the | ||
4237 | * file. | ||
4238 | */ | ||
4239 | if (!preserve) { | ||
4240 | error = inode_permission(inode, MAY_READ); | ||
4241 | if (error) | ||
4242 | return error; | ||
4243 | } | ||
4244 | |||
4245 | mutex_lock(&inode->i_mutex); | ||
4246 | vfs_dq_init(dir); | ||
4247 | error = ocfs2_reflink(old_dentry, dir, new_dentry, preserve); | ||
4248 | mutex_unlock(&inode->i_mutex); | ||
4249 | if (!error) | ||
4250 | fsnotify_create(dir, new_dentry); | ||
4251 | return error; | ||
4252 | } | ||
4253 | /* | ||
4254 | * Most codes are copied from sys_linkat. | ||
4255 | */ | ||
4256 | int ocfs2_reflink_ioctl(struct inode *inode, | ||
4257 | const char __user *oldname, | ||
4258 | const char __user *newname, | ||
4259 | bool preserve) | ||
4260 | { | ||
4261 | struct dentry *new_dentry; | ||
4262 | struct nameidata nd; | ||
4263 | struct path old_path; | ||
4264 | int error; | ||
4265 | char *to = NULL; | ||
4266 | |||
4267 | if (!ocfs2_refcount_tree(OCFS2_SB(inode->i_sb))) | ||
4268 | return -EOPNOTSUPP; | ||
4269 | |||
4270 | error = user_path_at(AT_FDCWD, oldname, 0, &old_path); | ||
4271 | if (error) { | ||
4272 | mlog_errno(error); | ||
4273 | return error; | ||
4274 | } | ||
4275 | |||
4276 | error = ocfs2_user_path_parent(newname, &nd, &to); | ||
4277 | if (error) { | ||
4278 | mlog_errno(error); | ||
4279 | goto out; | ||
4280 | } | ||
4281 | |||
4282 | error = -EXDEV; | ||
4283 | if (old_path.mnt != nd.path.mnt) | ||
4284 | goto out_release; | ||
4285 | new_dentry = lookup_create(&nd, 0); | ||
4286 | error = PTR_ERR(new_dentry); | ||
4287 | if (IS_ERR(new_dentry)) { | ||
4288 | mlog_errno(error); | ||
4289 | goto out_unlock; | ||
4290 | } | ||
4291 | |||
4292 | error = mnt_want_write(nd.path.mnt); | ||
4293 | if (error) { | ||
4294 | mlog_errno(error); | ||
4295 | goto out_dput; | ||
4296 | } | ||
4297 | |||
4298 | error = ocfs2_vfs_reflink(old_path.dentry, | ||
4299 | nd.path.dentry->d_inode, | ||
4300 | new_dentry, preserve); | ||
4301 | mnt_drop_write(nd.path.mnt); | ||
4302 | out_dput: | ||
4303 | dput(new_dentry); | ||
4304 | out_unlock: | ||
4305 | mutex_unlock(&nd.path.dentry->d_inode->i_mutex); | ||
4306 | out_release: | ||
4307 | path_put(&nd.path); | ||
4308 | putname(to); | ||
4309 | out: | ||
4310 | path_put(&old_path); | ||
4311 | |||
4312 | return error; | ||
4313 | } | ||
diff --git a/fs/ocfs2/refcounttree.h b/fs/ocfs2/refcounttree.h index 2c238e682570..c1d19b1d3ecc 100644 --- a/fs/ocfs2/refcounttree.h +++ b/fs/ocfs2/refcounttree.h | |||
@@ -99,4 +99,8 @@ int ocfs2_increase_refcount(handle_t *handle, | |||
99 | u64 cpos, u32 len, | 99 | u64 cpos, u32 len, |
100 | struct ocfs2_alloc_context *meta_ac, | 100 | struct ocfs2_alloc_context *meta_ac, |
101 | struct ocfs2_cached_dealloc_ctxt *dealloc); | 101 | struct ocfs2_cached_dealloc_ctxt *dealloc); |
102 | int ocfs2_reflink_ioctl(struct inode *inode, | ||
103 | const char __user *oldname, | ||
104 | const char __user *newname, | ||
105 | bool preserve); | ||
102 | #endif /* OCFS2_REFCOUNTTREE_H */ | 106 | #endif /* OCFS2_REFCOUNTTREE_H */ |