aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@redhat.com>2008-10-03 17:32:43 -0400
committerTheodore Ts'o <tytso@mit.edu>2008-10-03 17:32:43 -0400
commit68c9d702bb72f367f3b148963ec6cf5e07ff7f65 (patch)
tree16fc61a1bd973c14a7858ab1fbcb306d3ae75184
parent00dc417fa3e763345b34ccb6034d72de76eea0a1 (diff)
generic block based fiemap implementation
Any block based fs (this patch includes ext3) just has to declare its own fiemap() function and then call this generic function with its own get_block_t. This works well for block based filesystems that will map multiple contiguous blocks at one time, but will work for filesystems that only map one block at a time, you will just end up with an "extent" for each block. One gotcha is this will not play nicely where there is hole+data after the EOF. This function will assume its hit the end of the data as soon as it hits a hole after the EOF, so if there is any data past that it will not pick that up. AFAIK no block based fs does this anyway, but its in the comments of the function anyway just in case. Signed-off-by: Josef Bacik <jbacik@redhat.com> Signed-off-by: Mark Fasheh <mfasheh@suse.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Cc: linux-fsdevel@vger.kernel.org
-rw-r--r--fs/ext2/ext2.h2
-rw-r--r--fs/ext2/file.c1
-rw-r--r--fs/ext2/inode.c8
-rw-r--r--fs/ext3/file.c1
-rw-r--r--fs/ext3/inode.c8
-rw-r--r--fs/ioctl.c118
-rw-r--r--include/linux/ext3_fs.h2
-rw-r--r--include/linux/fs.h3
8 files changed, 143 insertions, 0 deletions
diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h
index 47d88da2d33b..bae998c1e44e 100644
--- a/fs/ext2/ext2.h
+++ b/fs/ext2/ext2.h
@@ -133,6 +133,8 @@ extern void ext2_truncate (struct inode *);
133extern int ext2_setattr (struct dentry *, struct iattr *); 133extern int ext2_setattr (struct dentry *, struct iattr *);
134extern void ext2_set_inode_flags(struct inode *inode); 134extern void ext2_set_inode_flags(struct inode *inode);
135extern void ext2_get_inode_flags(struct ext2_inode_info *); 135extern void ext2_get_inode_flags(struct ext2_inode_info *);
136extern int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
137 u64 start, u64 len);
136int __ext2_write_begin(struct file *file, struct address_space *mapping, 138int __ext2_write_begin(struct file *file, struct address_space *mapping,
137 loff_t pos, unsigned len, unsigned flags, 139 loff_t pos, unsigned len, unsigned flags,
138 struct page **pagep, void **fsdata); 140 struct page **pagep, void **fsdata);
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index 5f2fa9c36293..45ed07122182 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -86,4 +86,5 @@ const struct inode_operations ext2_file_inode_operations = {
86#endif 86#endif
87 .setattr = ext2_setattr, 87 .setattr = ext2_setattr,
88 .permission = ext2_permission, 88 .permission = ext2_permission,
89 .fiemap = ext2_fiemap,
89}; 90};
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 991d6dfeb51f..7658b33e2653 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -31,6 +31,7 @@
31#include <linux/writeback.h> 31#include <linux/writeback.h>
32#include <linux/buffer_head.h> 32#include <linux/buffer_head.h>
33#include <linux/mpage.h> 33#include <linux/mpage.h>
34#include <linux/fiemap.h>
34#include "ext2.h" 35#include "ext2.h"
35#include "acl.h" 36#include "acl.h"
36#include "xip.h" 37#include "xip.h"
@@ -704,6 +705,13 @@ int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_
704 705
705} 706}
706 707
708int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
709 u64 start, u64 len)
710{
711 return generic_block_fiemap(inode, fieinfo, start, len,
712 ext2_get_block);
713}
714
707static int ext2_writepage(struct page *page, struct writeback_control *wbc) 715static int ext2_writepage(struct page *page, struct writeback_control *wbc)
708{ 716{
709 return block_write_full_page(page, ext2_get_block, wbc); 717 return block_write_full_page(page, ext2_get_block, wbc);
diff --git a/fs/ext3/file.c b/fs/ext3/file.c
index acc4913d3019..3be1e0689c9a 100644
--- a/fs/ext3/file.c
+++ b/fs/ext3/file.c
@@ -134,5 +134,6 @@ const struct inode_operations ext3_file_inode_operations = {
134 .removexattr = generic_removexattr, 134 .removexattr = generic_removexattr,
135#endif 135#endif
136 .permission = ext3_permission, 136 .permission = ext3_permission,
137 .fiemap = ext3_fiemap,
137}; 138};
138 139
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index 507d8689b111..ebfec4d0148e 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -36,6 +36,7 @@
36#include <linux/mpage.h> 36#include <linux/mpage.h>
37#include <linux/uio.h> 37#include <linux/uio.h>
38#include <linux/bio.h> 38#include <linux/bio.h>
39#include <linux/fiemap.h>
39#include "xattr.h" 40#include "xattr.h"
40#include "acl.h" 41#include "acl.h"
41 42
@@ -981,6 +982,13 @@ out:
981 return ret; 982 return ret;
982} 983}
983 984
985int ext3_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
986 u64 start, u64 len)
987{
988 return generic_block_fiemap(inode, fieinfo, start, len,
989 ext3_get_block);
990}
991
984/* 992/*
985 * `handle' can be NULL if create is zero 993 * `handle' can be NULL if create is zero
986 */ 994 */
diff --git a/fs/ioctl.c b/fs/ioctl.c
index 045d9601fbbd..33a6b7ecb8b8 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -13,6 +13,8 @@
13#include <linux/security.h> 13#include <linux/security.h>
14#include <linux/module.h> 14#include <linux/module.h>
15#include <linux/uaccess.h> 15#include <linux/uaccess.h>
16#include <linux/writeback.h>
17#include <linux/buffer_head.h>
16 18
17#include <asm/ioctls.h> 19#include <asm/ioctls.h>
18 20
@@ -224,6 +226,122 @@ static int ioctl_fiemap(struct file *filp, unsigned long arg)
224 return error; 226 return error;
225} 227}
226 228
229#define blk_to_logical(inode, blk) (blk << (inode)->i_blkbits)
230#define logical_to_blk(inode, offset) (offset >> (inode)->i_blkbits);
231
232/*
233 * @inode - the inode to map
234 * @arg - the pointer to userspace where we copy everything to
235 * @get_block - the fs's get_block function
236 *
237 * This does FIEMAP for block based inodes. Basically it will just loop
238 * through get_block until we hit the number of extents we want to map, or we
239 * go past the end of the file and hit a hole.
240 *
241 * If it is possible to have data blocks beyond a hole past @inode->i_size, then
242 * please do not use this function, it will stop at the first unmapped block
243 * beyond i_size
244 */
245int generic_block_fiemap(struct inode *inode,
246 struct fiemap_extent_info *fieinfo, u64 start,
247 u64 len, get_block_t *get_block)
248{
249 struct buffer_head tmp;
250 unsigned int start_blk;
251 long long length = 0, map_len = 0;
252 u64 logical = 0, phys = 0, size = 0;
253 u32 flags = FIEMAP_EXTENT_MERGED;
254 int ret = 0;
255
256 if ((ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC)))
257 return ret;
258
259 start_blk = logical_to_blk(inode, start);
260
261 /* guard against change */
262 mutex_lock(&inode->i_mutex);
263
264 length = (long long)min_t(u64, len, i_size_read(inode));
265 map_len = length;
266
267 do {
268 /*
269 * we set b_size to the total size we want so it will map as
270 * many contiguous blocks as possible at once
271 */
272 memset(&tmp, 0, sizeof(struct buffer_head));
273 tmp.b_size = map_len;
274
275 ret = get_block(inode, start_blk, &tmp, 0);
276 if (ret)
277 break;
278
279 /* HOLE */
280 if (!buffer_mapped(&tmp)) {
281 /*
282 * first hole after going past the EOF, this is our
283 * last extent
284 */
285 if (length <= 0) {
286 flags = FIEMAP_EXTENT_MERGED|FIEMAP_EXTENT_LAST;
287 ret = fiemap_fill_next_extent(fieinfo, logical,
288 phys, size,
289 flags);
290 break;
291 }
292
293 length -= blk_to_logical(inode, 1);
294
295 /* if we have holes up to/past EOF then we're done */
296 if (length <= 0)
297 break;
298
299 start_blk++;
300 } else {
301 if (length <= 0 && size) {
302 ret = fiemap_fill_next_extent(fieinfo, logical,
303 phys, size,
304 flags);
305 if (ret)
306 break;
307 }
308
309 logical = blk_to_logical(inode, start_blk);
310 phys = blk_to_logical(inode, tmp.b_blocknr);
311 size = tmp.b_size;
312 flags = FIEMAP_EXTENT_MERGED;
313
314 length -= tmp.b_size;
315 start_blk += logical_to_blk(inode, size);
316
317 /*
318 * if we are past the EOF we need to loop again to see
319 * if there is a hole so we can mark this extent as the
320 * last one, and if not keep mapping things until we
321 * find a hole, or we run out of slots in the extent
322 * array
323 */
324 if (length <= 0)
325 continue;
326
327 ret = fiemap_fill_next_extent(fieinfo, logical, phys,
328 size, flags);
329 if (ret)
330 break;
331 }
332 cond_resched();
333 } while (1);
334
335 mutex_unlock(&inode->i_mutex);
336
337 /* if ret is 1 then we just hit the end of the extent array */
338 if (ret == 1)
339 ret = 0;
340
341 return ret;
342}
343EXPORT_SYMBOL(generic_block_fiemap);
344
227static int file_ioctl(struct file *filp, unsigned int cmd, 345static int file_ioctl(struct file *filp, unsigned int cmd,
228 unsigned long arg) 346 unsigned long arg)
229{ 347{
diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h
index 80171ee89a22..8120fa1bc235 100644
--- a/include/linux/ext3_fs.h
+++ b/include/linux/ext3_fs.h
@@ -837,6 +837,8 @@ extern void ext3_truncate (struct inode *);
837extern void ext3_set_inode_flags(struct inode *); 837extern void ext3_set_inode_flags(struct inode *);
838extern void ext3_get_inode_flags(struct ext3_inode_info *); 838extern void ext3_get_inode_flags(struct ext3_inode_info *);
839extern void ext3_set_aops(struct inode *inode); 839extern void ext3_set_aops(struct inode *inode);
840extern int ext3_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
841 u64 start, u64 len);
840 842
841/* ioctl.c */ 843/* ioctl.c */
842extern int ext3_ioctl (struct inode *, struct file *, unsigned int, 844extern int ext3_ioctl (struct inode *, struct file *, unsigned int,
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 194fb237a307..385c9a197df1 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1998,6 +1998,9 @@ extern int vfs_fstat(unsigned int, struct kstat *);
1998 1998
1999extern int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, 1999extern int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
2000 unsigned long arg); 2000 unsigned long arg);
2001extern int generic_block_fiemap(struct inode *inode,
2002 struct fiemap_extent_info *fieinfo, u64 start,
2003 u64 len, get_block_t *get_block);
2001 2004
2002extern void get_filesystem(struct file_system_type *fs); 2005extern void get_filesystem(struct file_system_type *fs);
2003extern void put_filesystem(struct file_system_type *fs); 2006extern void put_filesystem(struct file_system_type *fs);