ext4: Handle different max offsets for bitmap & extent-based files
Subsystems (2)
cover ext4: Handle different max offsets for bitmap & extent-based files
Basic approach: have both ext4_max_bitmap_size() and ext4_max_size() functions to compute max offsets for both types of formats.
Use vfs sb->s_maxbytes for the "native" maxbytes, i.e. extent-format files.
Put the smaller bitmap limit in a new sbi->s_bitmap_maxbytes in the ext4 superblock info structure.
Catch bitmap files in ext4_file_write() and ext4_setattr() to limit extending writes, llseeks, and truncates to too-large offsets which the VFS let through due to the extent-format maxbytes. On write, allow writes up to the max, but then stop, by using iov_shorten() to limit the size of the write to the maximum.
3 patches follow:
ext4_two_maxbytes_functions.patch - differentiate the maxbytes f'ns ext4_bitmap_maxbytes_vfs.patch - export iov_shorten from kernel ext4_bitmap_maxbytes.patch - store, and limit to, bitmap_maxbytes
Comments?
Thanks,
-Eric
PATCH 1/3 ext4: different maxbytes functions for bitmap & extent files
use 2 different maxbytes functions for bitmapped & extent-based files.
---
fs/ext4/super.c +43 -3
@@ -1656,17 +1656,57 @@ static void ext4_orphan_cleanup (struct }/* -* Maximal file size. There is a direct, and {,double-,triple-}indirect +* Maximal extent format file size. +* Resulting logical blkno at s_maxbytes must fit in our on-disk +* extent format containers, within a sector_t, and within i_blocks +* in the vfs. ext4 inode has 48 bits of i_block in fsblock units, +* so that won't be a limiting factor. +* +* Note, this does *not* consider any metadata overhead for vfs i_blocks. +*/ +static loff_t ext4_max_size (int blkbits) +{ +loff_t res; +loff_t upper_limit =MAX_LFS_FILESIZE ; + +/* small i_blocks in vfs inode? */ +if (sizeof (blkcnt_t) <sizeof (u64)) { +/* +* CONFIG_LSF is not enabled implies the inode +* i_block represent total blocks in 512 bytes +* 32 == size of vfs inode i_blocks * 8 +*/ + upper_limit = (1LL <<32 ) -1 ; + +/* total blocks in file system block size */ + upper_limit >>= (blkbits -9 ); + upper_limit <<= blkbits; + } + +/* 32-bit extent-start container, ee_block */ + res =1LL <<32 ; + res <<= blkbits; + res -=1 ; + +/* Sanity check against vm- & vfs- imposed limits */ +if (res > upper_limit) + res = upper_limit; + +return res; +} + +/* +* Maximal bitmap file size. There is a direct, and {,double-,triple-}indirect * block limit, and also a limit of (248 - 1) 512-byte sectors in i_blocks. * We need to be 1 filesystem block less than the 248 sector limit. */ -static loff_t ext4_max_size (int bits) +static loff_t ext4_max_bitmap_size (int bits) {loff_t res =EXT4_NDIR_BLOCKS ;int meta_blocks;loff_t upper_limit;/* This is calculated to be the largest file size for a -* dense, file such that the total number of +* dense, bitmapped file such that the total number of * sectors in the file, including data and all indirect blocks, * does not exceed 248 -1 * __u32 i_blocks_lo and _u16 i_blocks_high representing the
Re: [PATCH 1/3] ext4: different maxbytes functions for bitmap & extent files
fs/ext4/super.c static void ext4_orphan_cleanup (struct · line 1673
Quoted 7 lines · Eric Sandeen
fs/ext4/super.c +7
-0+static loff_t ext4_max_size (int blkbits) +{ +loff_t res; +loff_t upper_limit =MAX_LFS_FILESIZE ; + +/* small i_blocks in vfs inode? */ +if (sizeof (blkcnt_t) <sizeof (u64)) {
It would probably be more future-proof by having a struct inode here and using "if (sizeof(inode->i_blocks) < sizeof(__u64))".
fs/ext4/super.c static void ext4_orphan_cleanup (struct · line 1683
Quoted 5 lines
fs/ext4/super.c +5
-0+ upper_limit = (1LL <<32 ) -1 ; + +/* total blocks in file system block size */ + upper_limit >>= (blkbits -9 ); + upper_limit <<= blkbits;
There is probably a cleaner way to do this, maybe:
upper_limit = (1LL << (32 + 9) - 1) & ~((1 << blkbits) - 1);
Cheers, Andreas
--
Andreas Dilger
Sr. Staff Engineer, Lustre Group
Sun Microsystems of Canada, Inc.
Re: [PATCH 1/3] ext4: different maxbytes functions for bitmap & extent files
Quoted 11 lines · Andreas Dilger
fs/ext4/super.c static void ext4_orphan_cleanup (struct · line 1673
Quoted 7 lines · Eric Sandeen
fs/ext4/super.c +7
-0+static loff_t ext4_max_size (int blkbits) +{ +loff_t res; +loff_t upper_limit =MAX_LFS_FILESIZE ; + +/* small i_blocks in vfs inode? */ +if (sizeof (blkcnt_t) <sizeof (u64)) {It would probably be more future-proof by having a struct inode here and using "if (
sizeof(inode->i_blocks)<sizeof(__u64))".
Ok. Honestly these were both just lifted from the original max_size function for bitmap files :)
-Eric
Quoted 16 lines
fs/ext4/super.c static void ext4_orphan_cleanup (struct · line 1683
Quoted 5 lines
fs/ext4/super.c +5
-0+ upper_limit = (1LL <<32 ) -1 ; + +/* total blocks in file system block size */ + upper_limit >>= (blkbits -9 ); + upper_limit <<= blkbits;There is probably a cleaner way to do this, maybe:
upper_limit = (1LL << (32 + 9) - 1) & ~((1 << blkbits) - 1);Cheers, Andreas
--
Andreas Dilger
Sr. Staff Engineer, Lustre Group
Sun Microsystems of Canada, Inc.
PATCH 2/3 ext4: export iov_shorten from kernel for ext4's use
In reply to Eric SandeenExport iov_shorten() from kernel so that ext4 can
truncate too-large writes to bitmapped files.
---
fs/read_write.c +2 -0
@@ -451,6 +451,8 @@ unsigned long iov_shorten(struct iovec *return seg; } +EXPORT_SYMBOL (iov_shorten); +ssize_t do_sync_readv_writev (struct file *filp,const struct iovec *iov,unsigned long nr_segs,size_t len,loff_t *ppos,iov_fn_t fn) {
PATCH 3/3 ext4: store maxbytes for bitmapped files and return EFBIG as appropriate
In reply to Eric SandeenCalculate & store the max offset for bitmapped files, and catch too-large seeks, truncates, and writes in ext4, shortening or rejecting as appropriate.
---
fs/ext4/super.c +1 -0
@@ -1979,6 +1979,7 @@ static int ext4_fill_super (struct super } } + sbi->s_bitmap_maxbytes =ext4_max_bitmap_size (sb->s_blocksize_bits); sb->s_maxbytes =ext4_max_size (sb->s_blocksize_bits);if (le32_to_cpu (es->s_rev_level) ==EXT4_GOOD_OLD_REV ) {
fs/ext4/file.c +18 -1
@@ -56,8 +56,25 @@ ext4_file_write(struct kiocb *iocb, consssize_t ret;int err; - ret =generic_file_aio_write (iocb, iov, nr_segs, pos); +/* +* If we have encountered a bitmap-format file, the size limit +* is smaller than s_maxbytes, which is for extent-mapped files. +*/ +if (!(EXT4_I (inode)->i_flags &EXT4_EXTENTS_FL )) { +struct ext4_sb_info *sbi =EXT4_SB (inode->i_sb); +size_t length =iov_length (iov, nr_segs); + +if (pos > sbi->s_bitmap_maxbytes) +return -EFBIG ; + +if (pos + length > sbi->s_bitmap_maxbytes) { + nr_segs =iov_shorten ((struct iovec *)iov, nr_segs, + sbi->s_bitmap_maxbytes - pos); + } + } + + ret =generic_file_aio_write (iocb, iov, nr_segs, pos);/* * Skip flushing if there was an error, or if nothing was written. */
fs/ext4/inode.c +14 -1
@@ -309,7 +309,9 @@ static int ext4_block_to_path(struct ino offsets[n++] = i_block & (ptrs -1 ); final = ptrs; }else { -ext4_warning (inode->i_sb,"ext4_block_to_path" ,"block > big" ); +ext4_warning (inode->i_sb,"ext4_block_to_path" ,"block %u > max" , + i_block + direct_blocks + + indirect_blocks + double_blocks); }if (boundary) *boundary = final -1 - (i_block & (ptrs -1 ));
@@ -3212,6 +3214,17 @@ int ext4_setattr(struct dentry *dentry,ext4_journal_stop (handle); } +if (attr->ia_valid &ATTR_SIZE ) { +if (!(EXT4_I (inode)->i_flags &EXT4_EXTENTS_FL )) { +struct ext4_sb_info *sbi =EXT4_SB (inode->i_sb); + +if (attr->ia_size > sbi->s_bitmap_maxbytes) { + error = -EFBIG ; + goto err_out; + } + } + } +if (S_ISREG (inode->i_mode) && attr->ia_valid &ATTR_SIZE && attr->ia_size < inode->i_size) {handle_t *handle;
include/linux/ext4_fs_sb.h +1 -0
@@ -38,6 +38,7 @@ struct ext4_sb_info {ext4_group_t s_groups_count;/* Number of groups in the fs */ unsigned long s_overhead_last;/* Last calculated overhead */ unsigned long s_blocks_last;/* Last seen block count */ +loff_t s_bitmap_maxbytes;/* max bytes for bitmap files */ struct buffer_head * s_sbh;/* Buffer containing the super block */ struct ext4_super_block * s_es;/* Pointer to the super block in the buffer */ struct buffer_head ** s_group_desc;
Re: [PATCH 3/3] ext4: (V2) store maxbytes for bitmapped files and return EFBIG as appropriate
Crud, minor off-by-one in ext4_file_write. V2 below:
===============================
Calculate & store the max offset for bitmapped files, and catch too-large seeks, truncates, and writes in ext4, shortening or rejecting as appropriate.
---
fs/ext4/super.c +1 -0
@@ -1979,6 +1979,7 @@ static int ext4_fill_super (struct super } } + sbi->s_bitmap_maxbytes =ext4_max_bitmap_size (sb->s_blocksize_bits); sb->s_maxbytes =ext4_max_size (sb->s_blocksize_bits);if (le32_to_cpu (es->s_rev_level) ==EXT4_GOOD_OLD_REV ) {
fs/ext4/file.c +22 -1
@@ -56,8 +56,29 @@ ext4_file_write(struct kiocb *iocb, consssize_t ret;int err; - ret =generic_file_aio_write (iocb, iov, nr_segs, pos); +/* +* If we have encountered a bitmap-format file, the size limit +* is smaller than s_maxbytes, which is for extent-mapped files. +*/ +if (!(EXT4_I (inode)->i_flags &EXT4_EXTENTS_FL )) { +struct ext4_sb_info *sbi =EXT4_SB (inode->i_sb); +size_t length =iov_length (iov, nr_segs); + +if (pos >= sbi->s_bitmap_maxbytes) { +if (length || pos > sbi->s_bitmap_maxbytes) { +return -EFBIG ; + } +/* zero-length writes at maxbytes are OK */ + } + +if (pos + length > sbi->s_bitmap_maxbytes) { + nr_segs =iov_shorten ((struct iovec *)iov, nr_segs, + sbi->s_bitmap_maxbytes - pos); + } + } + + ret =generic_file_aio_write (iocb, iov, nr_segs, pos);/* * Skip flushing if there was an error, or if nothing was written. */
fs/ext4/inode.c +14 -1
@@ -309,7 +309,9 @@ static int ext4_block_to_path(struct ino offsets[n++] = i_block & (ptrs -1 ); final = ptrs; }else { -ext4_warning (inode->i_sb,"ext4_block_to_path" ,"block > big" ); +ext4_warning (inode->i_sb,"ext4_block_to_path" ,"block %u > max" , + i_block + direct_blocks + + indirect_blocks + double_blocks); }if (boundary) *boundary = final -1 - (i_block & (ptrs -1 ));
@@ -3212,6 +3214,17 @@ int ext4_setattr(struct dentry *dentry,ext4_journal_stop (handle); } +if (attr->ia_valid &ATTR_SIZE ) { +if (!(EXT4_I (inode)->i_flags &EXT4_EXTENTS_FL )) { +struct ext4_sb_info *sbi =EXT4_SB (inode->i_sb); + +if (attr->ia_size > sbi->s_bitmap_maxbytes) { + error = -EFBIG ; + goto err_out; + } + } + } +if (S_ISREG (inode->i_mode) && attr->ia_valid &ATTR_SIZE && attr->ia_size < inode->i_size) {handle_t *handle;
include/linux/ext4_fs_sb.h +1 -0
@@ -38,6 +38,7 @@ struct ext4_sb_info {ext4_group_t s_groups_count;/* Number of groups in the fs */ unsigned long s_overhead_last;/* Last calculated overhead */ unsigned long s_blocks_last;/* Last seen block count */ +loff_t s_bitmap_maxbytes;/* max bytes for bitmap files */ struct buffer_head * s_sbh;/* Buffer containing the super block */ struct ext4_super_block * s_es;/* Pointer to the super block in the buffer */ struct buffer_head ** s_group_desc;