diff options
author | Eric Sandeen <sandeen@redhat.com> | 2008-01-29 07:58:27 +0300 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2008-01-29 07:58:27 +0300 |
commit | e2b4657453c0d5571bd3c7256585c486ed42d364 (patch) | |
tree | 5b8ab501cdf5fa7427ef32440ace56eac72e9ecf /fs/ext4 | |
parent | 19295529db35381d46dbaf246f69b4e3b3393996 (diff) | |
download | linux-e2b4657453c0d5571bd3c7256585c486ed42d364.tar.xz |
ext4: store maxbytes for bitmapped files and return EFBIG as appropriate
Calculate & store the max offset for bitmapped files, and
catch too-large seeks, truncates, and writes in ext4, shortening
or rejecting as appropriate.
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/file.c | 19 | ||||
-rw-r--r-- | fs/ext4/inode.c | 16 | ||||
-rw-r--r-- | fs/ext4/super.c | 1 |
3 files changed, 34 insertions, 2 deletions
diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 1a81cd66d63b..a6b2aa14626e 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -56,8 +56,25 @@ ext4_file_write(struct kiocb *iocb, const struct iovec *iov, ssize_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. */ diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 9cf85721d83c..eaace1373ccb 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -314,7 +314,10 @@ static int ext4_block_to_path(struct inode *inode, 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)); @@ -3092,6 +3095,17 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) 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; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index c79e46b7f159..0931831537a2 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1922,6 +1922,7 @@ static int ext4_fill_super (struct super_block *sb, void *data, int silent) } } + 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) { |