diff options
author | Brian Foster <bfoster@redhat.com> | 2013-05-17 09:30:32 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2013-05-20 10:58:57 -0400 |
commit | 3634a6327815d39dd93e5c44a602daae91c66297 (patch) | |
tree | fa8a52812af954f8604ad105263594d9d43c9c46 /fs | |
parent | de82b923012ff8790bcfff381eb3ca9069d00f49 (diff) |
fuse: truncate pagecache range on hole punch
fuse supports hole punch via the fallocate() FALLOC_FL_PUNCH_HOLE
interface. When a hole punch is passed through, the page cache
is not cleared and thus allows reading stale data from the cache.
This is easily demonstrable (using FOPEN_KEEP_CACHE) by reading a
smallish random data file into cache, punching a hole and creating
a copy of the file. Drop caches or remount and observe that the
original file no longer matches the file copied after the hole
punch. The original file contains a zeroed range and the latter
file contains stale data.
Protect against writepage requests in progress and punch out the
associated page cache range after a successful client fs hole
punch.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/fuse/file.c | 22 |
1 files changed, 20 insertions, 2 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index fe191325fefa..a200a2d80377 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/compat.h> | 16 | #include <linux/compat.h> |
17 | #include <linux/swap.h> | 17 | #include <linux/swap.h> |
18 | #include <linux/aio.h> | 18 | #include <linux/aio.h> |
19 | #include <linux/falloc.h> | ||
19 | 20 | ||
20 | static const struct file_operations fuse_direct_io_file_operations; | 21 | static const struct file_operations fuse_direct_io_file_operations; |
21 | 22 | ||
@@ -2453,6 +2454,7 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, | |||
2453 | loff_t length) | 2454 | loff_t length) |
2454 | { | 2455 | { |
2455 | struct fuse_file *ff = file->private_data; | 2456 | struct fuse_file *ff = file->private_data; |
2457 | struct inode *inode = file->f_inode; | ||
2456 | struct fuse_conn *fc = ff->fc; | 2458 | struct fuse_conn *fc = ff->fc; |
2457 | struct fuse_req *req; | 2459 | struct fuse_req *req; |
2458 | struct fuse_fallocate_in inarg = { | 2460 | struct fuse_fallocate_in inarg = { |
@@ -2466,9 +2468,16 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, | |||
2466 | if (fc->no_fallocate) | 2468 | if (fc->no_fallocate) |
2467 | return -EOPNOTSUPP; | 2469 | return -EOPNOTSUPP; |
2468 | 2470 | ||
2471 | if (mode & FALLOC_FL_PUNCH_HOLE) { | ||
2472 | mutex_lock(&inode->i_mutex); | ||
2473 | fuse_set_nowrite(inode); | ||
2474 | } | ||
2475 | |||
2469 | req = fuse_get_req_nopages(fc); | 2476 | req = fuse_get_req_nopages(fc); |
2470 | if (IS_ERR(req)) | 2477 | if (IS_ERR(req)) { |
2471 | return PTR_ERR(req); | 2478 | err = PTR_ERR(req); |
2479 | goto out; | ||
2480 | } | ||
2472 | 2481 | ||
2473 | req->in.h.opcode = FUSE_FALLOCATE; | 2482 | req->in.h.opcode = FUSE_FALLOCATE; |
2474 | req->in.h.nodeid = ff->nodeid; | 2483 | req->in.h.nodeid = ff->nodeid; |
@@ -2483,6 +2492,15 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, | |||
2483 | } | 2492 | } |
2484 | fuse_put_request(fc, req); | 2493 | fuse_put_request(fc, req); |
2485 | 2494 | ||
2495 | out: | ||
2496 | if (mode & FALLOC_FL_PUNCH_HOLE) { | ||
2497 | if (!err) | ||
2498 | truncate_pagecache_range(inode, offset, | ||
2499 | offset + length - 1); | ||
2500 | fuse_release_nowrite(inode); | ||
2501 | mutex_unlock(&inode->i_mutex); | ||
2502 | } | ||
2503 | |||
2486 | return err; | 2504 | return err; |
2487 | } | 2505 | } |
2488 | 2506 | ||