diff options
Diffstat (limited to 'fs/nfs/nfs4file.c')
-rw-r--r-- | fs/nfs/nfs4file.c | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index b0dbe0abed53..4aa571956cd6 100644 --- a/fs/nfs/nfs4file.c +++ b/fs/nfs/nfs4file.c | |||
@@ -4,6 +4,7 @@ | |||
4 | * Copyright (C) 1992 Rick Sladkey | 4 | * Copyright (C) 1992 Rick Sladkey |
5 | */ | 5 | */ |
6 | #include <linux/fs.h> | 6 | #include <linux/fs.h> |
7 | #include <linux/file.h> | ||
7 | #include <linux/falloc.h> | 8 | #include <linux/falloc.h> |
8 | #include <linux/nfs_fs.h> | 9 | #include <linux/nfs_fs.h> |
9 | #include "delegation.h" | 10 | #include "delegation.h" |
@@ -192,8 +193,138 @@ static long nfs42_fallocate(struct file *filep, int mode, loff_t offset, loff_t | |||
192 | return nfs42_proc_deallocate(filep, offset, len); | 193 | return nfs42_proc_deallocate(filep, offset, len); |
193 | return nfs42_proc_allocate(filep, offset, len); | 194 | return nfs42_proc_allocate(filep, offset, len); |
194 | } | 195 | } |
196 | |||
197 | static noinline long | ||
198 | nfs42_ioctl_clone(struct file *dst_file, unsigned long srcfd, | ||
199 | u64 src_off, u64 dst_off, u64 count) | ||
200 | { | ||
201 | struct inode *dst_inode = file_inode(dst_file); | ||
202 | struct nfs_server *server = NFS_SERVER(dst_inode); | ||
203 | struct fd src_file; | ||
204 | struct inode *src_inode; | ||
205 | unsigned int bs = server->clone_blksize; | ||
206 | int ret; | ||
207 | |||
208 | /* dst file must be opened for writing */ | ||
209 | if (!(dst_file->f_mode & FMODE_WRITE)) | ||
210 | return -EINVAL; | ||
211 | |||
212 | ret = mnt_want_write_file(dst_file); | ||
213 | if (ret) | ||
214 | return ret; | ||
215 | |||
216 | src_file = fdget(srcfd); | ||
217 | if (!src_file.file) { | ||
218 | ret = -EBADF; | ||
219 | goto out_drop_write; | ||
220 | } | ||
221 | |||
222 | src_inode = file_inode(src_file.file); | ||
223 | |||
224 | /* src and dst must be different files */ | ||
225 | ret = -EINVAL; | ||
226 | if (src_inode == dst_inode) | ||
227 | goto out_fput; | ||
228 | |||
229 | /* src file must be opened for reading */ | ||
230 | if (!(src_file.file->f_mode & FMODE_READ)) | ||
231 | goto out_fput; | ||
232 | |||
233 | /* src and dst must be regular files */ | ||
234 | ret = -EISDIR; | ||
235 | if (!S_ISREG(src_inode->i_mode) || !S_ISREG(dst_inode->i_mode)) | ||
236 | goto out_fput; | ||
237 | |||
238 | ret = -EXDEV; | ||
239 | if (src_file.file->f_path.mnt != dst_file->f_path.mnt || | ||
240 | src_inode->i_sb != dst_inode->i_sb) | ||
241 | goto out_fput; | ||
242 | |||
243 | /* check alignment w.r.t. clone_blksize */ | ||
244 | ret = -EINVAL; | ||
245 | if (bs) { | ||
246 | if (!IS_ALIGNED(src_off, bs) || !IS_ALIGNED(dst_off, bs)) | ||
247 | goto out_fput; | ||
248 | if (!IS_ALIGNED(count, bs) && i_size_read(src_inode) != (src_off + count)) | ||
249 | goto out_fput; | ||
250 | } | ||
251 | |||
252 | /* XXX: do we lock at all? what if server needs CB_RECALL_LAYOUT? */ | ||
253 | if (dst_inode < src_inode) { | ||
254 | mutex_lock_nested(&dst_inode->i_mutex, I_MUTEX_PARENT); | ||
255 | mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_CHILD); | ||
256 | } else { | ||
257 | mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_PARENT); | ||
258 | mutex_lock_nested(&dst_inode->i_mutex, I_MUTEX_CHILD); | ||
259 | } | ||
260 | |||
261 | /* flush all pending writes on both src and dst so that server | ||
262 | * has the latest data */ | ||
263 | ret = nfs_sync_inode(src_inode); | ||
264 | if (ret) | ||
265 | goto out_unlock; | ||
266 | ret = nfs_sync_inode(dst_inode); | ||
267 | if (ret) | ||
268 | goto out_unlock; | ||
269 | |||
270 | ret = nfs42_proc_clone(src_file.file, dst_file, src_off, dst_off, count); | ||
271 | |||
272 | /* truncate inode page cache of the dst range so that future reads can fetch | ||
273 | * new data from server */ | ||
274 | if (!ret) | ||
275 | truncate_inode_pages_range(&dst_inode->i_data, dst_off, dst_off + count - 1); | ||
276 | |||
277 | out_unlock: | ||
278 | if (dst_inode < src_inode) { | ||
279 | mutex_unlock(&src_inode->i_mutex); | ||
280 | mutex_unlock(&dst_inode->i_mutex); | ||
281 | } else { | ||
282 | mutex_unlock(&dst_inode->i_mutex); | ||
283 | mutex_unlock(&src_inode->i_mutex); | ||
284 | } | ||
285 | out_fput: | ||
286 | fdput(src_file); | ||
287 | out_drop_write: | ||
288 | mnt_drop_write_file(dst_file); | ||
289 | return ret; | ||
290 | } | ||
291 | |||
292 | static long nfs42_ioctl_clone_range(struct file *dst_file, void __user *argp) | ||
293 | { | ||
294 | struct nfs_ioctl_clone_range_args args; | ||
295 | |||
296 | if (copy_from_user(&args, argp, sizeof(args))) | ||
297 | return -EFAULT; | ||
298 | |||
299 | return nfs42_ioctl_clone(dst_file, args.src_fd, args.src_off, args.dst_off, args.count); | ||
300 | } | ||
301 | #else | ||
302 | static long nfs42_ioctl_clone(struct file *dst_file, unsigned long srcfd, | ||
303 | u64 src_off, u64 dst_off, u64 count) | ||
304 | { | ||
305 | return -ENOTTY; | ||
306 | } | ||
307 | |||
308 | static long nfs42_ioctl_clone_range(struct file *dst_file, void __user *argp) | ||
309 | { | ||
310 | return -ENOTTY; | ||
311 | } | ||
195 | #endif /* CONFIG_NFS_V4_2 */ | 312 | #endif /* CONFIG_NFS_V4_2 */ |
196 | 313 | ||
314 | long nfs4_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
315 | { | ||
316 | void __user *argp = (void __user *)arg; | ||
317 | |||
318 | switch (cmd) { | ||
319 | case NFS_IOC_CLONE: | ||
320 | return nfs42_ioctl_clone(file, arg, 0, 0, 0); | ||
321 | case NFS_IOC_CLONE_RANGE: | ||
322 | return nfs42_ioctl_clone_range(file, argp); | ||
323 | } | ||
324 | |||
325 | return -ENOTTY; | ||
326 | } | ||
327 | |||
197 | const struct file_operations nfs4_file_operations = { | 328 | const struct file_operations nfs4_file_operations = { |
198 | #ifdef CONFIG_NFS_V4_2 | 329 | #ifdef CONFIG_NFS_V4_2 |
199 | .llseek = nfs4_file_llseek, | 330 | .llseek = nfs4_file_llseek, |
@@ -216,4 +347,9 @@ const struct file_operations nfs4_file_operations = { | |||
216 | #endif /* CONFIG_NFS_V4_2 */ | 347 | #endif /* CONFIG_NFS_V4_2 */ |
217 | .check_flags = nfs_check_flags, | 348 | .check_flags = nfs_check_flags, |
218 | .setlease = simple_nosetlease, | 349 | .setlease = simple_nosetlease, |
350 | #ifdef CONFIG_COMPAT | ||
351 | .unlocked_ioctl = nfs4_ioctl, | ||
352 | #else | ||
353 | .compat_ioctl = nfs4_ioctl, | ||
354 | #endif /* CONFIG_COMPAT */ | ||
219 | }; | 355 | }; |