/* * Functions to read and write a single inode. */ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode, int bufsize) { blk64_t block_nr; unsigned long group, block, offset; char *ptr; errcode_t retval; unsigned i; int clen, inodes_per_block; io_channel io; int length = EXT2_INODE_SIZE(fs->super); struct ext2_inode_large *iptr; int cache_slot, fail_csum; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); /* 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; } 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 = ext2fs_create_inode_cache(fs, 4); if (retval) return retval; } /* Check to see if it's in the inode cache */ for (i = 0; i < fs->icache->cache_size; i++) { if (fs->icache->cache[i].ino == ino) { memcpy(inode, fs->icache->cache[i].inode, (bufsize > length) ? length : bufsize); 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); cache_slot = (fs->icache->cache_last + 1) % fs->icache->cache_size; iptr = (struct ext2_inode_large *)fs->icache->cache[cache_slot].inode; ptr = (char *) iptr; 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; ptr += clen; block_nr++; } length = EXT2_INODE_SIZE(fs->super); /* Verify the inode checksum. */ fail_csum = !ext2fs_inode_csum_verify(fs, ino, iptr); #ifdef WORDS_BIGENDIAN ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) iptr, (struct ext2_inode_large *) iptr, 0, length); #endif /* Update the inode cache bookkeeping */ if (!fail_csum) { fs->icache->cache_last = cache_slot; fs->icache->cache[cache_slot].ino = ino; } memcpy(inode, iptr, (bufsize > length) ? length : bufsize); if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) && fail_csum) return EXT2_ET_INODE_CSUM_INVALID; return 0; }
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 *w_inode; char *ptr; unsigned i; int clen; int length = EXT2_INODE_SIZE(fs->super); 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; } if ((ino == 0) || (ino > fs->super->s_inodes_count)) return EXT2_ET_BAD_INODE_NUM; /* Prepare our shadow buffer for read/modify/byteswap/write */ retval = ext2fs_get_mem(length, &w_inode); if (retval) return retval; if (bufsize < length) { int old_flags = fs->flags; fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; retval = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)w_inode, length); fs->flags = (old_flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) | (fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS); if (retval) goto errout; } /* 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) { memcpy(fs->icache->cache[i].inode, inode, (bufsize > length) ? length : bufsize); break; } } } else { retval = ext2fs_create_inode_cache(fs, 4); if (retval) goto errout; } memcpy(w_inode, inode, (bufsize > length) ? length : bufsize); if (!(fs->flags & EXT2_FLAG_RW)) { retval = EXT2_ET_RO_FILSYS; goto errout; } #ifdef WORDS_BIGENDIAN ext2fs_swap_inode_full(fs, w_inode, w_inode, 1, length); #endif retval = ext2fs_inode_csum_set(fs, ino, w_inode); if (retval) goto errout; 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); 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 *) 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; } memcpy((char *) fs->icache->buffer + (unsigned) offset, ptr, clen); retval = io_channel_write_blk64(fs->io, block_nr, 1, fs->icache->buffer); if (retval) goto errout; offset = 0; ptr += clen; length -= clen; block_nr++; } fs->flags |= EXT2_FLAG_CHANGED; errout: ext2fs_free_mem(&w_inode); return retval; }
int main(int argc, char *argv[]) { ext2_filsys fs; struct ext2_super_block param; errcode_t retval; int i; /* setup */ initialize_ext2_error_table(); memset(¶m, 0, sizeof(param)); ext2fs_blocks_count_set(¶m, 32768); param.s_inodes_count = 100; param.s_feature_incompat |= EXT4_FEATURE_INCOMPAT_INLINE_DATA; param.s_rev_level = EXT2_DYNAMIC_REV; param.s_inode_size = 256; retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, ¶m, test_io_manager, &fs); if (retval) { com_err("setup", retval, "while initializing filesystem"); exit(1); } retval = ext2fs_allocate_tables(fs); if (retval) { com_err("setup", retval, "while allocating tables for test filesysmte"); exit(1); } /* initialize inode cache */ if (!fs->icache) { struct ext2_inode inode; ext2_ino_t first_ino = EXT2_FIRST_INO(fs->super); int i; /* we just want to init inode cache. So ignore error */ ext2fs_create_inode_cache(fs, 16); if (!fs->icache) { fprintf(stderr, "tst_inline_data: init inode cache failed\n"); exit(1); } /* setup inode cache */ for (i = 0; i < fs->icache->cache_size; i++) fs->icache->cache[i].ino = first_ino++; } /* test */ if (file_test(fs)) { fprintf(stderr, "tst_inline_data(FILE): FAILED\n"); return 1; } printf("tst_inline_data(FILE): OK\n"); if (dir_test(fs)) { fprintf(stderr, "tst_inline_data(DIR): FAILED\n"); return 1; } printf("tst_inline_data(DIR): OK\n"); return 0; }