X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=lib%2Fext2fs%2Finode.c;h=b7805bcb13fc801d746bc8c5e4c0f6aadedd43b1;hb=refs%2Fchanges%2F69%2F18569%2F20;hp=04c5e4d1331c00ca3c86ce69e0821e4f4e7447b2;hpb=7f88b04341d88c5df0360d930832c38040303b61;p=tools%2Fe2fsprogs.git diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c index 04c5e4d..b7805bc 100644 --- a/lib/ext2fs/inode.c +++ b/lib/ext2fs/inode.c @@ -1,58 +1,185 @@ /* * inode.c --- utility routines to read and write inodes - * - * Copyright (C) 1993 Theodore Ts'o. This file may be redistributed - * under the terms of the GNU Public License. + * + * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% */ +#include "config.h" #include #include +#if HAVE_UNISTD_H #include -#include -#include -#include +#endif #if HAVE_ERRNO_H #include #endif +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" +#include "e2image.h" + +struct ext2_struct_inode_scan { + errcode_t magic; + ext2_filsys fs; + ext2_ino_t current_inode; + blk64_t current_block; + dgrp_t current_group; + ext2_ino_t inodes_left; + blk_t blocks_left; + dgrp_t groups_left; + blk_t inode_buffer_blocks; + char * inode_buffer; + int inode_size; + char * ptr; + int bytes_left; + char *temp_buffer; + errcode_t (*done_group)(ext2_filsys fs, + ext2_inode_scan scan, + dgrp_t group, + void * priv_data); + void * done_group_data; + int bad_block_ptr; + int scan_flags; + int reserved[6]; +}; + +/* + * This routine flushes the icache, if it exists. + */ +errcode_t ext2fs_flush_icache(ext2_filsys fs) +{ + int i; + + if (!fs->icache) + return 0; + + for (i=0; i < fs->icache->cache_size; i++) + fs->icache->cache[i].ino = 0; + + fs->icache->buffer_blk = 0; + return 0; +} -#include +static errcode_t create_icache(ext2_filsys fs) +{ + errcode_t retval; -#include "ext2fs.h" + if (fs->icache) + return 0; + retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache), &fs->icache); + if (retval) + return retval; -static void inocpy_with_swap(struct ext2_inode *t, struct ext2_inode *f); + memset(fs->icache, 0, sizeof(struct ext2_inode_cache)); + retval = ext2fs_get_mem(fs->blocksize, &fs->icache->buffer); + if (retval) { + ext2fs_free_mem(&fs->icache); + return retval; + } + fs->icache->buffer_blk = 0; + fs->icache->cache_last = -1; + fs->icache->cache_size = 4; + fs->icache->refcount = 1; + retval = ext2fs_get_array(fs->icache->cache_size, + sizeof(struct ext2_inode_cache_ent), + &fs->icache->cache); + if (retval) { + ext2fs_free_mem(&fs->icache->buffer); + ext2fs_free_mem(&fs->icache); + return retval; + } + ext2fs_flush_icache(fs); + return 0; +} errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks, ext2_inode_scan *ret_scan) { ext2_inode_scan scan; + errcode_t retval; + errcode_t (*save_get_blocks)(ext2_filsys f, ext2_ino_t ino, blk_t *blocks); EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); - scan = (ext2_inode_scan) malloc(sizeof(struct ext2_struct_inode_scan)); - if (!scan) - return ENOMEM; + /* + * If fs->badblocks isn't set, then set it --- since the inode + * scanning functions require it. + */ + if (fs->badblocks == 0) { + /* + * Temporarly save fs->get_blocks and set it to zero, + * for compatibility with old e2fsck's. + */ + save_get_blocks = fs->get_blocks; + fs->get_blocks = 0; + retval = ext2fs_read_bb_inode(fs, &fs->badblocks); + if (retval && fs->badblocks) { + ext2fs_badblocks_list_free(fs->badblocks); + fs->badblocks = 0; + } + fs->get_blocks = save_get_blocks; + } + + retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan), &scan); + if (retval) + return retval; memset(scan, 0, sizeof(struct ext2_struct_inode_scan)); scan->magic = EXT2_ET_MAGIC_INODE_SCAN; scan->fs = fs; scan->inode_size = EXT2_INODE_SIZE(fs->super); scan->bytes_left = 0; - scan->current_group = -1; + scan->current_group = 0; + scan->groups_left = fs->group_desc_count - 1; scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8; - scan->groups_left = fs->group_desc_count; - scan->inode_buffer = malloc(scan->inode_buffer_blocks * fs->blocksize); + scan->current_block = ext2fs_inode_table_loc(scan->fs, + scan->current_group); + scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super); + scan->blocks_left = scan->fs->inode_blocks_per_group; + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + __u32 unused = ext2fs_bg_itable_unused(fs, scan->current_group); + if (scan->inodes_left > unused) + scan->inodes_left -= unused; + else + scan->inodes_left = 0; + scan->blocks_left = + (scan->inodes_left + + (fs->blocksize / scan->inode_size - 1)) * + scan->inode_size / fs->blocksize; + } + retval = io_channel_alloc_buf(fs->io, scan->inode_buffer_blocks, + &scan->inode_buffer); scan->done_group = 0; scan->done_group_data = 0; - if (!scan->inode_buffer) { - free(scan); - return ENOMEM; + scan->bad_block_ptr = 0; + if (retval) { + ext2fs_free_mem(&scan); + return retval; } - scan->temp_buffer = malloc(scan->inode_size); - if (!scan->temp_buffer) { - free(scan->inode_buffer); - free(scan); - return ENOMEM; + retval = ext2fs_get_mem(scan->inode_size, &scan->temp_buffer); + if (retval) { + ext2fs_free_mem(&scan->inode_buffer); + ext2fs_free_mem(&scan); + return retval; } + if (scan->fs->badblocks && scan->fs->badblocks->num) + scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS; + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + scan->scan_flags |= EXT2_SF_DO_LAZY; *ret_scan = scan; return 0; } @@ -61,12 +188,12 @@ void ext2fs_close_inode_scan(ext2_inode_scan scan) { if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) return; - - free(scan->inode_buffer); + + ext2fs_free_mem(&scan->inode_buffer); scan->inode_buffer = NULL; - free(scan->temp_buffer); + ext2fs_free_mem(&scan->temp_buffer); scan->temp_buffer = NULL; - free(scan); + ext2fs_free_mem(&scan); return; } @@ -74,33 +201,229 @@ void ext2fs_set_inode_callback(ext2_inode_scan scan, errcode_t (*done_group)(ext2_filsys fs, ext2_inode_scan scan, dgrp_t group, - void * private), + void * priv_data), void *done_group_data) { if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) return; - + scan->done_group = done_group; scan->done_group_data = done_group_data; } -errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ino_t *ino, - struct ext2_inode *inode) +int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags, + int clear_flags) +{ + int old_flags; + + if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) + return 0; + + old_flags = scan->scan_flags; + scan->scan_flags &= ~clear_flags; + scan->scan_flags |= set_flags; + return old_flags; +} + +/* + * This function is called by ext2fs_get_next_inode when it needs to + * get ready to read in a new blockgroup. + */ +static errcode_t get_next_blockgroup(ext2_inode_scan scan) +{ + ext2_filsys fs = scan->fs; + + scan->current_group++; + scan->groups_left--; + + scan->current_block = ext2fs_inode_table_loc(scan->fs, + scan->current_group); + scan->current_inode = scan->current_group * + EXT2_INODES_PER_GROUP(fs->super); + + scan->bytes_left = 0; + scan->inodes_left = EXT2_INODES_PER_GROUP(fs->super); + scan->blocks_left = fs->inode_blocks_per_group; + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + __u32 unused = ext2fs_bg_itable_unused(fs, scan->current_group); + if (scan->inodes_left > unused) + scan->inodes_left -= unused; + else + scan->inodes_left = 0; + scan->blocks_left = + (scan->inodes_left + + (fs->blocksize / scan->inode_size - 1)) * + scan->inode_size / fs->blocksize; + } + + return 0; +} + +errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan, + int group) +{ + scan->current_group = group - 1; + scan->groups_left = scan->fs->group_desc_count - group; + return get_next_blockgroup(scan); +} + +/* + * This function is called by get_next_blocks() to check for bad + * blocks in the inode table. + * + * This function assumes that badblocks_list->list is sorted in + * increasing order. + */ +static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan, + blk64_t *num_blocks) +{ + blk64_t blk = scan->current_block; + badblocks_list bb = scan->fs->badblocks; + + /* + * If the inode table is missing, then obviously there are no + * bad blocks. :-) + */ + if (blk == 0) + return 0; + + /* + * If the current block is greater than the bad block listed + * in the bad block list, then advance the pointer until this + * is no longer the case. If we run out of bad blocks, then + * we don't need to do any more checking! + */ + while (blk > bb->list[scan->bad_block_ptr]) { + if (++scan->bad_block_ptr >= bb->num) { + scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS; + return 0; + } + } + + /* + * If the current block is equal to the bad block listed in + * the bad block list, then handle that one block specially. + * (We could try to handle runs of bad blocks, but that + * only increases CPU efficiency by a small amount, at the + * expense of a huge expense of code complexity, and for an + * uncommon case at that.) + */ + if (blk == bb->list[scan->bad_block_ptr]) { + scan->scan_flags |= EXT2_SF_BAD_INODE_BLK; + *num_blocks = 1; + if (++scan->bad_block_ptr >= bb->num) + scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS; + return 0; + } + + /* + * If there is a bad block in the range that we're about to + * read in, adjust the number of blocks to read so that we we + * don't read in the bad block. (Then the next block to read + * will be the bad block, which is handled in the above case.) + */ + if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr]) + *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk); + + return 0; +} + +/* + * This function is called by ext2fs_get_next_inode when it needs to + * read in more blocks from the current blockgroup's inode table. + */ +static errcode_t get_next_blocks(ext2_inode_scan scan) +{ + blk64_t num_blocks; + errcode_t retval; + + /* + * Figure out how many blocks to read; we read at most + * inode_buffer_blocks, and perhaps less if there aren't that + * many blocks left to read. + */ + num_blocks = scan->inode_buffer_blocks; + if (num_blocks > scan->blocks_left) + num_blocks = scan->blocks_left; + + /* + * If the past block "read" was a bad block, then mark the + * left-over extra bytes as also being bad. + */ + if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) { + if (scan->bytes_left) + scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES; + scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK; + } + + /* + * Do inode bad block processing, if necessary. + */ + if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) { + retval = check_for_inode_bad_blocks(scan, &num_blocks); + if (retval) + return retval; + } + + if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) || + (scan->current_block == 0)) { + memset(scan->inode_buffer, 0, + (size_t) num_blocks * scan->fs->blocksize); + } else { + retval = io_channel_read_blk64(scan->fs->io, + scan->current_block, + (int) num_blocks, + scan->inode_buffer); + if (retval) + return EXT2_ET_NEXT_INODE_READ; + } + scan->ptr = scan->inode_buffer; + scan->bytes_left = num_blocks * scan->fs->blocksize; + + scan->blocks_left -= num_blocks; + if (scan->current_block) + scan->current_block += num_blocks; + return 0; +} + +#if 0 +/* + * Returns 1 if the entire inode_buffer has a non-zero size and + * contains all zeros. (Not just deleted inodes, since that means + * that part of the inode table was used at one point; we want all + * zeros, which means that the inode table is pristine.) + */ +static inline int is_empty_scan(ext2_inode_scan scan) +{ + int i; + + if (scan->bytes_left == 0) + return 0; + + for (i=0; i < scan->bytes_left; i++) + if (scan->ptr[i]) + return 0; + return 1; +} +#endif + +errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino, + struct ext2_inode *inode, int bufsize) { errcode_t retval; - int num_blocks; int extra_bytes = 0; - + EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN); /* * Do we need to start reading a new block group? */ if (scan->inodes_left <= 0) { + force_new_group: if (scan->done_group) { retval = (scan->done_group) - (scan->fs, scan, - scan->current_group, + (scan->fs, scan, scan->current_group, scan->done_group_data); if (retval) return retval; @@ -109,19 +432,28 @@ errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ino_t *ino, *ino = 0; return 0; } - scan->current_group++; - scan->groups_left--; - - scan->current_block = scan->fs-> - group_desc[scan->current_group].bg_inode_table; - - if (scan->current_block == 0) + retval = get_next_blockgroup(scan); + if (retval) + return retval; + } + /* + * These checks are done outside the above if statement so + * they can be done for block group #0. + */ + if ((scan->scan_flags & EXT2_SF_DO_LAZY) && + (ext2fs_bg_flags_test(scan->fs, scan->current_group, EXT2_BG_INODE_UNINIT) + )) + goto force_new_group; + if (scan->inodes_left == 0) + goto force_new_group; + if (scan->current_block == 0) { + if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) { + goto force_new_group; + } else return EXT2_ET_MISSING_INODE_TABLE; - scan->bytes_left = 0; - scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super); - scan->blocks_left = scan->fs->inode_blocks_per_group; } + /* * Have we run out of space in the inode buffer? If so, we * need to read in more blocks. @@ -129,211 +461,350 @@ errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ino_t *ino, if (scan->bytes_left < scan->inode_size) { memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left); extra_bytes = scan->bytes_left; - - scan->blocks_left -= scan->inode_buffer_blocks; - num_blocks = scan->inode_buffer_blocks; - if (scan->blocks_left < 0) - num_blocks += scan->blocks_left; - - retval = io_channel_read_blk(scan->fs->io, scan->current_block, - num_blocks, scan->inode_buffer); + + retval = get_next_blocks(scan); if (retval) - return EXT2_ET_NEXT_INODE_READ; - scan->ptr = scan->inode_buffer; - scan->bytes_left = num_blocks * scan->fs->blocksize; - - scan->current_block += scan->inode_buffer_blocks; + return retval; +#if 0 + /* + * XXX test Need check for used inode somehow. + * (Note: this is hard.) + */ + if (is_empty_scan(scan)) + goto force_new_group; +#endif } + retval = 0; if (extra_bytes) { memcpy(scan->temp_buffer+extra_bytes, scan->ptr, scan->inode_size - extra_bytes); scan->ptr += scan->inode_size - extra_bytes; scan->bytes_left -= scan->inode_size - extra_bytes; - if (scan->fs->flags & EXT2_SWAP_BYTES) - inocpy_with_swap(inode, (struct ext2_inode *) - scan->temp_buffer); - else - *inode = *((struct ext2_inode *) scan->temp_buffer); +#ifdef WORDS_BIGENDIAN + memset(inode, 0, bufsize); + ext2fs_swap_inode_full(scan->fs, + (struct ext2_inode_large *) inode, + (struct ext2_inode_large *) scan->temp_buffer, + 0, bufsize); +#else + memcpy(inode, scan->temp_buffer, bufsize); +#endif + if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES) + retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE; + scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES; } else { - if (scan->fs->flags & EXT2_SWAP_BYTES) - inocpy_with_swap(inode, (struct ext2_inode *) - scan->ptr); - else - *inode = *((struct ext2_inode *) scan->ptr); +#ifdef WORDS_BIGENDIAN + memset(inode, 0, bufsize); + ext2fs_swap_inode_full(scan->fs, + (struct ext2_inode_large *) inode, + (struct ext2_inode_large *) scan->ptr, + 0, bufsize); +#else + memcpy(inode, scan->ptr, bufsize); +#endif scan->ptr += scan->inode_size; scan->bytes_left -= scan->inode_size; + if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) + retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE; } scan->inodes_left--; scan->current_inode++; *ino = scan->current_inode; - return 0; + return retval; +} + +errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino, + struct ext2_inode *inode) +{ + return ext2fs_get_next_inode_full(scan, ino, inode, + sizeof(struct ext2_inode)); } /* * Functions to read and write a single inode. */ -static char *inode_buffer = 0; -static blk_t inode_buffer_block = 0; -static int inode_buffer_size = 0; - -errcode_t ext2fs_read_inode (ext2_filsys fs, unsigned long ino, - struct ext2_inode * inode) +errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode, int bufsize) { - unsigned long group, block, block_nr, offset; + blk64_t block_nr; + unsigned long group, block, offset; char *ptr; errcode_t retval; - int clen, length; + int clen, i, inodes_per_block, length; + io_channel io; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); - if (ino > fs->super->s_inodes_count) - return EXT2_ET_BAD_INODE_NUM; - if (inode_buffer_size != fs->blocksize) { - if (inode_buffer) - free(inode_buffer); - inode_buffer_size = 0; - inode_buffer = malloc(fs->blocksize); - if (!inode_buffer) - return ENOMEM; - inode_buffer_size = fs->blocksize; - inode_buffer_block = 0; + /* Check to see if user has an override function */ + if (fs->read_inode && + ((bufsize == sizeof(struct ext2_inode)) || + (EXT2_INODE_SIZE(fs->super) == sizeof(struct ext2_inode)))) { + retval = (fs->read_inode)(fs, ino, inode); + if (retval != EXT2_ET_CALLBACK_NOTHANDLED) + return retval; } - - group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); - offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * - EXT2_INODE_SIZE(fs->super); - block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); - block_nr = fs->group_desc[group].bg_inode_table + block; - if (block_nr != inode_buffer_block) { - retval = io_channel_read_blk(fs->io, block_nr, 1, - inode_buffer); + if ((ino == 0) || (ino > fs->super->s_inodes_count)) + return EXT2_ET_BAD_INODE_NUM; + /* Create inode cache if not present */ + if (!fs->icache) { + retval = create_icache(fs); if (retval) return retval; - inode_buffer_block = block_nr; + } + /* Check to see if it's in the inode cache */ + if (bufsize == sizeof(struct ext2_inode)) { + /* only old good inode can be retrieved from the cache */ + for (i=0; i < fs->icache->cache_size; i++) { + if (fs->icache->cache[i].ino == ino) { + *inode = fs->icache->cache[i].inode; + return 0; + } + } + } + if (fs->flags & EXT2_FLAG_IMAGE_FILE) { + inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super); + block_nr = fs->image_header->offset_inode / fs->blocksize; + block_nr += (ino - 1) / inodes_per_block; + offset = ((ino - 1) % inodes_per_block) * + EXT2_INODE_SIZE(fs->super); + io = fs->image_io; + } else { + group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); + if (group > fs->group_desc_count) + return EXT2_ET_BAD_INODE_NUM; + offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * + EXT2_INODE_SIZE(fs->super); + block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); + if (!ext2fs_inode_table_loc(fs, (unsigned) group)) + return EXT2_ET_MISSING_INODE_TABLE; + block_nr = ext2fs_inode_table_loc(fs, group) + + block; + io = fs->io; } offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); - ptr = ((char *) inode_buffer) + offset; - memset(inode, 0, sizeof(struct ext2_inode)); length = EXT2_INODE_SIZE(fs->super); - if (length > sizeof(struct ext2_inode)) - length = sizeof(struct ext2_inode); - - if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) { - clen = EXT2_BLOCK_SIZE(fs->super) - offset; - memcpy((char *) inode, ptr, clen); + if (bufsize < length) + length = bufsize; + + ptr = (char *) inode; + while (length) { + clen = length; + if ((offset + length) > fs->blocksize) + clen = fs->blocksize - offset; + + if (block_nr != fs->icache->buffer_blk) { + retval = io_channel_read_blk64(io, block_nr, 1, + fs->icache->buffer); + if (retval) + return retval; + fs->icache->buffer_blk = block_nr; + } + + memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset, + clen); + + offset = 0; length -= clen; - - retval = io_channel_read_blk(fs->io, block_nr+1, 1, - inode_buffer); - if (retval) - return retval; - inode_buffer_block = block_nr+1; - - memcpy(((char *) inode) + clen, - inode_buffer, length); - } else - memcpy((char *) inode, ptr, length); - - if (fs->flags & EXT2_SWAP_BYTES) - inocpy_with_swap(inode, inode); + ptr += clen; + block_nr++; + } + +#ifdef WORDS_BIGENDIAN + ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode, + (struct ext2_inode_large *) inode, + 0, bufsize); +#endif + + /* Update the inode cache */ + fs->icache->cache_last = (fs->icache->cache_last + 1) % + fs->icache->cache_size; + fs->icache->cache[fs->icache->cache_last].ino = ino; + fs->icache->cache[fs->icache->cache_last].inode = *inode; return 0; } -errcode_t ext2fs_write_inode(ext2_filsys fs, unsigned long ino, - struct ext2_inode * inode) +errcode_t ext2fs_read_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode) { - unsigned long group, block, block_nr, offset; - errcode_t retval; - struct ext2_inode temp_inode; + return ext2fs_read_inode_full(fs, ino, inode, + sizeof(struct ext2_inode)); +} + +errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode, int bufsize) +{ + blk64_t block_nr; + unsigned long group, block, offset; + errcode_t retval = 0; + struct ext2_inode_large temp_inode, *w_inode; char *ptr; - int i, clen, length; + int clen, i, length; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + /* Check to see if user provided an override function */ + if (fs->write_inode) { + retval = (fs->write_inode)(fs, ino, inode); + if (retval != EXT2_ET_CALLBACK_NOTHANDLED) + return retval; + } + + /* Check to see if the inode cache needs to be updated */ + if (fs->icache) { + for (i=0; i < fs->icache->cache_size; i++) { + if (fs->icache->cache[i].ino == ino) { + fs->icache->cache[i].inode = *inode; + break; + } + } + } else { + retval = create_icache(fs); + if (retval) + return retval; + } + if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; - if (ino > fs->super->s_inodes_count) + if ((ino == 0) || (ino > fs->super->s_inodes_count)) return EXT2_ET_BAD_INODE_NUM; - if (inode_buffer_size != fs->blocksize) { - if (inode_buffer) - free(inode_buffer); - inode_buffer_size = 0; - inode_buffer = malloc(fs->blocksize); - if (!inode_buffer) - return ENOMEM; - inode_buffer_size = fs->blocksize; - inode_buffer_block = 0; - } - if (fs->flags & EXT2_SWAP_BYTES) - inocpy_with_swap(&temp_inode, inode); - else - memcpy(&temp_inode, inode, sizeof(struct ext2_inode)); - + length = bufsize; + if (length < EXT2_INODE_SIZE(fs->super)) + length = EXT2_INODE_SIZE(fs->super); + + if (length > (int) sizeof(struct ext2_inode_large)) { + w_inode = malloc(length); + if (!w_inode) { + retval = ENOMEM; + goto errout; + } + } else + w_inode = &temp_inode; + memset(w_inode, 0, length); + +#ifdef WORDS_BIGENDIAN + ext2fs_swap_inode_full(fs, w_inode, + (struct ext2_inode_large *) inode, + 1, bufsize); +#else + memcpy(w_inode, inode, bufsize); +#endif + group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * EXT2_INODE_SIZE(fs->super); block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); - block_nr = fs->group_desc[group].bg_inode_table + block; + if (!ext2fs_inode_table_loc(fs, (unsigned) group)) { + retval = EXT2_ET_MISSING_INODE_TABLE; + goto errout; + } + block_nr = ext2fs_inode_table_loc(fs, (unsigned) group) + block; + offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); - ptr = (char *) inode_buffer + offset; length = EXT2_INODE_SIZE(fs->super); - if (length > sizeof(struct ext2_inode)) - length = sizeof(struct ext2_inode); - - if (inode_buffer_block != block_nr) { - retval = io_channel_read_blk(fs->io, block_nr, 1, - inode_buffer); - if (retval) - return retval; - inode_buffer_block = block_nr; - } - - if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) { - clen = EXT2_BLOCK_SIZE(fs->super) - offset; - memcpy(ptr, &temp_inode, clen); - length -= clen; - } else { - memcpy(ptr, &temp_inode, length); - length = 0; - } - retval = io_channel_write_blk(fs->io, block_nr, 1, inode_buffer); - if (retval) - return retval; + if (length > bufsize) + length = bufsize; - if (length) { - retval = io_channel_read_blk(fs->io, ++block_nr, 1, - inode_buffer); - if (retval) { - inode_buffer_block = 0; - return retval; + ptr = (char *) w_inode; + + while (length) { + clen = length; + if ((offset + length) > fs->blocksize) + clen = fs->blocksize - offset; + + if (fs->icache->buffer_blk != block_nr) { + retval = io_channel_read_blk64(fs->io, block_nr, 1, + fs->icache->buffer); + if (retval) + goto errout; + fs->icache->buffer_blk = block_nr; } - inode_buffer_block = block_nr; - memcpy(inode_buffer, ((char *) &temp_inode) + clen, length); - retval = io_channel_write_blk(fs->io, block_nr, 1, - inode_buffer); + + memcpy((char *) fs->icache->buffer + (unsigned) offset, + ptr, clen); + + retval = io_channel_write_blk64(fs->io, block_nr, 1, + fs->icache->buffer); if (retval) - return retval; + goto errout; + + offset = 0; + ptr += clen; + length -= clen; + block_nr++; } - + fs->flags |= EXT2_FLAG_CHANGED; - return 0; +errout: + if (w_inode && w_inode != &temp_inode) + free(w_inode); + return retval; } -errcode_t ext2fs_get_blocks(ext2_filsys fs, ino_t ino, blk_t *blocks) +errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode) +{ + return ext2fs_write_inode_full(fs, ino, inode, + sizeof(struct ext2_inode)); +} + +/* + * This function should be called when writing a new inode. It makes + * sure that extra part of large inodes is initialized properly. + */ +errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode) +{ + struct ext2_inode *buf; + int size = EXT2_INODE_SIZE(fs->super); + struct ext2_inode_large *large_inode; + errcode_t retval; + __u32 t = fs->now ? fs->now : time(NULL); + + if (!inode->i_ctime) + inode->i_ctime = t; + if (!inode->i_mtime) + inode->i_mtime = t; + if (!inode->i_atime) + inode->i_atime = t; + + if (size == sizeof(struct ext2_inode)) + return ext2fs_write_inode_full(fs, ino, inode, + sizeof(struct ext2_inode)); + + buf = malloc(size); + if (!buf) + return ENOMEM; + + memset(buf, 0, size); + *buf = *inode; + + large_inode = (struct ext2_inode_large *) buf; + large_inode->i_extra_isize = sizeof(struct ext2_inode_large) - + EXT2_GOOD_OLD_INODE_SIZE; + if (!large_inode->i_crtime) + large_inode->i_crtime = t; + + retval = ext2fs_write_inode_full(fs, ino, buf, size); + free(buf); + return retval; +} + + +errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks) { struct ext2_inode inode; int i; errcode_t retval; - + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (ino > fs->super->s_inodes_count) @@ -351,48 +822,26 @@ errcode_t ext2fs_get_blocks(ext2_filsys fs, ino_t ino, blk_t *blocks) return 0; } -errcode_t ext2fs_check_directory(ext2_filsys fs, ino_t ino) +errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino) { struct ext2_inode inode; errcode_t retval; - + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (ino > fs->super->s_inodes_count) return EXT2_ET_BAD_INODE_NUM; - if (fs->check_directory) - return (fs->check_directory)(fs, ino); + if (fs->check_directory) { + retval = (fs->check_directory)(fs, ino); + if (retval != EXT2_ET_CALLBACK_NOTHANDLED) + return retval; + } retval = ext2fs_read_inode(fs, ino, &inode); if (retval) return retval; if (!LINUX_S_ISDIR(inode.i_mode)) - return ENOTDIR; + return EXT2_ET_NO_DIRECTORY; return 0; } -static void inocpy_with_swap(struct ext2_inode *t, struct ext2_inode *f) -{ - unsigned i; - - t->i_mode = ext2fs_swab16(f->i_mode); - t->i_uid = ext2fs_swab16(f->i_uid); - t->i_size = ext2fs_swab32(f->i_size); - t->i_atime = ext2fs_swab32(f->i_atime); - t->i_ctime = ext2fs_swab32(f->i_ctime); - t->i_mtime = ext2fs_swab32(f->i_mtime); - t->i_dtime = ext2fs_swab32(f->i_dtime); - t->i_gid = ext2fs_swab16(f->i_gid); - t->i_links_count = ext2fs_swab16(f->i_links_count); - t->i_blocks = ext2fs_swab32(f->i_blocks); - t->i_flags = ext2fs_swab32(f->i_flags); - for (i = 0; i < EXT2_N_BLOCKS; i++) - t->i_block[i] = ext2fs_swab32(f->i_block[i]); - t->i_version = ext2fs_swab32(f->i_version); - t->i_file_acl = ext2fs_swab32(f->i_file_acl); - t->i_dir_acl = ext2fs_swab32(f->i_dir_acl); - t->i_faddr = ext2fs_swab32(f->i_faddr); - t->osd2.linux2.l_i_frag = f->osd2.linux2.l_i_frag; - t->osd2.linux2.l_i_fsize = f->osd2.linux2.l_i_fsize; - t->osd2.linux2.i_pad1 = ext2fs_swab16(f->osd2.linux2.i_pad1); -}