static int check_filesystem(struct undo_context *ctx, io_channel channel) { struct ext2_super_block super, *sb; char *buf; __u32 sb_crc; errcode_t retval; io_channel_set_blksize(channel, SUPERBLOCK_OFFSET); retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super); if (retval) { com_err(prg_name, retval, "%s", _("while reading filesystem superblock.")); return retval; } /* * Compare the FS and the undo file superblock so that we can't apply * e2undo "patches" out of order. */ retval = ext2fs_get_mem(ctx->blocksize, &buf); if (retval) { com_err(prg_name, retval, "%s", _("while allocating memory")); return retval; } retval = io_channel_read_blk64(ctx->undo_file, ctx->super_block, -SUPERBLOCK_SIZE, buf); if (retval) { com_err(prg_name, retval, "%s", _("while fetching superblock")); goto out; } sb = (struct ext2_super_block *)buf; sb->s_magic = ~sb->s_magic; if (memcmp(&super, buf, sizeof(super))) { print_undo_mismatch(&super, (struct ext2_super_block *)buf); retval = -1; goto out; } sb_crc = ext2fs_crc32c_le(~0, (unsigned char *)buf, SUPERBLOCK_SIZE); if (ext2fs_le32_to_cpu(ctx->hdr.sb_crc) != sb_crc) { fprintf(stderr, _("Undo file superblock checksum doesn't match.\n")); retval = -1; goto out; } out: ext2fs_free_mem(&buf); return retval; }
static errcode_t test_read_blk64(io_channel channel, unsigned long long block, int count, void *buf) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->real) retval = io_channel_read_blk64(data->real, block, count, buf); if (data->read_blk64) data->read_blk64(block, count, retval); if (data->flags & TEST_FLAG_READ) fprintf(data->outfile, "Test_io: read_blk64(%llu, %d) returned %s\n", block, count, retval ? error_message(retval) : "OK"); if (data->block && data->block == block) { if (data->flags & TEST_FLAG_DUMP) test_dump_block(channel, data, block, buf); if (--data->read_abort_count == 0) test_abort(channel, block); } return retval; }
void do_dump_unused(int argc EXT2FS_ATTR((unused)), char **argv) { blk64_t blk; unsigned char buf[EXT2_MAX_BLOCK_SIZE]; unsigned int i; errcode_t retval; if (common_args_process(argc, argv, 1, 1, "dump_unused", "", 0)) return; for (blk=current_fs->super->s_first_data_block; blk < ext2fs_blocks_count(current_fs->super); blk++) { if (ext2fs_test_block_bitmap2(current_fs->block_map,blk)) continue; retval = io_channel_read_blk64(current_fs->io, blk, 1, buf); if (retval) { com_err(argv[0], retval, "While reading block\n"); return; } for (i=0; i < current_fs->blocksize; i++) if (buf[i]) break; if (i >= current_fs->blocksize) continue; printf("\nUnused block %llu contains non-zero data:\n\n", blk); for (i=0; i < current_fs->blocksize; i++) fputc(buf[i], stdout); } }
/* * This function adds a journal device to a filesystem */ errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev) { struct stat st; errcode_t retval; char buf[SUPERBLOCK_SIZE]; journal_superblock_t *jsb; int start; __u32 i, nr_users; /* Make sure the device exists and is a block device */ if (stat(journal_dev->device_name, &st) < 0) return errno; if (!S_ISBLK(st.st_mode)) return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */ /* Get the journal superblock */ start = ext2fs_journal_sb_start(journal_dev->blocksize); if ((retval = io_channel_read_blk64(journal_dev->io, start, -SUPERBLOCK_SIZE, buf))) return retval; jsb = (journal_superblock_t *) buf; if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) || (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) return EXT2_ET_NO_JOURNAL_SB; if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize) return EXT2_ET_UNEXPECTED_BLOCK_SIZE; /* Check and see if this filesystem has already been added */ nr_users = ntohl(jsb->s_nr_users); if (nr_users > JFS_USERS_MAX) return EXT2_ET_CORRUPT_JOURNAL_SB; for (i=0; i < nr_users; i++) { if (memcmp(fs->super->s_uuid, &jsb->s_users[i*16], 16) == 0) break; } if (i >= nr_users) { memcpy(&jsb->s_users[nr_users*16], fs->super->s_uuid, 16); jsb->s_nr_users = htonl(nr_users+1); } /* Writeback the journal superblock */ if ((retval = io_channel_write_blk64(journal_dev->io, start, -SUPERBLOCK_SIZE, buf))) return retval; fs->super->s_journal_inum = 0; fs->super->s_journal_dev = st.st_rdev; memcpy(fs->super->s_journal_uuid, jsb->s_uuid, sizeof(fs->super->s_journal_uuid)); memset(fs->super->s_jnl_blocks, 0, sizeof(fs->super->s_jnl_blocks)); ext2fs_set_feature_journal(fs->super); ext2fs_mark_super_dirty(fs); return 0; }
static void htree_dump_int_block(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, struct ext2_dx_root_info * rootnode, blk64_t blk, char *buf, int level) { char *cbuf; errcode_t errcode; blk64_t pblk; cbuf = malloc(fs->blocksize); if (!cbuf) { fprintf(pager, "Couldn't allocate child block.\n"); return; } errcode = ext2fs_bmap2(fs, ino, inode, buf, 0, blk, 0, &pblk); if (errcode) { com_err("htree_dump_int_block", errcode, "while mapping logical block %llu\n", blk); goto errout; } errcode = io_channel_read_blk64(current_fs->io, pblk, 1, buf); if (errcode) { com_err("htree_dump_int_block", errcode, "while reading block %llu\n", blk); goto errout; } htree_dump_int_node(fs, ino, inode, rootnode, (struct ext2_dx_entry *) (buf+8), cbuf, level); errout: free(cbuf); }
/* * 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; } check_inode_block_sanity(scan, num_blocks); 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; }
static int process_block(ext2_filsys fs, blk64_t *block_nr, e2_blkcnt_t blockcnt, blk64_t ref_block, int ref_offset, void *priv_data) { struct process_block_struct *pb; errcode_t retval; int ret; blk64_t block, orig; pb = (struct process_block_struct *) priv_data; block = orig = *block_nr; ret = 0; /* * Let's see if this is one which we need to relocate */ if (ext2fs_test_block_bitmap2(pb->reserve, block)) { do { if (++block >= ext2fs_blocks_count(fs->super)) block = fs->super->s_first_data_block; if (block == orig) { pb->error = EXT2_ET_BLOCK_ALLOC_FAIL; return BLOCK_ABORT; } } while (ext2fs_test_block_bitmap2(pb->reserve, block) || ext2fs_test_block_bitmap2(pb->alloc_map, block)); retval = io_channel_read_blk64(fs->io, orig, 1, pb->buf); if (retval) { pb->error = retval; return BLOCK_ABORT; } retval = io_channel_write_blk64(fs->io, block, 1, pb->buf); if (retval) { pb->error = retval; return BLOCK_ABORT; } *block_nr = block; ext2fs_mark_block_bitmap2(pb->alloc_map, block); ret = BLOCK_CHANGED; if (pb->flags & EXT2_BMOVE_DEBUG) printf("ino=%u, blockcnt=%lld, %llu->%llu\n", (unsigned) pb->ino, blockcnt, (unsigned long long) orig, (unsigned long long) block); } if (pb->add_dir) { retval = ext2fs_add_dir_block2(fs->dblist, pb->ino, block, blockcnt); if (retval) { pb->error = retval; ret |= BLOCK_ABORT; } } return ret; }
static int check_filesystem(TDB_CONTEXT *tdb, io_channel channel) { __u32 s_mtime; __u8 s_uuid[16]; errcode_t retval; TDB_DATA tdb_key, tdb_data; struct ext2_super_block super; io_channel_set_blksize(channel, SUPERBLOCK_OFFSET); retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super); if (retval) { com_err(prg_name, retval, _("Failed to read the file system data \n")); return retval; } tdb_key.dptr = mtime_key; tdb_key.dsize = sizeof(mtime_key); tdb_data = tdb_fetch(tdb, tdb_key); if (!tdb_data.dptr) { retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb); com_err(prg_name, retval, _("Failed tdb_fetch %s\n"), tdb_errorstr(tdb)); return retval; } s_mtime = *(__u32 *)tdb_data.dptr; if (super.s_mtime != s_mtime) { com_err(prg_name, 0, _("The file system Mount time didn't match %u\n"), s_mtime); return -1; } tdb_key.dptr = uuid_key; tdb_key.dsize = sizeof(uuid_key); tdb_data = tdb_fetch(tdb, tdb_key); if (!tdb_data.dptr) { retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb); com_err(prg_name, retval, _("Failed tdb_fetch %s\n"), tdb_errorstr(tdb)); return retval; } memcpy(s_uuid, tdb_data.dptr, sizeof(s_uuid)); if (memcmp(s_uuid, super.s_uuid, sizeof(s_uuid))) { com_err(prg_name, 0, _("The file system UUID didn't match \n")); return -1; } return 0; }
static void print_journal_information(ext2_filsys fs) { errcode_t retval; char buf[1024]; char str[80]; unsigned int i; journal_superblock_t *jsb; /* Get the journal superblock */ if ((retval = io_channel_read_blk64(fs->io, fs->super->s_first_data_block + 1, -1024, buf))) { com_err(program_name, retval, "%s", _("while reading journal superblock")); exit(1); } jsb = (journal_superblock_t *) buf; if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) || (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) { com_err(program_name, 0, "%s", _("Couldn't find journal superblock magic numbers")); exit(1); } if (jsb->s_feature_compat & ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_CHECKSUM)) printf(_("Journal checksum type: crc32\n")); if (jsb->s_feature_incompat & ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V2)) printf(_("Journal checksum type: %s\n" "Journal checksum: 0x%08x\n"), journal_checksum_type_str(jsb->s_checksum_type), ext2fs_be32_to_cpu(jsb->s_checksum)); printf(_("\nJournal block size: %u\n" "Journal length: %u\n" "Journal first block: %u\n" "Journal sequence: 0x%08x\n" "Journal start: %u\n" "Journal number of users: %u\n"), (unsigned int)ntohl(jsb->s_blocksize), (unsigned int)ntohl(jsb->s_maxlen), (unsigned int)ntohl(jsb->s_first), (unsigned int)ntohl(jsb->s_sequence), (unsigned int)ntohl(jsb->s_start), (unsigned int)ntohl(jsb->s_nr_users)); for (i=0; i < ntohl(jsb->s_nr_users); i++) { uuid_unparse(&jsb->s_users[i*16], str); printf(i ? " %s\n" : _("Journal users: %s\n"), str); } }
static errcode_t undo_setup_tdb(struct undo_private_data *data) { int i; errcode_t retval; if (data->tdb_written == 1) return 0; data->tdb_written = 1; /* Make a bitmap to track what we've written */ memset(&data->fake_fs, 0, sizeof(data->fake_fs)); data->fake_fs.blocksize = data->tdb_data_size; retval = ext2fs_alloc_generic_bmap(&data->fake_fs, EXT2_ET_MAGIC_BLOCK_BITMAP64, EXT2FS_BMAP64_RBTREE, 0, ~1ULL, ~1ULL, "undo block map", &data->written_block_map); if (retval) return retval; /* Allocate key block */ retval = ext2fs_get_mem(data->tdb_data_size, &data->keyb); if (retval) return retval; data->key_blk_num = data->first_key_blk; /* Record block size */ dbg_printf("Undo block size %llu\n", data->tdb_data_size); dbg_printf("Keys per block %llu\n", KEYS_PER_BLOCK(data)); data->hdr.block_size = ext2fs_cpu_to_le32(data->tdb_data_size); io_channel_set_blksize(data->undo_file, data->tdb_data_size); /* Ensure that we have space for header blocks */ for (i = 0; i <= 2; i++) { retval = io_channel_read_blk64(data->undo_file, i, 1, data->keyb); if (retval) memset(data->keyb, 0, data->tdb_data_size); retval = io_channel_write_blk64(data->undo_file, i, 1, data->keyb); if (retval) return retval; retval = io_channel_flush(data->undo_file); if (retval) return retval; } memset(data->keyb, 0, data->tdb_data_size); return 0; }
static errcode_t follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir, ext2_ino_t inode, int link_count, char *buf, ext2_ino_t *res_inode) { char *pathname; char *buffer = 0; errcode_t retval; struct ext2_inode ei; blk64_t blk; #ifdef NAMEI_DEBUG printf("follow_link: root=%lu, dir=%lu, inode=%lu, lc=%d\n", root, dir, inode, link_count); #endif retval = ext2fs_read_inode (fs, inode, &ei); if (retval) return retval; if (!LINUX_S_ISLNK (ei.i_mode)) { *res_inode = inode; return 0; } if (link_count++ >= EXT2FS_MAX_NESTED_LINKS) return EXT2_ET_SYMLINK_LOOP; if (ext2fs_inode_data_blocks(fs,&ei)) { retval = ext2fs_bmap2(fs, inode, &ei, NULL, 0, 0, NULL, &blk); if (retval) return retval; retval = ext2fs_get_mem(fs->blocksize, &buffer); if (retval) return retval; retval = io_channel_read_blk64(fs->io, blk, 1, buffer); if (retval) { ext2fs_free_mem(&buffer); return retval; } pathname = buffer; } else pathname = (char *)&(ei.i_block[0]); retval = open_namei(fs, root, dir, pathname, ei.i_size, 1, link_count, buf, res_inode); if (buffer) ext2fs_free_mem(&buffer); return retval; }
static int search_dir_block(ext2_filsys fs, blk64_t *blocknr, e2_blkcnt_t blockcnt, blk64_t ref_blk EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct process_block_struct *p; struct ext2_dir_entry *dirent; errcode_t errcode; unsigned int offset = 0; unsigned int rec_len; if (blockcnt < 0) return 0; p = (struct process_block_struct *) priv_data; errcode = io_channel_read_blk64(current_fs->io, *blocknr, 1, p->buf); if (errcode) { com_err("search_dir_block", errcode, "while reading block %lu", (unsigned long) *blocknr); return BLOCK_ABORT; } while (offset < fs->blocksize) { dirent = (struct ext2_dir_entry *) (p->buf + offset); errcode = ext2fs_get_rec_len(fs, dirent, &rec_len); if (errcode) { com_err("htree_dump_leaf_inode", errcode, "while getting rec_len for block %lu", (unsigned long) *blocknr); return BLOCK_ABORT; } if (dirent->inode && p->len == (dirent->name_len & 0xFF) && strncmp(p->search_name, dirent->name, p->len) == 0) { printf("Entry found at logical block %lld, " "phys %llu, offset %u\n", (long long)blockcnt, *blocknr, offset); printf("offset %u\n", offset); return BLOCK_ABORT; } offset += rec_len; } return 0; }
static errcode_t e2fsck_handle_read_error(io_channel channel, unsigned long block, int count, void *data, size_t size EXT2FS_ATTR((unused)), int actual EXT2FS_ATTR((unused)), errcode_t error) { int i; char *p; ext2_filsys fs = (ext2_filsys) channel->app_data; e2fsck_t ctx; ctx = (e2fsck_t) fs->priv_data; if (ctx->flags & E2F_FLAG_EXITING) return 0; /* * If more than one block was read, try reading each block * separately. We could use the actual bytes read to figure * out where to start, but we don't bother. */ if (count > 1) { p = (char *) data; for (i=0; i < count; i++, p += channel->block_size, block++) { error = io_channel_read_blk64(channel, block, 1, p); if (error) return error; } return 0; } if (operation) printf(_("Error reading block %lu (%s) while %s. "), block, error_message(error), operation); else printf(_("Error reading block %lu (%s). "), block, error_message(error)); preenhalt(ctx); if (ask(ctx, _("Ignore error"), 1)) { if (ask(ctx, _("Force rewrite"), 1)) io_channel_write_blk64(channel, block, count, data); return 0; } return error; }
static errcode_t write_file_system_identity(io_channel undo_channel, TDB_CONTEXT *tdb) { errcode_t retval; struct ext2_super_block super; TDB_DATA tdb_key, tdb_data; struct undo_private_data *data; io_channel channel; int block_size ; data = (struct undo_private_data *) undo_channel->private_data; channel = data->real; block_size = channel->block_size; io_channel_set_blksize(channel, SUPERBLOCK_OFFSET); retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super); if (retval) goto err_out; /* Write to tdb file in the file system byte order */ tdb_key.dptr = mtime_key; tdb_key.dsize = sizeof(mtime_key); tdb_data.dptr = (unsigned char *) &(super.s_mtime); tdb_data.dsize = sizeof(super.s_mtime); retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT); if (retval == -1) { retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb); goto err_out; } tdb_key.dptr = uuid_key; tdb_key.dsize = sizeof(uuid_key); tdb_data.dptr = (unsigned char *)&(super.s_uuid); tdb_data.dsize = sizeof(super.s_uuid); retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT); if (retval == -1) { retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb); } err_out: io_channel_set_blksize(channel, block_size); return retval; }
void ll_rw_block(int rw, int nr, struct buffer_head *bhp[]) { int retval; struct buffer_head *bh; for (; nr > 0; --nr) { bh = *bhp++; if (rw == READ && !bh->b_uptodate) { jfs_debug(3, "reading block %llu/%p\n", bh->b_blocknr, (void *) bh); retval = io_channel_read_blk64(bh->b_io, bh->b_blocknr, 1, bh->b_data); if (retval) { com_err(bh->b_ctx->device_name, retval, "while reading block %llu\n", bh->b_blocknr); bh->b_err = retval; continue; } bh->b_uptodate = 1; } else if (rw == WRITE && bh->b_dirty) { jfs_debug(3, "writing block %llu/%p\n", bh->b_blocknr, (void *) bh); retval = io_channel_write_blk64(bh->b_io, bh->b_blocknr, 1, bh->b_data); if (retval) { com_err(bh->b_ctx->device_name, retval, "while writing block %llu\n", bh->b_blocknr); bh->b_err = retval; continue; } bh->b_dirty = 0; bh->b_uptodate = 1; } else { jfs_debug(3, "no-op %s for block %llu\n", rw == READ ? "read" : "write", bh->b_blocknr); } } }
errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block, void *buf, int flags EXT2FS_ATTR((unused))) { errcode_t retval; char *p, *end; struct ext2_dir_entry *dirent; unsigned int name_len, rec_len; retval = io_channel_read_blk64(fs->io, block, 1, buf); if (retval) return retval; p = (char *) buf; end = (char *) buf + fs->blocksize; while (p < end-8) { dirent = (struct ext2_dir_entry *) p; #ifdef WORDS_BIGENDIAN dirent->inode = ext2fs_swab32(dirent->inode); dirent->rec_len = ext2fs_swab16(dirent->rec_len); dirent->name_len = ext2fs_swab16(dirent->name_len); #endif name_len = dirent->name_len; #ifdef WORDS_BIGENDIAN if (flags & EXT2_DIRBLOCK_V2_STRUCT) dirent->name_len = ext2fs_swab16(dirent->name_len); #endif if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0) return retval; if ((rec_len < 8) || (rec_len % 4)) { rec_len = 8; retval = EXT2_ET_DIR_CORRUPTED; } else if (((name_len & 0xFF) + 8) > rec_len) retval = EXT2_ET_DIR_CORRUPTED; p += rec_len; } return retval; }
int main(int argc, char *argv[]) { int c, force = 0, dry_run = 0, verbose = 0, dump = 0; io_channel channel; errcode_t retval; int mount_flags, csum_error = 0, io_error = 0; size_t i, keys_per_block; char *device_name, *tdb_file; io_manager manager = unix_io_manager; struct undo_context undo_ctx; char *buf; struct undo_key_block *keyb; struct undo_key *dkey; struct undo_key_info *ikey; __u32 key_crc, blk_crc, hdr_crc; blk64_t lblk; ext2_filsys fs; __u64 offset = 0; char opt_offset_string[40] = { 0 }; #ifdef ENABLE_NLS setlocale(LC_MESSAGES, ""); setlocale(LC_CTYPE, ""); bindtextdomain(NLS_CAT_NAME, LOCALEDIR); textdomain(NLS_CAT_NAME); set_com_err_gettext(gettext); #endif add_error_table(&et_ext2_error_table); prg_name = argv[0]; while ((c = getopt(argc, argv, "fhno:vz:")) != EOF) { switch (c) { case 'f': force = 1; break; case 'h': dump = 1; break; case 'n': dry_run = 1; break; case 'o': offset = strtoull(optarg, &buf, 0); if (*buf) { com_err(prg_name, 0, _("illegal offset - %s"), optarg); exit(1); } /* used to indicate that an offset was specified */ opt_offset_string[0] = 1; break; case 'v': verbose = 1; break; case 'z': undo_file = optarg; break; default: usage(); } } if (argc != optind + 2) usage(); tdb_file = argv[optind]; device_name = argv[optind+1]; if (undo_file && strcmp(tdb_file, undo_file) == 0) { printf(_("Will not write to an undo file while replaying it.\n")); exit(1); } /* Interpret the undo file */ retval = manager->open(tdb_file, IO_FLAG_EXCLUSIVE, &undo_ctx.undo_file); if (retval) { com_err(prg_name, errno, _("while opening undo file `%s'\n"), tdb_file); exit(1); } retval = io_channel_read_blk64(undo_ctx.undo_file, 0, -(int)sizeof(undo_ctx.hdr), &undo_ctx.hdr); if (retval) { com_err(prg_name, retval, _("while reading undo file")); exit(1); } if (memcmp(undo_ctx.hdr.magic, E2UNDO_MAGIC, sizeof(undo_ctx.hdr.magic))) { fprintf(stderr, _("%s: Not an undo file.\n"), tdb_file); exit(1); } if (dump) { dump_header(&undo_ctx.hdr); exit(1); } hdr_crc = ext2fs_crc32c_le(~0, (unsigned char *)&undo_ctx.hdr, sizeof(struct undo_header) - sizeof(__u32)); if (!force && ext2fs_le32_to_cpu(undo_ctx.hdr.header_crc) != hdr_crc) { fprintf(stderr, _("%s: Header checksum doesn't match.\n"), tdb_file); exit(1); } undo_ctx.blocksize = ext2fs_le32_to_cpu(undo_ctx.hdr.block_size); undo_ctx.fs_blocksize = ext2fs_le32_to_cpu(undo_ctx.hdr.fs_block_size); if (undo_ctx.blocksize == 0 || undo_ctx.fs_blocksize == 0) { fprintf(stderr, _("%s: Corrupt undo file header.\n"), tdb_file); exit(1); } if (!force && undo_ctx.blocksize > E2UNDO_MAX_BLOCK_SIZE) { fprintf(stderr, _("%s: Undo block size too large.\n"), tdb_file); exit(1); } if (!force && undo_ctx.blocksize < E2UNDO_MIN_BLOCK_SIZE) { fprintf(stderr, _("%s: Undo block size too small.\n"), tdb_file); exit(1); } undo_ctx.super_block = ext2fs_le64_to_cpu(undo_ctx.hdr.super_offset); undo_ctx.num_keys = ext2fs_le64_to_cpu(undo_ctx.hdr.num_keys); io_channel_set_blksize(undo_ctx.undo_file, undo_ctx.blocksize); /* * Do not compare undo_ctx.hdr.f_compat with the available compatible * features set, because a "missing" compatible feature should * not cause any problems. */ if (!force && (undo_ctx.hdr.f_incompat || undo_ctx.hdr.f_rocompat)) { fprintf(stderr, _("%s: Unknown undo file feature set.\n"), tdb_file); exit(1); } /* open the fs */ retval = ext2fs_check_if_mounted(device_name, &mount_flags); if (retval) { com_err(prg_name, retval, _("Error while determining whether " "%s is mounted."), device_name); exit(1); } if (mount_flags & EXT2_MF_MOUNTED) { com_err(prg_name, retval, "%s", _("e2undo should only be run " "on unmounted filesystems")); exit(1); } if (undo_file) { retval = e2undo_setup_tdb(device_name, &manager); if (retval) exit(1); } retval = manager->open(device_name, IO_FLAG_EXCLUSIVE | (dry_run ? 0 : IO_FLAG_RW), &channel); if (retval) { com_err(prg_name, retval, _("while opening `%s'"), device_name); exit(1); } if (*opt_offset_string || e2undo_has_feature_fs_offset(&undo_ctx.hdr)) { if (!*opt_offset_string) offset = ext2fs_le64_to_cpu(undo_ctx.hdr.fs_offset); retval = snprintf(opt_offset_string, sizeof(opt_offset_string), "offset=%llu", offset); if ((size_t) retval >= sizeof(opt_offset_string)) { /* should not happen... */ com_err(prg_name, 0, _("specified offset is too large")); exit(1); } io_channel_set_options(channel, opt_offset_string); } if (!force && check_filesystem(&undo_ctx, channel)) exit(1); /* prepare to read keys */ retval = ext2fs_get_mem(sizeof(struct undo_key_info) * undo_ctx.num_keys, &undo_ctx.keys); if (retval) { com_err(prg_name, retval, "%s", _("while allocating memory")); exit(1); } ikey = undo_ctx.keys; retval = ext2fs_get_mem(undo_ctx.blocksize, &keyb); if (retval) { com_err(prg_name, retval, "%s", _("while allocating memory")); exit(1); } retval = ext2fs_get_mem(E2UNDO_MAX_EXTENT_BLOCKS * undo_ctx.blocksize, &buf); if (retval) { com_err(prg_name, retval, "%s", _("while allocating memory")); exit(1); } /* load keys */ keys_per_block = KEYS_PER_BLOCK(&undo_ctx); lblk = ext2fs_le64_to_cpu(undo_ctx.hdr.key_offset); dbg_printf("nr_keys=%lu, kpb=%zu, blksz=%u\n", undo_ctx.num_keys, keys_per_block, undo_ctx.blocksize); for (i = 0; i < undo_ctx.num_keys; i += keys_per_block) { size_t j, max_j; __le32 crc; retval = io_channel_read_blk64(undo_ctx.undo_file, lblk, 1, keyb); if (retval) { com_err(prg_name, retval, "%s", _("while reading keys")); if (force) { io_error = 1; undo_ctx.num_keys = i - 1; break; } exit(1); } /* check keys */ if (!force && ext2fs_le32_to_cpu(keyb->magic) != KEYBLOCK_MAGIC) { fprintf(stderr, _("%s: wrong key magic at %llu\n"), tdb_file, lblk); exit(1); } crc = keyb->crc; keyb->crc = 0; key_crc = ext2fs_crc32c_le(~0, (unsigned char *)keyb, undo_ctx.blocksize); if (!force && ext2fs_le32_to_cpu(crc) != key_crc) { fprintf(stderr, _("%s: key block checksum error at %llu.\n"), tdb_file, lblk); exit(1); } /* load keys from key block */ lblk++; max_j = undo_ctx.num_keys - i; if (max_j > keys_per_block) max_j = keys_per_block; for (j = 0, dkey = keyb->keys; j < max_j; j++, ikey++, dkey++) { ikey->fsblk = ext2fs_le64_to_cpu(dkey->fsblk); ikey->fileblk = lblk; ikey->blk_crc = ext2fs_le32_to_cpu(dkey->blk_crc); ikey->size = ext2fs_le32_to_cpu(dkey->size); lblk += (ikey->size + undo_ctx.blocksize - 1) / undo_ctx.blocksize; if (E2UNDO_MAX_EXTENT_BLOCKS * undo_ctx.blocksize < ikey->size) { com_err(prg_name, retval, _("%s: block %llu is too long."), tdb_file, ikey->fsblk); exit(1); } /* check each block's crc */ retval = io_channel_read_blk64(undo_ctx.undo_file, ikey->fileblk, -(int)ikey->size, buf); if (retval) { com_err(prg_name, retval, _("while fetching block %llu."), ikey->fileblk); if (!force) exit(1); io_error = 1; continue; } blk_crc = ext2fs_crc32c_le(~0, (unsigned char *)buf, ikey->size); if (blk_crc != ikey->blk_crc) { fprintf(stderr, _("checksum error in filesystem block " "%llu (undo blk %llu)\n"), ikey->fsblk, ikey->fileblk); if (!force) exit(1); csum_error = 1; } } } ext2fs_free_mem(&keyb); /* sort keys in fs block order */ qsort(undo_ctx.keys, undo_ctx.num_keys, sizeof(struct undo_key_info), key_compare); /* replay */ io_channel_set_blksize(channel, undo_ctx.fs_blocksize); for (i = 0, ikey = undo_ctx.keys; i < undo_ctx.num_keys; i++, ikey++) { retval = io_channel_read_blk64(undo_ctx.undo_file, ikey->fileblk, -(int)ikey->size, buf); if (retval) { com_err(prg_name, retval, _("while fetching block %llu."), ikey->fileblk); io_error = 1; continue; } if (verbose) printf("Replayed block of size %u from %llu to %llu\n", ikey->size, ikey->fileblk, ikey->fsblk); if (dry_run) continue; retval = io_channel_write_blk64(channel, ikey->fsblk, -(int)ikey->size, buf); if (retval) { com_err(prg_name, retval, _("while writing block %llu."), ikey->fsblk); io_error = 1; } } if (csum_error) fprintf(stderr, _("Undo file corruption; run e2fsck NOW!\n")); if (io_error) fprintf(stderr, _("IO error during replay; run e2fsck NOW!\n")); if (!(ext2fs_le32_to_cpu(undo_ctx.hdr.state) & E2UNDO_STATE_FINISHED)) { force = 1; fprintf(stderr, _("Incomplete undo record; run e2fsck.\n")); } ext2fs_free_mem(&buf); ext2fs_free_mem(&undo_ctx.keys); io_channel_close(channel); /* If there were problems, try to force a fsck */ if (!dry_run && (force || csum_error || io_error)) { retval = ext2fs_open2(device_name, NULL, EXT2_FLAG_RW | EXT2_FLAG_64BITS, 0, 0, manager, &fs); if (retval) goto out; fs->super->s_state &= ~EXT2_VALID_FS; if (csum_error || io_error) fs->super->s_state |= EXT2_ERROR_FS; ext2fs_mark_super_dirty(fs); ext2fs_close_free(&fs); } out: io_channel_close(undo_ctx.undo_file); return csum_error; }
static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) { dgrp_t i; char *block_bitmap = 0, *inode_bitmap = 0; char *buf; errcode_t retval; int block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8; int inode_nbytes = EXT2_INODES_PER_GROUP(fs->super) / 8; int csum_flag = 0; unsigned int cnt; blk64_t blk; blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block); blk64_t blk_cnt; ext2_ino_t ino_itr = 1; ext2_ino_t ino_cnt; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if ((block_nbytes > fs->blocksize) || (inode_nbytes > fs->blocksize)) return EXT2_ET_CORRUPT_SUPERBLOCK; fs->write_bitmaps = ext2fs_write_bitmaps; if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) csum_flag = 1; retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); if (retval) return retval; if (do_block) { if (fs->block_map) ext2fs_free_block_bitmap(fs->block_map); strcpy(buf, "block bitmap for "); strcat(buf, fs->device_name); retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); if (retval) goto cleanup; retval = io_channel_alloc_buf(fs->io, 0, &block_bitmap); if (retval) goto cleanup; } else block_nbytes = 0; if (do_inode) { if (fs->inode_map) ext2fs_free_inode_bitmap(fs->inode_map); strcpy(buf, "inode bitmap for "); strcat(buf, fs->device_name); retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); if (retval) goto cleanup; retval = io_channel_alloc_buf(fs->io, 0, &inode_bitmap); if (retval) goto cleanup; } else inode_nbytes = 0; ext2fs_free_mem(&buf); if (fs->flags & EXT2_FLAG_IMAGE_FILE) { blk = (fs->image_header->offset_inodemap / fs->blocksize); ino_cnt = fs->super->s_inodes_count; while (inode_nbytes > 0) { retval = io_channel_read_blk64(fs->image_io, blk++, 1, inode_bitmap); if (retval) goto cleanup; cnt = fs->blocksize << 3; if (cnt > ino_cnt) cnt = ino_cnt; retval = ext2fs_set_inode_bitmap_range2(fs->inode_map, ino_itr, cnt, inode_bitmap); if (retval) goto cleanup; ino_itr += fs->blocksize << 3; ino_cnt -= fs->blocksize << 3; inode_nbytes -= fs->blocksize; } blk = (fs->image_header->offset_blockmap / fs->blocksize); blk_cnt = (blk64_t)EXT2_CLUSTERS_PER_GROUP(fs->super) * fs->group_desc_count; while (block_nbytes > 0) { retval = io_channel_read_blk64(fs->image_io, blk++, 1, block_bitmap); if (retval) goto cleanup; cnt = fs->blocksize << 3; if (cnt > blk_cnt) cnt = blk_cnt; retval = ext2fs_set_block_bitmap_range2(fs->block_map, blk_itr, cnt, block_bitmap); if (retval) goto cleanup; blk_itr += fs->blocksize << 3; blk_cnt -= fs->blocksize << 3; block_nbytes -= fs->blocksize; } goto success_cleanup; } for (i = 0; i < fs->group_desc_count; i++) { if (block_bitmap) { blk = ext2fs_block_bitmap_loc(fs, i); if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) && ext2fs_group_desc_csum_verify(fs, i)) blk = 0; if (blk) { retval = io_channel_read_blk64(fs->io, blk, 1, block_bitmap); if (retval) { retval = EXT2_ET_BLOCK_BITMAP_READ; goto cleanup; } } else memset(block_bitmap, 0, block_nbytes); cnt = block_nbytes << 3; retval = ext2fs_set_block_bitmap_range2(fs->block_map, blk_itr, cnt, block_bitmap); if (retval) goto cleanup; blk_itr += block_nbytes << 3; } if (inode_bitmap) { blk = ext2fs_inode_bitmap_loc(fs, i); if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) && ext2fs_group_desc_csum_verify(fs, i)) blk = 0; if (blk) { retval = io_channel_read_blk64(fs->io, blk, 1, inode_bitmap); if (retval) { retval = EXT2_ET_INODE_BITMAP_READ; goto cleanup; } } else memset(inode_bitmap, 0, inode_nbytes); cnt = inode_nbytes << 3; retval = ext2fs_set_inode_bitmap_range2(fs->inode_map, ino_itr, cnt, inode_bitmap); if (retval) goto cleanup; ino_itr += inode_nbytes << 3; } } success_cleanup: if (inode_bitmap) ext2fs_free_mem(&inode_bitmap); if (block_bitmap) ext2fs_free_mem(&block_bitmap); return 0; cleanup: if (do_block) { ext2fs_free_mem(&fs->block_map); fs->block_map = 0; } if (do_inode) { ext2fs_free_mem(&fs->inode_map); fs->inode_map = 0; } if (inode_bitmap) ext2fs_free_mem(&inode_bitmap); if (block_bitmap) ext2fs_free_mem(&block_bitmap); if (buf) ext2fs_free_mem(&buf); return retval; }
static errcode_t write_undo_indexes(struct undo_private_data *data, int flush) { errcode_t retval; struct ext2_super_block super; io_channel channel; int block_size; __u32 sb_crc, hdr_crc; /* Spit out a key block, if there's any data */ if (data->keys_in_block) { data->keyb->magic = ext2fs_cpu_to_le32(KEYBLOCK_MAGIC); data->keyb->crc = 0; data->keyb->crc = ext2fs_cpu_to_le32( ext2fs_crc32c_le(~0, (unsigned char *)data->keyb, data->tdb_data_size)); dbg_printf("Writing keyblock to blk %llu\n", data->key_blk_num); retval = io_channel_write_blk64(data->undo_file, data->key_blk_num, 1, data->keyb); if (retval) return retval; /* Move on to the next key block if it's full. */ if (data->keys_in_block == KEYS_PER_BLOCK(data)) { memset(data->keyb, 0, data->tdb_data_size); data->keys_in_block = 0; data->key_blk_num = data->undo_blk_num; data->undo_blk_num++; } } /* Prepare superblock for write */ channel = data->real; block_size = channel->block_size; io_channel_set_blksize(channel, SUPERBLOCK_OFFSET); retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super); if (retval) goto err_out; sb_crc = ext2fs_crc32c_le(~0, (unsigned char *)&super, SUPERBLOCK_SIZE); super.s_magic = ~super.s_magic; /* Write the undo header to disk. */ memcpy(data->hdr.magic, E2UNDO_MAGIC, sizeof(data->hdr.magic)); data->hdr.num_keys = ext2fs_cpu_to_le64(data->num_keys); data->hdr.super_offset = ext2fs_cpu_to_le64(data->super_blk_num); data->hdr.key_offset = ext2fs_cpu_to_le64(data->first_key_blk); data->hdr.fs_block_size = ext2fs_cpu_to_le32(block_size); data->hdr.sb_crc = ext2fs_cpu_to_le32(sb_crc); hdr_crc = ext2fs_crc32c_le(~0, (unsigned char *)&data->hdr, sizeof(data->hdr) - sizeof(data->hdr.header_crc)); data->hdr.header_crc = ext2fs_cpu_to_le32(hdr_crc); retval = io_channel_write_blk64(data->undo_file, 0, -(int)sizeof(data->hdr), &data->hdr); if (retval) goto err_out; /* * Record the entire superblock (in FS byte order) so that we can't * apply e2undo files to the wrong FS or out of order. */ dbg_printf("Writing superblock to block %llu\n", data->super_blk_num); retval = io_channel_write_blk64(data->undo_file, data->super_blk_num, -SUPERBLOCK_SIZE, &super); if (retval) goto err_out; if (flush) retval = io_channel_flush(data->undo_file); err_out: io_channel_set_blksize(channel, block_size); return retval; }
void do_htree_dump(int argc, char *argv[]) { ext2_ino_t ino; struct ext2_inode inode; blk64_t blk; char *buf = NULL; struct ext2_dx_root_info *rootnode; struct ext2_dx_entry *ent; errcode_t errcode; if (check_fs_open(argv[0])) return; pager = open_pager(); if (common_inode_args_process(argc, argv, &ino, 0)) goto errout; if (debugfs_read_inode(ino, &inode, argv[1])) goto errout; if (!LINUX_S_ISDIR(inode.i_mode)) { com_err(argv[0], 0, "Not a directory"); goto errout; } if ((inode.i_flags & EXT2_BTREE_FL) == 0) { com_err(argv[0], 0, "Not a hash-indexed directory"); goto errout; } buf = malloc(2*current_fs->blocksize); if (!buf) { com_err(argv[0], 0, "Couldn't allocate htree buffer"); goto errout; } errcode = ext2fs_bmap2(current_fs, ino, &inode, buf, 0, 0, 0, &blk); if (errcode) { com_err("do_htree_block", errcode, "while mapping logical block 0\n"); goto errout; } errcode = io_channel_read_blk64(current_fs->io, blk, 1, buf); if (errcode) { com_err(argv[0], errcode, "Error reading root node"); goto errout; } rootnode = (struct ext2_dx_root_info *) (buf + 24); fprintf(pager, "Root node dump:\n"); fprintf(pager, "\t Reserved zero: %u\n", rootnode->reserved_zero); fprintf(pager, "\t Hash Version: %d\n", rootnode->hash_version); fprintf(pager, "\t Info length: %d\n", rootnode->info_length); fprintf(pager, "\t Indirect levels: %d\n", rootnode->indirect_levels); fprintf(pager, "\t Flags: %d\n", rootnode->unused_flags); ent = (struct ext2_dx_entry *) (buf + 24 + rootnode->info_length); htree_dump_int_node(current_fs, ino, &inode, rootnode, ent, buf + current_fs->blocksize, rootnode->indirect_levels); errout: free(buf); close_pager(pager); }
static errcode_t undo_write_tdb(io_channel channel, unsigned long long block, int count) { int size, sz; unsigned long long block_num, backing_blk_num; errcode_t retval = 0; ext2_loff_t offset; struct undo_private_data *data; unsigned char *read_ptr; unsigned long long end_block; unsigned long long data_size; void *data_ptr; struct undo_key *key; __u32 blk_crc; data = (struct undo_private_data *) channel->private_data; if (data->undo_file == NULL) { /* * Transaction database not initialized */ return 0; } if (count == 1) size = channel->block_size; else { if (count < 0) size = -count; else size = count * channel->block_size; } retval = undo_setup_tdb(data); if (retval) return retval; /* * Data is stored in tdb database as blocks of tdb_data_size size * This helps in efficient lookup further. * * We divide the disk to blocks of tdb_data_size. */ offset = (block * channel->block_size) + data->offset ; block_num = offset / data->tdb_data_size; end_block = (offset + size - 1) / data->tdb_data_size; while (block_num <= end_block) { __u32 keysz; /* * Check if we have the record already */ if (ext2fs_test_block_bitmap2(data->written_block_map, block_num)) { /* Try the next block */ block_num++; continue; } ext2fs_mark_block_bitmap2(data->written_block_map, block_num); /* * Read one block using the backing I/O manager * The backing I/O manager block size may be * different from the tdb_data_size. * Also we need to recalcuate the block number with respect * to the backing I/O manager. */ offset = block_num * data->tdb_data_size; backing_blk_num = (offset - data->offset) / channel->block_size; count = data->tdb_data_size + ((offset - data->offset) % channel->block_size); retval = ext2fs_get_mem(count, &read_ptr); if (retval) { return retval; } memset(read_ptr, 0, count); actual_size = 0; if ((count % channel->block_size) == 0) sz = count / channel->block_size; else sz = -count; retval = io_channel_read_blk64(data->real, backing_blk_num, sz, read_ptr); if (retval) { if (retval != EXT2_ET_SHORT_READ) { free(read_ptr); return retval; } /* * short read so update the record size * accordingly */ data_size = actual_size; } else { data_size = data->tdb_data_size; } if (data_size == 0) { free(read_ptr); block_num++; continue; } dbg_printf("Read %llu bytes from FS block %llu (blk=%llu cnt=%u)\n", data_size, backing_blk_num, block, count); if ((data_size % data->undo_file->block_size) == 0) sz = data_size / data->undo_file->block_size; else sz = -actual_size; data_ptr = read_ptr + ((offset - data->offset) % data->undo_file->block_size); /* extend this key? */ if (data->keys_in_block) { key = data->keyb->keys + data->keys_in_block - 1; keysz = ext2fs_le32_to_cpu(key->size); } else { key = NULL; keysz = 0; } if (key != NULL && ext2fs_le64_to_cpu(key->fsblk) + ((keysz + data->tdb_data_size - 1) / data->tdb_data_size) == backing_blk_num && E2UNDO_MAX_EXTENT_BLOCKS * data->tdb_data_size > keysz + sz) { blk_crc = ext2fs_le32_to_cpu(key->blk_crc); blk_crc = ext2fs_crc32c_le(blk_crc, (unsigned char *)data_ptr, data_size); key->blk_crc = ext2fs_cpu_to_le32(blk_crc); key->size = ext2fs_cpu_to_le32(keysz + data_size); } else { data->num_keys++; key = data->keyb->keys + data->keys_in_block; data->keys_in_block++; key->fsblk = ext2fs_cpu_to_le64(backing_blk_num); blk_crc = ext2fs_crc32c_le(~0, (unsigned char *)data_ptr, data_size); key->blk_crc = ext2fs_cpu_to_le32(blk_crc); key->size = ext2fs_cpu_to_le32(data_size); } dbg_printf("Writing block %llu to offset %llu size %d key %zu\n", block_num, data->undo_blk_num, sz, data->num_keys - 1); retval = io_channel_write_blk64(data->undo_file, data->undo_blk_num, sz, data_ptr); if (retval) { free(read_ptr); return retval; } data->undo_blk_num++; free(read_ptr); /* Write out the key block */ retval = write_undo_indexes(data, 0); if (retval) return retval; /* Next block */ block_num++; } return retval; }
/* * 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; }
blk64_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name, io_manager manager) { struct ext2_super_block *sb; io_channel io = NULL; void *buf = NULL; int blocksize; blk64_t superblock, ret_sb = 8193; if (fs && fs->super) { ret_sb = (fs->super->s_blocks_per_group + fs->super->s_first_data_block); if (ctx) { ctx->superblock = ret_sb; ctx->blocksize = fs->blocksize; } return ret_sb; } if (ctx) { if (ctx->blocksize) { ret_sb = ctx->blocksize * 8; if (ctx->blocksize == 1024) ret_sb++; ctx->superblock = ret_sb; return ret_sb; } ctx->superblock = ret_sb; ctx->blocksize = 1024; } if (!name || !manager) goto cleanup; if (manager->open(name, 0, &io) != 0) goto cleanup; if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf)) goto cleanup; sb = (struct ext2_super_block *) buf; for (blocksize = EXT2_MIN_BLOCK_SIZE; blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) { superblock = blocksize*8; if (blocksize == 1024) superblock++; io_channel_set_blksize(io, blocksize); if (io_channel_read_blk64(io, superblock, -SUPERBLOCK_SIZE, buf)) continue; #ifdef WORDS_BIGENDIAN if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ext2fs_swap_super(sb); #endif if ((sb->s_magic == EXT2_SUPER_MAGIC) && (EXT2_BLOCK_SIZE(sb) == blocksize)) { ret_sb = superblock; if (ctx) { ctx->superblock = superblock; ctx->blocksize = blocksize; } break; } } cleanup: if (io) io_channel_close(io); if (buf) ext2fs_free_mem(&buf); return (ret_sb); }
/* * 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; int clen, i, inodes_per_block, length; io_channel io; 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 = create_icache(fs); if (retval) return retval; } /* 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); length = EXT2_INODE_SIZE(fs->super); 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; 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; }
/* * Note: if superblock is non-zero, block-size must also be non-zero. * Superblock and block_size can be zero to use the default size. * * Valid flags for ext2fs_open() * * EXT2_FLAG_RW - Open the filesystem for read/write. * EXT2_FLAG_FORCE - Open the filesystem even if some of the * features aren't supported. * EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device * EXT2_FLAG_SKIP_MMP - Open without multi-mount protection check. * EXT2_FLAG_64BITS - Allow 64-bit bitfields (needed for large * filesystems) */ errcode_t ext2fs_open2(const char *name, const char *io_options, int flags, int superblock, unsigned int block_size, io_manager manager, ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; unsigned long i, first_meta_bg; __u32 features; unsigned int blocks_per_group, io_flags; blk64_t group_block, blk; char *dest, *cp; int group_zero_adjust = 0; #ifdef WORDS_BIGENDIAN unsigned int groups_per_block; struct ext2_group_desc *gdp; int j; #endif char *time_env; EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER); retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); if (retval) return retval; memset(fs, 0, sizeof(struct struct_ext2_filsys)); fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; fs->flags = flags; /* don't overwrite sb backups unless flag is explicitly cleared */ fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; fs->umask = 022; time_env = getenv("E2FSPROGS_FAKE_TIME"); if (time_env) fs->now = strtoul(time_env, NULL, 0); retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); cp = strchr(fs->device_name, '?'); if (!io_options && cp) { *cp++ = 0; io_options = cp; } io_flags = 0; if (flags & EXT2_FLAG_RW) io_flags |= IO_FLAG_RW; if (flags & EXT2_FLAG_EXCLUSIVE) io_flags |= IO_FLAG_EXCLUSIVE; if (flags & EXT2_FLAG_DIRECT_IO) io_flags |= IO_FLAG_DIRECT_IO; retval = manager->open(fs->device_name, io_flags, &fs->io); if (retval) goto cleanup; if (io_options && (retval = io_channel_set_options(fs->io, io_options))) goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; retval = io_channel_alloc_buf(fs->io, -SUPERBLOCK_SIZE, &fs->super); if (retval) goto cleanup; if (flags & EXT2_FLAG_IMAGE_FILE) { retval = ext2fs_get_mem(sizeof(struct ext2_image_hdr), &fs->image_header); if (retval) goto cleanup; retval = io_channel_read_blk(fs->io, 0, -(int)sizeof(struct ext2_image_hdr), fs->image_header); if (retval) goto cleanup; if (fs->image_header->magic_number != EXT2_ET_MAGIC_E2IMAGE) return EXT2_ET_MAGIC_E2IMAGE; superblock = 1; block_size = fs->image_header->fs_blocksize; } /* * If the user specifies a specific block # for the * superblock, then he/she must also specify the block size! * Otherwise, read the master superblock located at offset * SUPERBLOCK_OFFSET from the start of the partition. * * Note: we only save a backup copy of the superblock if we * are reading the superblock from the primary superblock location. */ if (superblock) { if (!block_size) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } io_channel_set_blksize(fs->io, block_size); group_block = superblock; fs->orig_super = 0; } else { io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); superblock = 1; group_block = 0; retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); if (retval) goto cleanup; } retval = io_channel_read_blk(fs->io, superblock, -SUPERBLOCK_SIZE, fs->super); if (retval) goto cleanup; if (fs->orig_super) memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE); if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)) { retval = 0; if (!ext2fs_verify_csum_type(fs, fs->super)) retval = EXT2_ET_UNKNOWN_CSUM; if (!ext2fs_superblock_csum_verify(fs, fs->super)) retval = EXT2_ET_SB_CSUM_INVALID; } #ifdef WORDS_BIGENDIAN fs->flags |= EXT2_FLAG_SWAP_BYTES; ext2fs_swap_super(fs->super); #else if (fs->flags & EXT2_FLAG_SWAP_BYTES) { retval = EXT2_ET_UNIMPLEMENTED; goto cleanup; } #endif if (fs->super->s_magic != EXT2_SUPER_MAGIC) retval = EXT2_ET_BAD_MAGIC; if (retval) goto cleanup; if (fs->super->s_rev_level > EXT2_LIB_CURRENT_REV) { retval = EXT2_ET_REV_TOO_HIGH; goto cleanup; } /* * Check for feature set incompatibility */ if (!(flags & EXT2_FLAG_FORCE)) { features = fs->super->s_feature_incompat; #ifdef EXT2_LIB_SOFTSUPP_INCOMPAT if (flags & EXT2_FLAG_SOFTSUPP_FEATURES) features &= ~EXT2_LIB_SOFTSUPP_INCOMPAT; #endif if (features & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } features = fs->super->s_feature_ro_compat; #ifdef EXT2_LIB_SOFTSUPP_RO_COMPAT if (flags & EXT2_FLAG_SOFTSUPP_FEATURES) features &= ~EXT2_LIB_SOFTSUPP_RO_COMPAT; #endif if ((flags & EXT2_FLAG_RW) && (features & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)) { retval = EXT2_ET_RO_UNSUPP_FEATURE; goto cleanup; } if (!(flags & EXT2_FLAG_JOURNAL_DEV_OK) && ext2fs_has_feature_journal_dev(fs->super)) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } } if ((fs->super->s_log_block_size + EXT2_MIN_BLOCK_LOG_SIZE) > EXT2_MAX_BLOCK_LOG_SIZE) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } /* * bigalloc requires cluster-aware bitfield operations, which at the * moment means we need EXT2_FLAG_64BITS. */ if (ext2fs_has_feature_bigalloc(fs->super) && !(flags & EXT2_FLAG_64BITS)) { retval = EXT2_ET_CANT_USE_LEGACY_BITMAPS; goto cleanup; } if (!ext2fs_has_feature_bigalloc(fs->super) && (fs->super->s_log_block_size != fs->super->s_log_cluster_size)) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->fragsize = fs->blocksize = EXT2_BLOCK_SIZE(fs->super); if (EXT2_INODE_SIZE(fs->super) < EXT2_GOOD_OLD_INODE_SIZE) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } /* Enforce the block group descriptor size */ if (ext2fs_has_feature_64bit(fs->super)) { if (fs->super->s_desc_size < EXT2_MIN_DESC_SIZE_64BIT) { retval = EXT2_ET_BAD_DESC_SIZE; goto cleanup; } } else { if (fs->super->s_desc_size && fs->super->s_desc_size != EXT2_MIN_DESC_SIZE) { retval = EXT2_ET_BAD_DESC_SIZE; goto cleanup; } } fs->cluster_ratio_bits = fs->super->s_log_cluster_size - fs->super->s_log_block_size; if (EXT2_BLOCKS_PER_GROUP(fs->super) != EXT2_CLUSTERS_PER_GROUP(fs->super) << fs->cluster_ratio_bits) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->inode_blocks_per_group = ((EXT2_INODES_PER_GROUP(fs->super) * EXT2_INODE_SIZE(fs->super) + EXT2_BLOCK_SIZE(fs->super) - 1) / EXT2_BLOCK_SIZE(fs->super)); if (block_size) { if (block_size != fs->blocksize) { retval = EXT2_ET_UNEXPECTED_BLOCK_SIZE; goto cleanup; } } /* * Set the blocksize to the filesystem's blocksize. */ io_channel_set_blksize(fs->io, fs->blocksize); /* * If this is an external journal device, don't try to read * the group descriptors, because they're not there. */ if (ext2fs_has_feature_journal_dev(fs->super)) { fs->group_desc_count = 0; *ret_fs = fs; return 0; } if (EXT2_INODES_PER_GROUP(fs->super) == 0) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } /* Precompute the FS UUID to seed other checksums */ ext2fs_init_csum_seed(fs); /* * Read group descriptors */ blocks_per_group = EXT2_BLOCKS_PER_GROUP(fs->super); if (blocks_per_group == 0 || blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(fs->super) || fs->inode_blocks_per_group > EXT2_MAX_INODES_PER_GROUP(fs->super) || EXT2_DESC_PER_BLOCK(fs->super) == 0 || fs->super->s_first_data_block >= ext2fs_blocks_count(fs->super)) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->group_desc_count = ext2fs_div64_ceil(ext2fs_blocks_count(fs->super) - fs->super->s_first_data_block, blocks_per_group); if (fs->group_desc_count * EXT2_INODES_PER_GROUP(fs->super) != fs->super->s_inodes_count) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, EXT2_DESC_PER_BLOCK(fs->super)); retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, &fs->group_desc); if (retval) goto cleanup; if (!group_block) group_block = fs->super->s_first_data_block; /* * On a FS with a 1K blocksize, block 0 is reserved for bootloaders * so we must increment block numbers to any group 0 items. * * However, we cannot touch group_block directly because in the meta_bg * case, the ext2fs_descriptor_block_loc2() function will interpret * group_block != s_first_data_block to mean that we want to access the * backup group descriptors. This is not what we want if the caller * set superblock == 0 (i.e. auto-detect the superblock), which is * what's going on here. */ if (group_block == 0 && fs->blocksize == 1024) group_zero_adjust = 1; dest = (char *) fs->group_desc; #ifdef WORDS_BIGENDIAN groups_per_block = EXT2_DESC_PER_BLOCK(fs->super); #endif if (ext2fs_has_feature_meta_bg(fs->super)) { first_meta_bg = fs->super->s_first_meta_bg; if (first_meta_bg > fs->desc_blocks) first_meta_bg = fs->desc_blocks; } else first_meta_bg = fs->desc_blocks; if (first_meta_bg) { retval = io_channel_read_blk(fs->io, group_block + group_zero_adjust + 1, first_meta_bg, dest); if (retval) goto cleanup; #ifdef WORDS_BIGENDIAN gdp = (struct ext2_group_desc *) dest; for (j=0; j < groups_per_block*first_meta_bg; j++) { gdp = ext2fs_group_desc(fs, fs->group_desc, j); ext2fs_swap_group_desc2(fs, gdp); } #endif dest += fs->blocksize*first_meta_bg; } for (i=first_meta_bg ; i < fs->desc_blocks; i++) { blk = ext2fs_descriptor_block_loc2(fs, group_block, i); retval = io_channel_read_blk64(fs->io, blk, 1, dest); if (retval) goto cleanup; #ifdef WORDS_BIGENDIAN for (j=0; j < groups_per_block; j++) { gdp = ext2fs_group_desc(fs, fs->group_desc, i * groups_per_block + j); ext2fs_swap_group_desc2(fs, gdp); } #endif dest += fs->blocksize; } fs->stride = fs->super->s_raid_stride; /* * If recovery is from backup superblock, Clear _UNININT flags & * reset bg_itable_unused to zero */ if (superblock > 1 && ext2fs_has_group_desc_csum(fs)) { dgrp_t group; for (group = 0; group < fs->group_desc_count; group++) { ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT); ext2fs_bg_itable_unused_set(fs, group, 0); /* The checksum will be reset later, but fix it here * anyway to avoid printing a lot of spurious errors. */ ext2fs_group_desc_csum_set(fs, group); } if (fs->flags & EXT2_FLAG_RW) ext2fs_mark_super_dirty(fs); } if (ext2fs_has_feature_mmp(fs->super) && !(flags & EXT2_FLAG_SKIP_MMP) && (flags & (EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE))) { retval = ext2fs_mmp_start(fs); if (retval) { fs->flags |= EXT2_FLAG_SKIP_MMP; /* just do cleanup */ ext2fs_mmp_stop(fs); goto cleanup; } } if (fs->flags & EXT2_FLAG_SHARE_DUP) { fs->block_sha_map = ext2fs_hashmap_create(ext2fs_djb2_hash, block_sha_map_free_entry, 4096); if (!fs->block_sha_map) { retval = EXT2_ET_NO_MEMORY; goto cleanup; } ext2fs_set_feature_shared_blocks(fs->super); } fs->flags &= ~EXT2_FLAG_NOFREE_ON_ERROR; *ret_fs = fs; return 0; cleanup: if (!(flags & EXT2_FLAG_NOFREE_ON_ERROR)) { ext2fs_free(fs); fs = NULL; } *ret_fs = fs; return retval; }
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 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 == 0) || (ino > fs->super->s_inodes_count)) return EXT2_ET_BAD_INODE_NUM; 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); 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); length = EXT2_INODE_SIZE(fs->super); if (length > bufsize) length = bufsize; 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: if (w_inode && w_inode != &temp_inode) free(w_inode); return retval; }
static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) { dgrp_t i; char *block_bitmap = 0, *inode_bitmap = 0; char *buf; errcode_t retval; int block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8; int inode_nbytes = EXT2_INODES_PER_GROUP(fs->super) / 8; int csum_flag; unsigned int cnt; blk64_t blk; blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block); blk64_t blk_cnt; ext2_ino_t ino_itr = 1; ext2_ino_t ino_cnt; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if ((block_nbytes > (int) fs->blocksize) || (inode_nbytes > (int) fs->blocksize)) return EXT2_ET_CORRUPT_SUPERBLOCK; fs->write_bitmaps = ext2fs_write_bitmaps; csum_flag = ext2fs_has_group_desc_csum(fs); retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); if (retval) return retval; if (do_block) { if (fs->block_map) ext2fs_free_block_bitmap(fs->block_map); strcpy(buf, "block bitmap for "); strcat(buf, fs->device_name); retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); if (retval) goto cleanup; retval = io_channel_alloc_buf(fs->io, 0, &block_bitmap); if (retval) goto cleanup; } else block_nbytes = 0; if (do_inode) { if (fs->inode_map) ext2fs_free_inode_bitmap(fs->inode_map); strcpy(buf, "inode bitmap for "); strcat(buf, fs->device_name); retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); if (retval) goto cleanup; retval = io_channel_alloc_buf(fs->io, 0, &inode_bitmap); if (retval) goto cleanup; } else inode_nbytes = 0; ext2fs_free_mem(&buf); if (fs->flags & EXT2_FLAG_IMAGE_FILE) { blk = (ext2fs_le32_to_cpu(fs->image_header->offset_inodemap) / fs->blocksize); ino_cnt = fs->super->s_inodes_count; while (inode_bitmap && ino_cnt > 0) { retval = io_channel_read_blk64(fs->image_io, blk++, 1, inode_bitmap); if (retval) goto cleanup; cnt = fs->blocksize << 3; if (cnt > ino_cnt) cnt = ino_cnt; retval = ext2fs_set_inode_bitmap_range2(fs->inode_map, ino_itr, cnt, inode_bitmap); if (retval) goto cleanup; ino_itr += cnt; ino_cnt -= cnt; } blk = (ext2fs_le32_to_cpu(fs->image_header->offset_blockmap) / fs->blocksize); blk_cnt = EXT2_GROUPS_TO_CLUSTERS(fs->super, fs->group_desc_count); while (block_bitmap && blk_cnt > 0) { retval = io_channel_read_blk64(fs->image_io, blk++, 1, block_bitmap); if (retval) goto cleanup; cnt = fs->blocksize << 3; if (cnt > blk_cnt) cnt = blk_cnt; retval = ext2fs_set_block_bitmap_range2(fs->block_map, blk_itr, cnt, block_bitmap); if (retval) goto cleanup; blk_itr += cnt; blk_cnt -= cnt; } goto success_cleanup; } for (i = 0; i < fs->group_desc_count; i++) { if (block_bitmap) { blk = ext2fs_block_bitmap_loc(fs, i); if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) && ext2fs_group_desc_csum_verify(fs, i)) blk = 0; if (blk) { retval = io_channel_read_blk64(fs->io, blk, 1, block_bitmap); if (retval) { retval = EXT2_ET_BLOCK_BITMAP_READ; goto cleanup; } /* verify block bitmap checksum */ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) && !ext2fs_block_bitmap_csum_verify(fs, i, block_bitmap, block_nbytes)) { retval = EXT2_ET_BLOCK_BITMAP_CSUM_INVALID; goto cleanup; } } else memset(block_bitmap, 0, block_nbytes); cnt = block_nbytes << 3; retval = ext2fs_set_block_bitmap_range2(fs->block_map, blk_itr, cnt, block_bitmap); if (retval) goto cleanup; blk_itr += block_nbytes << 3; } if (inode_bitmap) { blk = ext2fs_inode_bitmap_loc(fs, i); if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) && ext2fs_group_desc_csum_verify(fs, i)) blk = 0; if (blk) { retval = io_channel_read_blk64(fs->io, blk, 1, inode_bitmap); if (retval) { retval = EXT2_ET_INODE_BITMAP_READ; goto cleanup; } /* verify inode bitmap checksum */ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) && !ext2fs_inode_bitmap_csum_verify(fs, i, inode_bitmap, inode_nbytes)) { retval = EXT2_ET_INODE_BITMAP_CSUM_INVALID; goto cleanup; } } else memset(inode_bitmap, 0, inode_nbytes); cnt = inode_nbytes << 3; retval = ext2fs_set_inode_bitmap_range2(fs->inode_map, ino_itr, cnt, inode_bitmap); if (retval) goto cleanup; ino_itr += inode_nbytes << 3; } } /* Mark group blocks for any BLOCK_UNINIT groups */ if (do_block) { retval = mark_uninit_bg_group_blocks(fs); if (retval) goto cleanup; } success_cleanup: if (inode_bitmap) ext2fs_free_mem(&inode_bitmap); if (block_bitmap) ext2fs_free_mem(&block_bitmap); return 0; cleanup: if (do_block) { ext2fs_free_mem(&fs->block_map); fs->block_map = 0; } if (do_inode) { ext2fs_free_mem(&fs->inode_map); fs->inode_map = 0; } if (inode_bitmap) ext2fs_free_mem(&inode_bitmap); if (block_bitmap) ext2fs_free_mem(&block_bitmap); if (buf) ext2fs_free_mem(&buf); return retval; }
static errcode_t undo_write_tdb(io_channel channel, unsigned long long block, int count) { int size, sz; unsigned long long block_num, backing_blk_num; errcode_t retval = 0; ext2_loff_t offset; struct undo_private_data *data; TDB_DATA tdb_key, tdb_data; unsigned char *read_ptr; unsigned long long end_block; data = (struct undo_private_data *) channel->private_data; if (data->tdb == NULL) { /* * Transaction database not initialized */ return 0; } if (count == 1) size = channel->block_size; else { if (count < 0) size = -count; else size = count * channel->block_size; } /* * Data is stored in tdb database as blocks of tdb_data_size size * This helps in efficient lookup further. * * We divide the disk to blocks of tdb_data_size. */ offset = (block * channel->block_size) + data->offset ; block_num = offset / data->tdb_data_size; end_block = (offset + size) / data->tdb_data_size; tdb_transaction_start(data->tdb); while (block_num <= end_block ) { tdb_key.dptr = (unsigned char *)&block_num; tdb_key.dsize = sizeof(block_num); /* * Check if we have the record already */ if (tdb_exists(data->tdb, tdb_key)) { /* Try the next block */ block_num++; continue; } /* * Read one block using the backing I/O manager * The backing I/O manager block size may be * different from the tdb_data_size. * Also we need to recalcuate the block number with respect * to the backing I/O manager. */ offset = block_num * data->tdb_data_size; backing_blk_num = (offset - data->offset) / channel->block_size; count = data->tdb_data_size + ((offset - data->offset) % channel->block_size); retval = ext2fs_get_mem(count, &read_ptr); if (retval) { tdb_transaction_cancel(data->tdb); return retval; } memset(read_ptr, 0, count); actual_size = 0; if ((count % channel->block_size) == 0) sz = count / channel->block_size; else sz = -count; retval = io_channel_read_blk64(data->real, backing_blk_num, sz, read_ptr); if (retval) { if (retval != EXT2_ET_SHORT_READ) { free(read_ptr); tdb_transaction_cancel(data->tdb); return retval; } /* * short read so update the record size * accordingly */ tdb_data.dsize = actual_size; } else { tdb_data.dsize = data->tdb_data_size; } tdb_data.dptr = read_ptr + ((offset - data->offset) % channel->block_size); #ifdef DEBUG printf("Printing with key %lld data %x and size %d\n", block_num, tdb_data.dptr, tdb_data.dsize); #endif if (!data->tdb_written) { data->tdb_written = 1; /* Write the blocksize to tdb file */ retval = write_block_size(data->tdb, data->tdb_data_size); if (retval) { tdb_transaction_cancel(data->tdb); retval = EXT2_ET_TDB_ERR_IO; free(read_ptr); return retval; } } retval = tdb_store(data->tdb, tdb_key, tdb_data, TDB_INSERT); if (retval == -1) { /* * TDB_ERR_EXISTS cannot happen because we * have already verified it doesn't exist */ tdb_transaction_cancel(data->tdb); retval = EXT2_ET_TDB_ERR_IO; free(read_ptr); return retval; } free(read_ptr); /* Next block */ block_num++; } tdb_transaction_commit(data->tdb); return retval; }
/* * Note: if superblock is non-zero, block-size must also be non-zero. * Superblock and block_size can be zero to use the default size. * * Valid flags for ext2fs_open() * * EXT2_FLAG_RW - Open the filesystem for read/write. * EXT2_FLAG_FORCE - Open the filesystem even if some of the * features aren't supported. * EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device */ errcode_t ext2fs_open2(const char *name, const char *io_options, int flags, int superblock, unsigned int block_size, io_channel * io, ext2_filsys *ret_fs) { ext2_filsys fs; io_manager manager = (*io)->manager; errcode_t retval; unsigned long i, first_meta_bg; __u32 features; int groups_per_block, blocks_per_group, io_flags; blk64_t group_block, blk; char *dest, *cp; #ifdef WORDS_BIGENDIAN struct ext2_group_desc *gdp; int j; #endif EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER); retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); if (retval) return retval; memset(fs, 0, sizeof(struct struct_ext2_filsys)); fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; fs->io = *io; fs->flags = flags; /* don't overwrite sb backups unless flag is explicitly cleared */ fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; fs->umask = 022; retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); cp = strchr(fs->device_name, '?'); if (!io_options && cp) { *cp++ = 0; io_options = cp; } io_flags = 0; if (flags & EXT2_FLAG_RW) io_flags |= IO_FLAG_RW; if (flags & EXT2_FLAG_EXCLUSIVE) io_flags |= IO_FLAG_EXCLUSIVE; if (flags & EXT2_FLAG_DIRECT_IO) io_flags |= IO_FLAG_DIRECT_IO; retval = manager->open(fs->device_name, io_flags, &fs->io); if (retval) goto cleanup; if (io_options && (retval = io_channel_set_options(fs->io, io_options))) goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; retval = ext2fs_get_memalign(SUPERBLOCK_SIZE, 512, &fs->super); if (retval) goto cleanup; if (flags & EXT2_FLAG_IMAGE_FILE) { retval = ext2fs_get_mem(sizeof(struct ext2_image_hdr), &fs->image_header); if (retval) goto cleanup; retval = io_channel_read_blk(fs->io, 0, -(int)sizeof(struct ext2_image_hdr), fs->image_header); if (retval) goto cleanup; if (fs->image_header->magic_number != EXT2_ET_MAGIC_E2IMAGE) return EXT2_ET_MAGIC_E2IMAGE; superblock = 1; block_size = fs->image_header->fs_blocksize; } /* * If the user specifies a specific block # for the * superblock, then he/she must also specify the block size! * Otherwise, read the master superblock located at offset * SUPERBLOCK_OFFSET from the start of the partition. * * Note: we only save a backup copy of the superblock if we * are reading the superblock from the primary superblock location. */ if (superblock) { if (!block_size) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } io_channel_set_blksize(fs->io, block_size); group_block = superblock; fs->orig_super = 0; } else { io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); superblock = 1; group_block = 0; retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); if (retval) goto cleanup; } retval = io_channel_read_blk(fs->io, superblock, -SUPERBLOCK_SIZE, fs->super); if (retval) goto cleanup; if (fs->orig_super) memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE); #ifdef WORDS_BIGENDIAN fs->flags |= EXT2_FLAG_SWAP_BYTES; ext2fs_swap_super(fs->super); #else if (fs->flags & EXT2_FLAG_SWAP_BYTES) { retval = EXT2_ET_UNIMPLEMENTED; goto cleanup; } #endif if (fs->super->s_magic != EXT2_SUPER_MAGIC) { retval = EXT2_ET_BAD_MAGIC; goto cleanup; } if (fs->super->s_rev_level > EXT2_LIB_CURRENT_REV) { retval = EXT2_ET_REV_TOO_HIGH; goto cleanup; } /* * Check for feature set incompatibility */ if (!(flags & EXT2_FLAG_FORCE)) { features = fs->super->s_feature_incompat; #ifdef EXT2_LIB_SOFTSUPP_INCOMPAT if (flags & EXT2_FLAG_SOFTSUPP_FEATURES) features &= !EXT2_LIB_SOFTSUPP_INCOMPAT; #endif if (features & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } features = fs->super->s_feature_ro_compat; #ifdef EXT2_LIB_SOFTSUPP_RO_COMPAT if (flags & EXT2_FLAG_SOFTSUPP_FEATURES) features &= !EXT2_LIB_SOFTSUPP_RO_COMPAT; #endif if ((flags & EXT2_FLAG_RW) && (features & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)) { retval = EXT2_ET_RO_UNSUPP_FEATURE; goto cleanup; } if (!(flags & EXT2_FLAG_JOURNAL_DEV_OK) && (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } } if ((fs->super->s_log_block_size + EXT2_MIN_BLOCK_LOG_SIZE) > EXT2_MAX_BLOCK_LOG_SIZE) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->blocksize = EXT2_BLOCK_SIZE(fs->super); if (EXT2_INODE_SIZE(fs->super) < EXT2_GOOD_OLD_INODE_SIZE) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->clustersize = EXT2_CLUSTER_SIZE(fs->super); fs->inode_blocks_per_group = ((EXT2_INODES_PER_GROUP(fs->super) * EXT2_INODE_SIZE(fs->super) + EXT2_BLOCK_SIZE(fs->super) - 1) / EXT2_BLOCK_SIZE(fs->super)); if (block_size) { if (block_size != fs->blocksize) { retval = EXT2_ET_UNEXPECTED_BLOCK_SIZE; goto cleanup; } } /* * Set the blocksize to the filesystem's blocksize. */ io_channel_set_blksize(fs->io, fs->blocksize); /* * If this is an external journal device, don't try to read * the group descriptors, because they're not there. */ if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { fs->group_desc_count = 0; *ret_fs = fs; return 0; } if (EXT2_INODES_PER_GROUP(fs->super) == 0) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } /* * Read group descriptors */ blocks_per_group = EXT2_BLOCKS_PER_GROUP(fs->super); if (blocks_per_group == 0 || blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(fs->super) || fs->inode_blocks_per_group > EXT2_MAX_INODES_PER_GROUP(fs->super) || EXT2_DESC_PER_BLOCK(fs->super) == 0 || fs->super->s_first_data_block >= ext2fs_blocks_count(fs->super)) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->group_desc_count = ext2fs_div64_ceil(ext2fs_blocks_count(fs->super) - fs->super->s_first_data_block, blocks_per_group); if (fs->group_desc_count * EXT2_INODES_PER_GROUP(fs->super) != fs->super->s_inodes_count) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, EXT2_DESC_PER_BLOCK(fs->super)); retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, &fs->group_desc); if (retval) goto cleanup; if (!group_block) group_block = fs->super->s_first_data_block; dest = (char *) fs->group_desc; groups_per_block = EXT2_DESC_PER_BLOCK(fs->super); if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) first_meta_bg = fs->super->s_first_meta_bg; else first_meta_bg = fs->desc_blocks; if (first_meta_bg) { retval = io_channel_read_blk(fs->io, group_block+1, first_meta_bg, dest); if (retval) goto cleanup; #ifdef WORDS_BIGENDIAN gdp = (struct ext2_group_desc *) dest; for (j=0; j < groups_per_block*first_meta_bg; j++) { gdp = ext2fs_group_desc(fs, fs->group_desc, j); ext2fs_swap_group_desc2(fs, gdp); } #endif dest += fs->blocksize*first_meta_bg; } for (i=first_meta_bg ; i < fs->desc_blocks; i++) { blk = ext2fs_descriptor_block_loc2(fs, group_block, i); retval = io_channel_read_blk64(fs->io, blk, 1, dest); if (retval) goto cleanup; #ifdef WORDS_BIGENDIAN for (j=0; j < groups_per_block; j++) { /* The below happens to work... be careful. */ gdp = ext2fs_group_desc(fs, fs->group_desc, j); ext2fs_swap_group_desc2(fs, gdp); } #endif dest += fs->blocksize; } fs->stride = fs->super->s_raid_stride; /* * If recovery is from backup superblock, Clear _UNININT flags & * reset bg_itable_unused to zero */ if (superblock > 1 && EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { dgrp_t group; for (group = 0; group < fs->group_desc_count; group++) { ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT); ext2fs_bg_itable_unused_set(fs, group, 0); } ext2fs_mark_super_dirty(fs); } fs->flags &= ~EXT2_FLAG_NOFREE_ON_ERROR; *ret_fs = fs; return 0; cleanup: if (flags & EXT2_FLAG_NOFREE_ON_ERROR) *ret_fs = fs; else ext2fs_free(fs); return retval; }