This patch adds direct EXT4_IOC_FIEMAP support to ldiskfs, for Lustre to call without having to go through do_vfs_ioctl() (which isn't exported, and has a number of other ioctls which are not suitable for Lustre). The actual FIEMAP support is already in the kernel/ext4 for normal usage. Index: linux-stage/fs/ext4/ext4.h =================================================================== --- linux-stage.orig/fs/ext4/ext4.h +++ linux-stage/fs/ext4/ext4.h @@ -405,7 +405,7 @@ struct ext4_new_group_data { #define EXT4_IOC_GROUP_ADD _IOW('f', 8, struct ext4_new_group_input) #define EXT4_IOC_MIGRATE _IO('f', 9) /* note ioctl 10 reserved for an early version of the FIEMAP ioctl */ - /* note ioctl 11 reserved for filesystem-independent FIEMAP ioctl */ +#define EXT4_IOC_FIEMAP _IOWR('f', 11, struct fiemap) #define EXT4_IOC_ALLOC_DA_BLKS _IO('f', 12) #define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent) Index: linux-stage/fs/ext4/ioctl.c =================================================================== --- linux-stage.orig/fs/ext4/ioctl.c +++ linux-stage/fs/ext4/ioctl.c @@ -18,6 +18,71 @@ #include "ext4_jbd2.h" #include "ext4.h" +/* So that the fiemap access checks can't overflow on 32 bit machines. */ +#define FIEMAP_MAX_EXTENTS (UINT_MAX / sizeof(struct fiemap_extent)) + +static int fiemap_check_ranges(struct super_block *sb, + u64 start, u64 len, u64 *new_len) +{ + *new_len = len; + + if (len == 0) + return -EINVAL; + + if (start > sb->s_maxbytes) + return -EFBIG; + + /* + * Shrink request scope to what the fs can actually handle. + */ + if ((len > sb->s_maxbytes) || + (sb->s_maxbytes - len) < start) + *new_len = sb->s_maxbytes - start; + + return 0; +} + +int ioctl_fiemap(struct inode *inode, struct file *filp, unsigned long arg) +{ + struct fiemap fiemap; + u64 len; + struct fiemap_extent_info fieinfo = {0, }; + struct super_block *sb = inode->i_sb; + int error = 0; + + if (copy_from_user(&fiemap, (struct fiemap __user *) arg, + sizeof(struct fiemap))) + return -EFAULT; + + if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS) + return -EINVAL; + + error = fiemap_check_ranges(sb, fiemap.fm_start, fiemap.fm_length, + &len); + if (error) + return error; + + fieinfo.fi_flags = fiemap.fm_flags; + fieinfo.fi_extents_max = fiemap.fm_extent_count; + fieinfo.fi_extents_start = (struct fiemap_extent *)(arg + sizeof(fiemap)); + + if (fiemap.fm_extent_count != 0 && + !access_ok(VERIFY_WRITE, (void *)arg, + offsetof(typeof(fiemap), fm_extents[fiemap.fm_extent_count]))) + return -EFAULT; + + if (fieinfo.fi_flags & FIEMAP_FLAG_SYNC) + filemap_write_and_wait(inode->i_mapping); + + error = ext4_fiemap(inode, &fieinfo, fiemap.fm_start, len); + fiemap.fm_flags = fieinfo.fi_flags; + fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped; + if (copy_to_user((char *)arg, &fiemap, sizeof(fiemap))) + error = -EFAULT; + + return error; +} + long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_dentry->d_inode; @@ -330,6 +395,9 @@ mext_out: mnt_drop_write(filp->f_path.mnt); return err; } + case EXT4_IOC_FIEMAP: { + return ioctl_fiemap(inode, filp, arg); + } default: return -ENOTTY; Index: linux-stage/fs/ext4/fiemap.h =================================================================== --- /dev/null +++ linux-stage/fs/ext4/fiemap.h @@ -0,0 +1,2 @@ + +#include_next