From: Darrick J. Wong <djwong@xxxxxxxxxx> Implement the basic file mapping reporting functions like FIEMAP, BMAP, and SEEK_DATA/HOLE. Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- fs/fuse/fuse_i.h | 8 ++++++ fs/fuse/fuse_trace.h | 57 +++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 1 + fs/fuse/file.c | 13 +++++++++ fs/fuse/file_iomap.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 149 insertions(+) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 4eb75ed90db300..a39e45eeec2e3e 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1626,12 +1626,20 @@ void fuse_iomap_conn_put(struct fuse_conn *fc); int fuse_iomap_add_device(struct fuse_conn *fc, const struct fuse_iomap_add_device_out *outarg); + +int fuse_iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, + u64 start, u64 length); +loff_t fuse_iomap_lseek(struct file *file, loff_t offset, int whence); +sector_t fuse_iomap_bmap(struct address_space *mapping, sector_t block); #else # define fuse_iomap_enabled(...) (false) # define fuse_has_iomap(...) (false) # define fuse_iomap_init_reply(...) ((void)0) # define fuse_iomap_conn_put(...) ((void)0) # define fuse_iomap_add_device(...) (-ENOSYS) +# define fuse_iomap_fiemap NULL +# define fuse_iomap_lseek(...) (-ENOSYS) +# define fuse_iomap_bmap(...) (-ENOSYS) #endif #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/fuse_trace.h b/fs/fuse/fuse_trace.h index e1a2e491d2581a..252eab698287bd 100644 --- a/fs/fuse/fuse_trace.h +++ b/fs/fuse/fuse_trace.h @@ -416,6 +416,63 @@ DEFINE_EVENT(fuse_iomap_dev_class, name, \ TP_ARGS(fc, idx, file)) DEFINE_FUSE_IOMAP_DEV_EVENT(fuse_iomap_add_dev); DEFINE_FUSE_IOMAP_DEV_EVENT(fuse_iomap_remove_dev); + +TRACE_EVENT(fuse_iomap_fiemap, + TP_PROTO(const struct inode *inode, u64 start, u64 count, + unsigned int flags), + + TP_ARGS(inode, start, count, flags), + + TP_STRUCT__entry( + __field(dev_t, connection) + __field(uint64_t, ino) + __field(u64, start) + __field(u64, count) + __field(unsigned int, flags) + ), + + TP_fast_assign( + const struct fuse_inode *fi = get_fuse_inode_c(inode); + const struct fuse_mount *fm = get_fuse_mount_c(inode); + + __entry->connection = fm->fc->dev; + __entry->ino = fi->orig_ino; + __entry->start = start; + __entry->count = count; + __entry->flags = flags; + ), + + TP_printk("connection %u ino %llu flags 0x%x start 0x%llx count 0x%llx", + __entry->connection, __entry->ino, __entry->flags, + __entry->start, __entry->count) +); + +TRACE_EVENT(fuse_iomap_lseek, + TP_PROTO(const struct inode *inode, loff_t offset, int whence), + + TP_ARGS(inode, offset, whence), + + TP_STRUCT__entry( + __field(dev_t, connection) + __field(uint64_t, ino) + __field(loff_t, offset) + __field(int, whence) + ), + + TP_fast_assign( + const struct fuse_inode *fi = get_fuse_inode_c(inode); + const struct fuse_mount *fm = get_fuse_mount_c(inode); + + __entry->connection = fm->fc->dev; + __entry->ino = fi->orig_ino; + __entry->offset = offset; + __entry->whence = whence; + ), + + TP_printk("connection %u ino %llu offset 0x%llx whence %d", + __entry->connection, __entry->ino, __entry->offset, + __entry->whence) +); #endif /* CONFIG_FUSE_IOMAP */ #endif /* _TRACE_FUSE_H */ diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 83ac192e7fdd19..be75a515c4f8b6 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -2230,6 +2230,7 @@ static const struct inode_operations fuse_common_inode_operations = { .set_acl = fuse_set_acl, .fileattr_get = fuse_fileattr_get, .fileattr_set = fuse_fileattr_set, + .fiemap = fuse_iomap_fiemap, }; static const struct inode_operations fuse_symlink_inode_operations = { diff --git a/fs/fuse/file.c b/fs/fuse/file.c index ada1ed9e653e42..6b54b9a8f8a84d 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2844,6 +2844,12 @@ static sector_t fuse_bmap(struct address_space *mapping, sector_t block) struct fuse_bmap_out outarg; int err; + if (fuse_has_iomap(inode)) { + sector_t alt_sec = fuse_iomap_bmap(mapping, block); + if (alt_sec > 0) + return alt_sec; + } + if (!inode->i_sb->s_bdev || fm->fc->no_bmap) return 0; @@ -2879,6 +2885,13 @@ static loff_t fuse_lseek(struct file *file, loff_t offset, int whence) struct fuse_lseek_out outarg; int err; + if (fuse_has_iomap(inode)) { + loff_t alt_pos = fuse_iomap_lseek(file, offset, whence); + + if (alt_pos >= 0 || (alt_pos < 0 && alt_pos != -ENOSYS)) + return alt_pos; + } + if (fm->fc->no_lseek) goto fallback; diff --git a/fs/fuse/file_iomap.c b/fs/fuse/file_iomap.c index faefd29a273bf3..f943cb3334a787 100644 --- a/fs/fuse/file_iomap.c +++ b/fs/fuse/file_iomap.c @@ -391,3 +391,73 @@ int fuse_iomap_add_device(struct fuse_conn *fc, return put_user(ret, outarg->map_dev); } + +int fuse_iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, + u64 start, u64 count) +{ + struct fuse_conn *fc = get_fuse_conn(inode); + int error; + + /* + * We are called directly from the vfs so we need to check per-inode + * support here explicitly. + */ + if (!fuse_has_iomap(inode)) + return -EOPNOTSUPP; + + if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) + return -EOPNOTSUPP; + + if (fuse_is_bad(inode)) + return -EIO; + + if (!fuse_allow_current_process(fc)) + return -EACCES; + + trace_fuse_iomap_fiemap(inode, start, count, fieinfo->fi_flags); + + inode_lock_shared(inode); + error = iomap_fiemap(inode, fieinfo, start, count, + &fuse_iomap_ops); + inode_unlock_shared(inode); + + return error; +} + +sector_t fuse_iomap_bmap(struct address_space *mapping, sector_t block) +{ + ASSERT(fuse_has_iomap(mapping->host)); + + return iomap_bmap(mapping, block, &fuse_iomap_ops); +} + +loff_t fuse_iomap_lseek(struct file *file, loff_t offset, int whence) +{ + struct inode *inode = file->f_mapping->host; + struct fuse_conn *fc = get_fuse_conn(inode); + + ASSERT(fuse_has_iomap(inode)); + + if (fuse_is_bad(inode)) + return -EIO; + + if (!fuse_allow_current_process(fc)) + return -EACCES; + + trace_fuse_iomap_lseek(inode, offset, whence); + + switch (whence) { + case SEEK_HOLE: + offset = iomap_seek_hole(inode, offset, &fuse_iomap_ops); + break; + case SEEK_DATA: + offset = iomap_seek_data(inode, offset, &fuse_iomap_ops); + break; + default: + return -ENOSYS; + } + + if (offset < 0) + return offset; + return vfs_setpos(file, offset, inode->i_sb->s_maxbytes); +}