diff options
Diffstat (limited to 'lib/ext2fs/fileio.c')
-rw-r--r-- | lib/ext2fs/fileio.c | 105 |
1 files changed, 97 insertions, 8 deletions
diff --git a/lib/ext2fs/fileio.c b/lib/ext2fs/fileio.c index 1f7002cd..5a39c321 100644 --- a/lib/ext2fs/fileio.c +++ b/lib/ext2fs/fileio.c @@ -18,6 +18,7 @@ #include "ext2_fs.h" #include "ext2fs.h" +#include "ext2fsP.h" struct ext2_file { errcode_t magic; @@ -142,8 +143,7 @@ errcode_t ext2fs_file_flush(ext2_file_t file) return retval; } - retval = io_channel_write_blk(fs->io, file->physblock, - 1, file->buf); + retval = io_channel_write_blk64(fs->io, file->physblock, 1, file->buf); if (retval) return retval; @@ -158,7 +158,7 @@ errcode_t ext2fs_file_flush(ext2_file_t file) */ static errcode_t sync_buffer_position(ext2_file_t file) { - blk_t b; + blk64_t b; errcode_t retval; b = file->pos / file->fs->blocksize; @@ -194,9 +194,9 @@ static errcode_t load_buffer(ext2_file_t file, int dontfill) return retval; if (!dontfill) { if (file->physblock) { - retval = io_channel_read_blk(fs->io, - file->physblock, - 1, file->buf); + retval = io_channel_read_blk64(fs->io, + file->physblock, + 1, file->buf); if (retval) return retval; } else @@ -298,6 +298,20 @@ errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, if (retval) goto fail; + /* + * OK, the physical block hasn't been allocated yet. + * Allocate it. + */ + if (!file->physblock) { + retval = ext2fs_bmap2(fs, file->ino, &file->inode, + BMAP_BUFFER, + file->ino ? BMAP_ALLOC : 0, + file->blockno, 0, + &file->physblock); + if (retval) + goto fail; + } + file->flags |= EXT2_FILE_BUF_DIRTY; memcpy(file->buf+start, ptr, c); file->pos += c; @@ -307,6 +321,15 @@ errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, } fail: + /* Update inode size */ + if (count != 0 && EXT2_I_SIZE(&file->inode) < file->pos) { + errcode_t rc; + + rc = ext2fs_file_set_size2(file, file->pos); + if (retval == 0) + retval = rc; + } + if (written) *written = count; return retval; @@ -371,6 +394,53 @@ ext2_off_t ext2fs_file_get_size(ext2_file_t file) return size; } +/* Zero the parts of the last block that are past EOF. */ +static errcode_t ext2fs_file_zero_past_offset(ext2_file_t file, + ext2_off64_t offset) +{ + ext2_filsys fs = file->fs; + char *b = NULL; + ext2_off64_t off = offset % fs->blocksize; + blk64_t blk; + int ret_flags; + errcode_t retval; + + if (off == 0) + return 0; + + retval = sync_buffer_position(file); + if (retval) + return retval; + + /* Is there an initialized block at the end? */ + retval = ext2fs_bmap2(fs, file->ino, NULL, NULL, 0, + offset / fs->blocksize, &ret_flags, &blk); + if (retval) + return retval; + if ((blk == 0) || (ret_flags & BMAP_RET_UNINIT)) + return 0; + + /* Zero to the end of the block */ + retval = ext2fs_get_mem(fs->blocksize, &b); + if (retval) + return retval; + + /* Read/zero/write block */ + retval = io_channel_read_blk64(fs->io, blk, 1, b); + if (retval) + goto out; + + memset(b + off, 0, fs->blocksize - off); + + retval = io_channel_write_blk64(fs->io, blk, 1, b); + if (retval) + goto out; + +out: + ext2fs_free_mem(&b); + return retval; +} + /* * This function sets the size of the file, truncating it if necessary * @@ -383,11 +453,26 @@ errcode_t ext2fs_file_set_size2(ext2_file_t file, ext2_off64_t size) EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + if (size && ext2fs_file_block_offset_too_big(file->fs, &file->inode, + (size - 1) / file->fs->blocksize)) + return EXT2_ET_FILE_TOO_BIG; truncate_block = ((size + file->fs->blocksize - 1) >> - EXT2_BLOCK_SIZE_BITS(file->fs->super)) + 1; + EXT2_BLOCK_SIZE_BITS(file->fs->super)); old_size = EXT2_I_SIZE(&file->inode); old_truncate = ((old_size + file->fs->blocksize - 1) >> - EXT2_BLOCK_SIZE_BITS(file->fs->super)) + 1; + EXT2_BLOCK_SIZE_BITS(file->fs->super)); + + /* If we're writing a large file, set the large_file flag */ + if (LINUX_S_ISREG(file->inode.i_mode) && + ext2fs_needs_large_file_feature(EXT2_I_SIZE(&file->inode)) && + (!EXT2_HAS_RO_COMPAT_FEATURE(file->fs->super, + EXT2_FEATURE_RO_COMPAT_LARGE_FILE) || + file->fs->super->s_rev_level == EXT2_GOOD_OLD_REV)) { + file->fs->super->s_feature_ro_compat |= + EXT2_FEATURE_RO_COMPAT_LARGE_FILE; + ext2fs_update_dynamic_rev(file->fs); + ext2fs_mark_super_dirty(file->fs); + } file->inode.i_size = size & 0xffffffff; file->inode.i_size_high = (size >> 32); @@ -397,6 +482,10 @@ errcode_t ext2fs_file_set_size2(ext2_file_t file, ext2_off64_t size) return retval; } + retval = ext2fs_file_zero_past_offset(file, size); + if (retval) + return retval; + if (truncate_block >= old_truncate) return 0; |