int op_readlink (const char *path, char *buf, size_t size) { int rt; size_t s; errcode_t rc; ext2_ino_t ino; char *b = NULL; char *pathname; struct ext2_inode inode; ext2_filsys e2fs; FUSE_EXT2_LOCK; e2fs = current_ext2fs(); debugf("enter"); debugf("path = %s", path); rt = do_readinode(e2fs, path, &ino, &inode); if (rt) { debugf("do_readinode(%s, &ino, &inode); failed", path); goto err; } if (!LINUX_S_ISLNK(inode.i_mode)) { debugf("%s is not a link", path); rt = -EINVAL; goto err; } if (ext2fs_inode_data_blocks(e2fs, &inode)) { rc = ext2fs_get_mem(EXT2_BLOCK_SIZE(e2fs->super), &b); if (rc) { debugf("ext2fs_get_mem(EXT2_BLOCK_SIZE(e2fs->super), &b); failed"); rt = -ENOMEM; goto err; } rc = io_channel_read_blk(e2fs->io, inode.i_block[0], 1, b); if (rc) { ext2fs_free_mem(&b); debugf("io_channel_read_blk(e2fs->io, inode.i_block[0], 1, b); failed"); rt = -EIO; goto err; } pathname = b; } else { pathname = (char *) &(inode.i_block[0]); } debugf("pathname: %s", pathname); s = (size < strlen(pathname) + 1) ? size : strlen(pathname) + 1; snprintf(buf, s, "%s", pathname); if (b) { ext2fs_free_mem(&b); } debugf("leave"); FUSE_EXT2_UNLOCK; return 0; err: FUSE_EXT2_UNLOCK; return rt; }
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; #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, "fhnvz:")) != EOF) { switch (c) { case 'f': force = 1; break; case 'h': dump = 1; break; case 'n': dry_run = 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); if (!force && (undo_ctx.hdr.f_compat || 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 (!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; }
errcode_t ext2fs_initialize(const char *name, int flags, struct ext2_super_block *param, io_manager manager, ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; struct ext2_super_block *super; unsigned int rem; unsigned int overhead = 0; unsigned int ipg; dgrp_t i; blk64_t free_blocks; blk_t numblocks; int rsv_gdt; int csum_flag; int bigalloc_flag; int io_flags; unsigned reserved_inos; char *buf = 0; char c; if (!param || !ext2fs_blocks_count(param)) return EXT2_ET_INVALID_ARGUMENT; 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 | EXT2_FLAG_RW; fs->umask = 022; fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE; #ifdef WORDS_BIGENDIAN fs->flags |= EXT2_FLAG_SWAP_BYTES; #endif 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(name, io_flags, &fs->io); if (retval) goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super); if (retval) goto cleanup; fs->super = super; memset(super, 0, SUPERBLOCK_SIZE); #define set_field(field, default) (super->field = param->field ? \ param->field : (default)) #define assign_field(field) (super->field = param->field) super->s_magic = EXT2_SUPER_MAGIC; super->s_state = EXT2_VALID_FS; bigalloc_flag = EXT2_HAS_RO_COMPAT_FEATURE(param, EXT4_FEATURE_RO_COMPAT_BIGALLOC); assign_field(s_log_block_size); if (bigalloc_flag) { set_field(s_log_cluster_size, super->s_log_block_size+4); if (super->s_log_block_size > super->s_log_cluster_size) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } } else super->s_log_cluster_size = super->s_log_block_size; set_field(s_first_data_block, super->s_log_cluster_size ? 0 : 1); set_field(s_max_mnt_count, 0); set_field(s_errors, EXT2_ERRORS_DEFAULT); set_field(s_feature_compat, 0); set_field(s_feature_incompat, 0); set_field(s_feature_ro_compat, 0); set_field(s_default_mount_opts, 0); set_field(s_first_meta_bg, 0); set_field(s_raid_stride, 0); /* default stride size: 0 */ set_field(s_raid_stripe_width, 0); /* default stripe width: 0 */ set_field(s_log_groups_per_flex, 0); set_field(s_flags, 0); if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { retval = EXT2_ET_RO_UNSUPP_FEATURE; goto cleanup; } set_field(s_rev_level, EXT2_GOOD_OLD_REV); if (super->s_rev_level >= EXT2_DYNAMIC_REV) { set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO); set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE); if (super->s_inode_size >= sizeof(struct ext2_inode_large)) { int extra_isize = sizeof(struct ext2_inode_large) - EXT2_GOOD_OLD_INODE_SIZE; set_field(s_min_extra_isize, extra_isize); set_field(s_want_extra_isize, extra_isize); } } else { super->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; super->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; } set_field(s_checkinterval, 0); super->s_mkfs_time = super->s_lastcheck = fs->now ? fs->now : time(NULL); super->s_creator_os = CREATOR_OS; fs->fragsize = fs->blocksize = EXT2_BLOCK_SIZE(super); fs->cluster_ratio_bits = super->s_log_cluster_size - super->s_log_block_size; if (bigalloc_flag) { if (param->s_blocks_per_group && param->s_clusters_per_group && ((param->s_clusters_per_group * EXT2FS_CLUSTER_RATIO(fs)) != param->s_blocks_per_group)) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } if (param->s_clusters_per_group) assign_field(s_clusters_per_group); else if (param->s_blocks_per_group) super->s_clusters_per_group = param->s_blocks_per_group / EXT2FS_CLUSTER_RATIO(fs); else super->s_clusters_per_group = fs->blocksize * 8; if (super->s_clusters_per_group > EXT2_MAX_CLUSTERS_PER_GROUP(super)) super->s_clusters_per_group = EXT2_MAX_CLUSTERS_PER_GROUP(super); super->s_blocks_per_group = EXT2FS_C2B(fs, super->s_clusters_per_group); } else { set_field(s_blocks_per_group, fs->blocksize * 8); if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super)) super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super); super->s_clusters_per_group = super->s_blocks_per_group; } ext2fs_blocks_count_set(super, ext2fs_blocks_count(param) & ~((blk64_t) EXT2FS_CLUSTER_MASK(fs))); ext2fs_r_blocks_count_set(super, ext2fs_r_blocks_count(param)); if (ext2fs_r_blocks_count(super) >= ext2fs_blocks_count(param)) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } /* * If we're creating an external journal device, we don't need * to bother with the rest. */ if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { fs->group_desc_count = 0; ext2fs_mark_super_dirty(fs); *ret_fs = fs; return 0; } retry: fs->group_desc_count = (dgrp_t) ext2fs_div64_ceil( ext2fs_blocks_count(super) - super->s_first_data_block, EXT2_BLOCKS_PER_GROUP(super)); if (fs->group_desc_count == 0) { retval = EXT2_ET_TOOSMALL; goto cleanup; } if (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) super->s_desc_size = EXT2_MIN_DESC_SIZE_64BIT; fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, EXT2_DESC_PER_BLOCK(super)); i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize; if (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT && (ext2fs_blocks_count(super) / i) > (1ULL << 32)) set_field(s_inodes_count, ~0U); else set_field(s_inodes_count, ext2fs_blocks_count(super) / i); /* * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so * that we have enough inodes for the filesystem(!) */ if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1) super->s_inodes_count = EXT2_FIRST_INODE(super)+1; /* * There should be at least as many inodes as the user * requested. Figure out how many inodes per group that * should be. But make sure that we don't allocate more than * one bitmap's worth of inodes each group. */ ipg = ext2fs_div_ceil(super->s_inodes_count, fs->group_desc_count); if (ipg > fs->blocksize * 8) { if (!bigalloc_flag && super->s_blocks_per_group >= 256) { /* Try again with slightly different parameters */ super->s_blocks_per_group -= 8; ext2fs_blocks_count_set(super, ext2fs_blocks_count(param)); super->s_clusters_per_group = super->s_blocks_per_group; goto retry; } else { retval = EXT2_ET_TOO_MANY_INODES; goto cleanup; } } if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super)) ipg = EXT2_MAX_INODES_PER_GROUP(super); ipg_retry: super->s_inodes_per_group = ipg; /* * Make sure the number of inodes per group completely fills * the inode table blocks in the descriptor. If not, add some * additional inodes/group. Waste not, want not... */ fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); super->s_inodes_per_group = ((fs->inode_blocks_per_group * EXT2_BLOCK_SIZE(super)) / EXT2_INODE_SIZE(super)); /* * Finally, make sure the number of inodes per group is a * multiple of 8. This is needed to simplify the bitmap * splicing code. */ if (super->s_inodes_per_group < 8) super->s_inodes_per_group = 8; super->s_inodes_per_group &= ~7; fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); /* * adjust inode count to reflect the adjusted inodes_per_group */ if ((__u64)super->s_inodes_per_group * fs->group_desc_count > ~0U) { ipg--; goto ipg_retry; } super->s_inodes_count = super->s_inodes_per_group * fs->group_desc_count; super->s_free_inodes_count = super->s_inodes_count; /* * check the number of reserved group descriptor table blocks */ if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) rsv_gdt = calc_reserved_gdt_blocks(fs); else rsv_gdt = 0; set_field(s_reserved_gdt_blocks, rsv_gdt); if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) { retval = EXT2_ET_RES_GDT_BLOCKS; goto cleanup; } /* * Calculate the maximum number of bookkeeping blocks per * group. It includes the superblock, the block group * descriptors, the block bitmap, the inode bitmap, the inode * table, and the reserved gdt blocks. */ overhead = (int) (3 + fs->inode_blocks_per_group + fs->desc_blocks + super->s_reserved_gdt_blocks); /* This can only happen if the user requested too many inodes */ if (overhead > super->s_blocks_per_group) { retval = EXT2_ET_TOO_MANY_INODES; goto cleanup; } /* * See if the last group is big enough to support the * necessary data structures. If not, we need to get rid of * it. We need to recalculate the overhead for the last block * group, since it might or might not have a superblock * backup. */ overhead = (int) (2 + fs->inode_blocks_per_group); if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1)) overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks; rem = ((ext2fs_blocks_count(super) - super->s_first_data_block) % super->s_blocks_per_group); if ((fs->group_desc_count == 1) && rem && (rem < overhead)) { retval = EXT2_ET_TOOSMALL; goto cleanup; } if (rem && (rem < overhead+50)) { ext2fs_blocks_count_set(super, ext2fs_blocks_count(super) - rem); goto retry; } /* * At this point we know how big the filesystem will be. So * we can do any and all allocations that depend on the block * count. */ retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); if (retval) goto cleanup; strcpy(buf, "block bitmap for "); strcat(buf, fs->device_name); retval = ext2fs_allocate_subcluster_bitmap(fs, buf, &fs->block_map); if (retval) goto cleanup; strcpy(buf, "inode bitmap for "); strcat(buf, fs->device_name); retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); if (retval) goto cleanup; ext2fs_free_mem(&buf); retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, &fs->group_desc); if (retval) goto cleanup; memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize); /* * Reserve the superblock and group descriptors for each * group, and fill in the correct group statistics for group. * Note that although the block bitmap, inode bitmap, and * inode table have not been allocated (and in fact won't be * by this routine), they are accounted for nevertheless. * * If FLEX_BG meta-data grouping is used, only account for the * superblock and group descriptors (the inode tables and * bitmaps will be accounted for when allocated). */ free_blocks = 0; csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM); reserved_inos = super->s_first_ino; for (i = 0; i < fs->group_desc_count; i++) { /* * Don't set the BLOCK_UNINIT group for the last group * because the block bitmap needs to be padded. */ if (csum_flag) { if (i != fs->group_desc_count - 1) ext2fs_bg_flags_set(fs, i, EXT2_BG_BLOCK_UNINIT); ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_UNINIT); numblocks = super->s_inodes_per_group; if (reserved_inos) { if (numblocks > reserved_inos) { numblocks -= reserved_inos; reserved_inos = 0; } else { reserved_inos -= numblocks; numblocks = 0; } } ext2fs_bg_itable_unused_set(fs, i, numblocks); } numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map); if (fs->super->s_log_groups_per_flex) numblocks += 2 + fs->inode_blocks_per_group; free_blocks += numblocks; ext2fs_bg_free_blocks_count_set(fs, i, numblocks); ext2fs_bg_free_inodes_count_set(fs, i, fs->super->s_inodes_per_group); ext2fs_bg_used_dirs_count_set(fs, i, 0); ext2fs_group_desc_csum_set(fs, i); } free_blocks &= ~EXT2FS_CLUSTER_MASK(fs); ext2fs_free_blocks_count_set(super, free_blocks); c = (char) 255; if (((int) c) == -1) { super->s_flags |= EXT2_FLAGS_SIGNED_HASH; } else { super->s_flags |= EXT2_FLAGS_UNSIGNED_HASH; } ext2fs_mark_super_dirty(fs); ext2fs_mark_bb_dirty(fs); ext2fs_mark_ib_dirty(fs); io_channel_set_blksize(fs->io, fs->blocksize); *ret_fs = fs; return 0; cleanup: free(buf); ext2fs_free(fs); return retval; }
extern errcode_t ext2fs_extent_open2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, ext2_extent_handle_t *ret_handle) { struct ext2_extent_handle *handle; errcode_t retval; int i; struct ext3_extent_header *eh; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!inode) if ((ino == 0) || (ino > fs->super->s_inodes_count)) return EXT2_ET_BAD_INODE_NUM; retval = ext2fs_get_mem(sizeof(struct ext2_extent_handle), &handle); if (retval) return retval; memset(handle, 0, sizeof(struct ext2_extent_handle)); retval = ext2fs_get_mem(sizeof(struct ext2_inode), &handle->inode); if (retval) goto errout; handle->ino = ino; handle->fs = fs; if (inode) { memcpy(handle->inode, inode, sizeof(struct ext2_inode)); } else { retval = ext2fs_read_inode(fs, ino, handle->inode); if (retval) goto errout; } eh = (struct ext3_extent_header *) &handle->inode->i_block[0]; for (i=0; i < EXT2_N_BLOCKS; i++) if (handle->inode->i_block[i]) break; if (i >= EXT2_N_BLOCKS) { eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC); eh->eh_depth = 0; eh->eh_entries = 0; i = (sizeof(handle->inode->i_block) - sizeof(*eh)) / sizeof(struct ext3_extent); eh->eh_max = ext2fs_cpu_to_le16(i); handle->inode->i_flags |= EXT4_EXTENTS_FL; } if (!(handle->inode->i_flags & EXT4_EXTENTS_FL)) { retval = EXT2_ET_INODE_NOT_EXTENT; goto errout; } retval = ext2fs_extent_header_verify(eh, sizeof(handle->inode->i_block)); if (retval) goto errout; handle->max_depth = ext2fs_le16_to_cpu(eh->eh_depth); handle->type = ext2fs_le16_to_cpu(eh->eh_magic); retval = ext2fs_get_mem(((handle->max_depth+1) * sizeof(struct extent_path)), &handle->path); memset(handle->path, 0, (handle->max_depth+1) * sizeof(struct extent_path)); handle->path[0].buf = (char *) handle->inode->i_block; handle->path[0].left = handle->path[0].entries = ext2fs_le16_to_cpu(eh->eh_entries); handle->path[0].max_entries = ext2fs_le16_to_cpu(eh->eh_max); handle->path[0].curr = 0; handle->path[0].end_blk = ((((__u64) handle->inode->i_size_high << 32) + handle->inode->i_size + (fs->blocksize - 1)) >> EXT2_BLOCK_SIZE_BITS(fs->super)); handle->path[0].visit_num = 1; handle->level = 0; handle->magic = EXT2_ET_MAGIC_EXTENT_HANDLE; *ret_handle = handle; return 0; errout: ext2fs_extent_free(handle); return retval; }
static errcode_t unix_open(const char *name, int flags, io_channel *channel) { io_channel io = NULL; struct unix_private_data *data = NULL; errcode_t retval; int open_flags, zeroes = 0; struct stat st; #ifdef __linux__ struct utsname ut; #endif if (name == 0) return EXT2_ET_BAD_DEVICE_NAME; retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); if (retval) return retval; memset(io, 0, sizeof(struct struct_io_channel)); io->magic = EXT2_ET_MAGIC_IO_CHANNEL; retval = ext2fs_get_mem(sizeof(struct unix_private_data), &data); if (retval) goto cleanup; io->manager = unix_io_manager; retval = ext2fs_get_mem(strlen(name)+1, &io->name); if (retval) goto cleanup; strcpy(io->name, name); io->private_data = data; io->block_size = 1024; io->read_error = 0; io->write_error = 0; io->refcount = 1; memset(data, 0, sizeof(struct unix_private_data)); data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL; data->io_stats.num_fields = 2; open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY; if (flags & IO_FLAG_EXCLUSIVE) open_flags |= O_EXCL; if (flags & IO_FLAG_DIRECT_IO) open_flags |= O_DIRECT; data->flags = flags; #ifdef HAVE_OPEN64 data->dev = open64(io->name, open_flags); #else data->dev = open(io->name, open_flags); #endif if (data->dev < 0) { retval = errno; goto cleanup; } #ifdef BLKSSZGET if (flags & IO_FLAG_DIRECT_IO) { if (ioctl(data->dev, BLKSSZGET, &data->align) != 0) data->align = io->block_size; } #endif #ifdef BLKDISCARDZEROES ioctl(data->dev, BLKDISCARDZEROES, &zeroes); if (zeroes) io->flags |= CHANNEL_FLAGS_DISCARD_ZEROES; #endif #if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) /* * Some operating systems require that the buffers be aligned, * regardless of O_DIRECT */ data->align = 512; #endif if ((retval = alloc_cache(io, data))) goto cleanup; #ifdef BLKROGET if (flags & IO_FLAG_RW) { int error; int readonly = 0; /* Is the block device actually writable? */ error = ioctl(data->dev, BLKROGET, &readonly); if (!error && readonly) { close(data->dev); retval = EPERM; goto cleanup; } } #endif #ifdef __linux__ #undef RLIM_INFINITY #if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG == 4))) #define RLIM_INFINITY ((unsigned long)(~0UL>>1)) #else #define RLIM_INFINITY (~0UL) #endif /* * Work around a bug in 2.4.10-2.4.18 kernels where writes to * block devices are wrongly getting hit by the filesize * limit. This workaround isn't perfect, since it won't work * if glibc wasn't built against 2.2 header files. (Sigh.) * */ if ((flags & IO_FLAG_RW) && (uname(&ut) == 0) && ((ut.release[0] == '2') && (ut.release[1] == '.') && (ut.release[2] == '4') && (ut.release[3] == '.') && (ut.release[4] == '1') && (ut.release[5] >= '0') && (ut.release[5] < '8')) && (fstat(data->dev, &st) == 0) && (S_ISBLK(st.st_mode))) { struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY; setrlimit(RLIMIT_FSIZE, &rlim); getrlimit(RLIMIT_FSIZE, &rlim); if (((unsigned long) rlim.rlim_cur) < ((unsigned long) rlim.rlim_max)) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_FSIZE, &rlim); } } #endif *channel = io; return 0; cleanup: if (data) { free_cache(data); ext2fs_free_mem(&data); } if (io) ext2fs_free_mem(&io); return retval; }
static errcode_t unix_open(const char *name, int flags, io_channel *channel) { io_channel io = NULL; struct unix_private_data *data = NULL; errcode_t retval; int open_flags; struct stat st; #ifdef __linux__ struct utsname ut; #endif if (name == 0) return EXT2_ET_BAD_DEVICE_NAME; retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); if (retval) return retval; memset(io, 0, sizeof(struct struct_io_channel)); io->magic = EXT2_ET_MAGIC_IO_CHANNEL; retval = ext2fs_get_mem(sizeof(struct unix_private_data), &data); if (retval) goto cleanup; io->manager = unix_io_manager; retval = ext2fs_get_mem(strlen(name)+1, &io->name); if (retval) goto cleanup; strcpy(io->name, name); io->private_data = data; io->block_size = 1024; io->read_error = 0; io->write_error = 0; io->refcount = 1; memset(data, 0, sizeof(struct unix_private_data)); data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL; if ((retval = alloc_cache(io, data))) goto cleanup; open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY; #ifdef CONFIG_LFS data->dev = open64(io->name, open_flags); #else data->dev = open(io->name, open_flags); #endif if (data->dev < 0) { retval = errno; goto cleanup; } #ifdef __linux__ #undef RLIM_INFINITY #if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG \ == 4))) #define RLIM_INFINITY ((unsigned long)(~0UL>>1)) #else #define RLIM_INFINITY (~0UL) #endif if ((flags & IO_FLAG_RW) && (uname(&ut) == 0) && ((ut.release[0] == '2') && (ut.release[1] == '.') && (ut.release[2] == '4') && (ut.release[3] == '.') && (ut.release[4] == '1') && (ut.release[5] >= '0') && (ut.release[5] < '8')) && (fstat(data->dev, &st) == 0) && (S_ISBLK(st.st_mode))) { struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY; setrlimit(RLIMIT_FSIZE, &rlim); getrlimit(RLIMIT_FSIZE, &rlim); if (((unsigned long) rlim.rlim_cur) < ((unsigned long) rlim.rlim_max)) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_FSIZE, &rlim); } } #endif *channel = io; return 0; cleanup: if (data) { free_cache(data); ext2fs_free_mem(&data); } ext2fs_free_mem(&io); return retval; }
errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size, ext2_icount_t hint, ext2_icount_t *ret) { ext2_icount_t icount; errcode_t retval; size_t bytes; ext2_ino_t i; if (hint) { EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT); if (hint->size > size) size = (size_t) hint->size; } retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount); if (retval) return retval; memset(icount, 0, sizeof(struct ext2_icount)); retval = ext2fs_allocate_inode_bitmap(fs, 0, &icount->single); if (retval) goto errout; if (flags & EXT2_ICOUNT_OPT_INCREMENT) { retval = ext2fs_allocate_inode_bitmap(fs, 0, &icount->multiple); if (retval) goto errout; } else icount->multiple = 0; if (size) { icount->size = size; } else { /* * Figure out how many special case inode counts we will * have. We know we will need one for each directory; * we also need to reserve some extra room for file links */ retval = ext2fs_get_num_dirs(fs, &icount->size); if (retval) goto errout; icount->size += fs->super->s_inodes_count / 50; } bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el)); #if 0 printf("Icount allocated %d entries, %d bytes.\n", icount->size, bytes); #endif retval = ext2fs_get_mem(bytes, &icount->list); if (retval) goto errout; memset(icount->list, 0, bytes); icount->magic = EXT2_ET_MAGIC_ICOUNT; icount->count = 0; icount->cursor = 0; icount->num_inodes = fs->super->s_inodes_count; /* * Populate the sorted list with those entries which were * found in the hint icount (since those are ones which will * likely need to be in the sorted list this time around). */ if (hint) { for (i=0; i < hint->count; i++) icount->list[i].ino = hint->list[i].ino; icount->count = hint->count; } *ret = icount; return 0; errout: ext2fs_free_icount(icount); return(retval); }
/* * Create new directory block */ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, ext2_ino_t parent_ino, char **block) { struct ext2_dir_entry *dir = NULL; errcode_t retval; char *buf; int rec_len; int filetype = 0; struct ext2_dir_entry_tail *t; int csum_size = 0; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; memset(buf, 0, fs->blocksize); dir = (struct ext2_dir_entry *) buf; if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) csum_size = sizeof(struct ext2_dir_entry_tail); retval = ext2fs_set_rec_len(fs, fs->blocksize - csum_size, dir); if (retval) { ext2fs_free_mem(&buf); return retval; } if (dir_ino) { if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) filetype = EXT2_FT_DIR; /* * Set up entry for '.' */ dir->inode = dir_ino; ext2fs_dirent_set_name_len(dir, 1); ext2fs_dirent_set_file_type(dir, filetype); dir->name[0] = '.'; rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1); dir->rec_len = EXT2_DIR_REC_LEN(1); /* * Set up entry for '..' */ dir = (struct ext2_dir_entry *) (buf + dir->rec_len); retval = ext2fs_set_rec_len(fs, rec_len, dir); if (retval) { ext2fs_free_mem(&buf); return retval; } dir->inode = parent_ino; ext2fs_dirent_set_name_len(dir, 2); ext2fs_dirent_set_file_type(dir, filetype); dir->name[0] = '.'; dir->name[1] = '.'; } if (csum_size) { t = EXT2_DIRENT_TAIL(buf, fs->blocksize); ext2fs_initialize_dirent_tail(fs, t); } *block = buf; return 0; }
errcode_t ext2fs_move_blocks(ext2_filsys fs, ext2fs_block_bitmap reserve, ext2fs_block_bitmap alloc_map, int flags) { ext2_ino_t ino; struct ext2_inode inode; errcode_t retval; struct process_block_struct pb; ext2_inode_scan scan; char *block_buf; retval = ext2fs_open_inode_scan(fs, 0, &scan); if (retval) return retval; pb.reserve = reserve; pb.error = 0; pb.alloc_map = alloc_map ? alloc_map : fs->block_map; pb.flags = flags; retval = ext2fs_get_mem(fs->blocksize * 4, &block_buf); if (retval) return retval; pb.buf = block_buf + fs->blocksize * 3; /* * If GET_DBLIST is set in the flags field, then we should * gather directory block information while we're doing the * block move. */ if (flags & EXT2_BMOVE_GET_DBLIST) { ext2fs_free_dblist(fs->dblist); fs->dblist = NULL; retval = ext2fs_init_dblist(fs, 0); if (retval) return retval; } retval = ext2fs_get_next_inode(scan, &ino, &inode); if (retval) return retval; while (ino) { if ((inode.i_links_count == 0) || !ext2fs_inode_has_valid_blocks(&inode)) goto next; pb.ino = ino; pb.inode = &inode; pb.add_dir = (LINUX_S_ISDIR(inode.i_mode) && flags & EXT2_BMOVE_GET_DBLIST); retval = ext2fs_block_iterate2(fs, ino, 0, block_buf, process_block, &pb); if (retval) return retval; if (pb.error) return pb.error; next: retval = ext2fs_get_next_inode(scan, &ino, &inode); if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) goto next; } return 0; }
blk_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; blk_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_blk(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); }
static errcode_t unix_open(const char *name, int flags, io_channel *channel) { io_channel io = NULL; struct unix_private_data *data = NULL; errcode_t retval; int open_flags; struct stat st; #ifdef __linux__ struct utsname ut; #endif if (name == 0) return EXT2_ET_BAD_DEVICE_NAME; retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); if (retval) return retval; memset(io, 0, sizeof(struct struct_io_channel)); io->magic = EXT2_ET_MAGIC_IO_CHANNEL; retval = ext2fs_get_mem(sizeof(struct unix_private_data), &data); if (retval) goto cleanup; io->manager = unix_io_manager; retval = ext2fs_get_mem(strlen(name)+1, &io->name); if (retval) goto cleanup; strcpy(io->name, name); io->private_data = data; io->block_size = 1024; io->read_error = 0; io->write_error = 0; io->refcount = 1; memset(data, 0, sizeof(struct unix_private_data)); data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL; if ((retval = alloc_cache(io, data))) goto cleanup; open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY; if (flags & IO_FLAG_EXCLUSIVE) open_flags |= O_EXCL; #ifdef HAVE_OPEN64 data->dev = open64(io->name, open_flags); #else data->dev = open(io->name, open_flags); #endif if (data->dev < 0) { retval = errno; goto cleanup; } #ifdef __linux__ #undef RLIM_INFINITY #if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG == 4))) #define RLIM_INFINITY ((unsigned long)(~0UL>>1)) #else #define RLIM_INFINITY (~0UL) #endif /* * Work around a bug in 2.4.10-2.4.18 kernels where writes to * block devices are wrongly getting hit by the filesize * limit. This workaround isn't perfect, since it won't work * if glibc wasn't built against 2.2 header files. (Sigh.) * */ if ((flags & IO_FLAG_RW) && (uname(&ut) == 0) && ((ut.release[0] == '2') && (ut.release[1] == '.') && (ut.release[2] == '4') && (ut.release[3] == '.') && (ut.release[4] == '1') && (ut.release[5] >= '0') && (ut.release[5] < '8')) && (fstat(data->dev, &st) == 0) && (S_ISBLK(st.st_mode))) { struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY; setrlimit(RLIMIT_FSIZE, &rlim); getrlimit(RLIMIT_FSIZE, &rlim); if (((unsigned long) rlim.rlim_cur) < ((unsigned long) rlim.rlim_max)) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_FSIZE, &rlim); } } #endif *channel = io; return 0; cleanup: if (data) { free_cache(data); ext2fs_free_mem(&data); } if (io) ext2fs_free_mem(&io); return retval; }
static errcode_t copy_file(ext2_filsys fs, int fd, ext2_ino_t newfile, int bufsize, int make_holes) { ext2_file_t e2_file; errcode_t retval, close_ret; int got; unsigned int written; char *buf; char *ptr; char *zero_buf; int cmp; retval = ext2fs_file_open(fs, newfile, EXT2_FILE_WRITE, &e2_file); if (retval) return retval; retval = ext2fs_get_mem(bufsize, &buf); if (retval) { com_err("copy_file", retval, "can't allocate buffer\n"); goto out_close; } /* This is used for checking whether the whole block is zero */ retval = ext2fs_get_memzero(bufsize, &zero_buf); if (retval) { com_err("copy_file", retval, "can't allocate zero buffer\n"); goto out_free_buf; } while (1) { got = read(fd, buf, bufsize); if (got == 0) break; if (got < 0) { retval = errno; goto fail; } ptr = buf; /* Sparse copy */ if (make_holes) { /* Check whether all is zero */ cmp = memcmp(ptr, zero_buf, got); if (cmp == 0) { /* The whole block is zero, make a hole */ retval = ext2fs_file_lseek(e2_file, got, EXT2_SEEK_CUR, NULL); if (retval) goto fail; got = 0; } } /* Normal copy */ while (got > 0) { retval = ext2fs_file_write(e2_file, ptr, got, &written); if (retval) goto fail; got -= written; ptr += written; } } fail: ext2fs_free_mem(&zero_buf); out_free_buf: ext2fs_free_mem(&buf); out_close: close_ret = ext2fs_file_close(e2_file); if (retval == 0) retval = close_ret; return retval; }
static errcode_t set_inode_xattr(ext2_filsys fs, ext2_ino_t ino, const char *filename) { #ifdef HAVE_LLISTXATTR errcode_t retval, close_retval; struct ext2_inode inode; struct ext2_xattr_handle *handle; ssize_t size, value_size; char *list; int i; size = llistxattr(filename, NULL, 0); if (size == -1) { com_err(__func__, errno, "llistxattr failed on %s", filename); return errno; } else if (size == 0) { return 0; } retval = ext2fs_xattrs_open(fs, ino, &handle); if (retval) { if (retval == EXT2_ET_MISSING_EA_FEATURE) return 0; com_err(__func__, retval, "while opening inode %u", ino); return retval; } retval = ext2fs_get_mem(size, &list); if (retval) { com_err(__func__, retval, "whilst allocating memory"); goto out; } size = llistxattr(filename, list, size); if (size == -1) { com_err(__func__, errno, "llistxattr failed on %s", filename); retval = errno; goto out; } for (i = 0; i < size; i += strlen(&list[i]) + 1) { const char *name = &list[i]; char *value; value_size = getxattr(filename, name, NULL, 0); if (value_size == -1) { com_err(__func__, errno, "getxattr failed on %s", filename); retval = errno; break; } retval = ext2fs_get_mem(value_size, &value); if (retval) { com_err(__func__, retval, "whilst allocating memory"); break; } value_size = getxattr(filename, name, value, value_size); if (value_size == -1) { ext2fs_free_mem(&value); com_err(__func__, errno, "getxattr failed on %s", filename); retval = errno; break; } retval = ext2fs_xattr_set(handle, name, value, value_size); ext2fs_free_mem(&value); if (retval) { com_err(__func__, retval, "while writing xattr %u", ino); break; } } out: ext2fs_free_mem(&list); close_retval = ext2fs_xattrs_close(&handle); if (close_retval) { com_err(__func__, retval, "while closing inode %u", ino); retval = retval ? retval : close_retval; } return retval; #else /* HAVE_LLISTXATTR */ return 0; #endif /* HAVE_LLISTXATTR */ }
errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest) { ext2_filsys fs; errcode_t retval; EXT2_CHECK_MAGIC(src, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); if (retval) return retval; *fs = *src; fs->device_name = 0; fs->super = 0; fs->orig_super = 0; fs->group_desc = 0; fs->inode_map = 0; fs->block_map = 0; fs->badblocks = 0; fs->dblist = 0; io_channel_bumpcount(fs->io); if (fs->icache) fs->icache->refcount++; retval = ext2fs_get_mem(strlen(src->device_name)+1, &fs->device_name); if (retval) goto errout; strcpy(fs->device_name, src->device_name); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super); if (retval) goto errout; memcpy(fs->super, src->super, SUPERBLOCK_SIZE); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); if (retval) goto errout; memcpy(fs->orig_super, src->orig_super, SUPERBLOCK_SIZE); retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize, &fs->group_desc); if (retval) goto errout; memcpy(fs->group_desc, src->group_desc, (size_t) fs->desc_blocks * fs->blocksize); if (src->inode_map) { retval = ext2fs_copy_bitmap(src->inode_map, &fs->inode_map); if (retval) goto errout; } if (src->block_map) { retval = ext2fs_copy_bitmap(src->block_map, &fs->block_map); if (retval) goto errout; } if (src->badblocks) { retval = ext2fs_badblocks_copy(src->badblocks, &fs->badblocks); if (retval) goto errout; } if (src->dblist) { retval = ext2fs_copy_dblist(src->dblist, &fs->dblist); if (retval) goto errout; } *dest = fs; return 0; errout: ext2fs_free(fs); return retval; }
errcode_t ext2fs_alloc_generic_bmap(ext2_filsys fs, errcode_t magic, int type, __u64 start, __u64 end, __u64 real_end, const char *descr, ext2fs_generic_bitmap *ret) { ext2fs_generic_bitmap bitmap; struct ext2_bitmap_ops *ops; errcode_t retval; switch (type) { case EXT2FS_BMAP64_BITARRAY: ops = &ext2fs_blkmap64_bitarray; break; default: return EINVAL; } retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap), &bitmap); if (retval) return retval; /* XXX factor out, repeated in copy_bmap */ bitmap->magic = magic; bitmap->fs = fs; bitmap->start = start; bitmap->end = end; bitmap->real_end = real_end; bitmap->bitmap_ops = ops; bitmap->cluster_bits = 0; switch (magic) { case EXT2_ET_MAGIC_INODE_BITMAP64: bitmap->base_error_code = EXT2_ET_BAD_INODE_MARK; break; case EXT2_ET_MAGIC_BLOCK_BITMAP64: bitmap->base_error_code = EXT2_ET_BAD_BLOCK_MARK; bitmap->cluster_bits = fs->cluster_ratio_bits; break; default: bitmap->base_error_code = EXT2_ET_BAD_GENERIC_MARK; } if (descr) { retval = ext2fs_get_mem(strlen(descr)+1, &bitmap->description); if (retval) { ext2fs_free_mem(&bitmap); return retval; } strcpy(bitmap->description, descr); } else bitmap->description = 0; retval = bitmap->bitmap_ops->new_bmap(fs, bitmap); if (retval) { ext2fs_free_mem(&bitmap->description); ext2fs_free_mem(&bitmap); return retval; } *ret = bitmap; return 0; }
static errcode_t set_inode_xattr(ext2_filsys fs, ext2_ino_t ino, const char *filename) { errcode_t retval, close_retval; struct ext2_xattr_handle *handle; ssize_t size, value_size; char *list = NULL; int i; size = llistxattr(filename, NULL, 0); if (size == -1) { retval = errno; com_err(__func__, retval, _("while listing attributes of \"%s\""), filename); return retval; } else if (size == 0) { return 0; } retval = ext2fs_xattrs_open(fs, ino, &handle); if (retval) { if (retval == EXT2_ET_MISSING_EA_FEATURE) return 0; com_err(__func__, retval, _("while opening inode %u"), ino); return retval; } retval = ext2fs_get_mem(size, &list); if (retval) { com_err(__func__, retval, _("while allocating memory")); goto out; } size = llistxattr(filename, list, size); if (size == -1) { retval = errno; com_err(__func__, retval, _("while listing attributes of \"%s\""), filename); goto out; } for (i = 0; i < size; i += strlen(&list[i]) + 1) { const char *name = &list[i]; char *value; value_size = lgetxattr(filename, name, NULL, 0); if (value_size == -1) { retval = errno; com_err(__func__, retval, _("while reading attribute \"%s\" of \"%s\""), name, filename); break; } retval = ext2fs_get_mem(value_size, &value); if (retval) { com_err(__func__, retval, _("while allocating memory")); break; } value_size = lgetxattr(filename, name, value, value_size); if (value_size == -1) { ext2fs_free_mem(&value); retval = errno; com_err(__func__, retval, _("while reading attribute \"%s\" of \"%s\""), name, filename); break; } retval = ext2fs_xattr_set(handle, name, value, value_size); ext2fs_free_mem(&value); if (retval) { com_err(__func__, retval, _("while writing attribute \"%s\" to inode %u"), name, ino); break; } } out: ext2fs_free_mem(&list); close_retval = ext2fs_xattrs_close(&handle); if (close_retval) { com_err(__func__, retval, _("while closing inode %u"), ino); retval = retval ? retval : close_retval; } return retval; return 0; }
errcode_t quota_write_inode(quota_ctx_t qctx, unsigned int qtype_bits) { int retval = 0; enum quota_type qtype; dict_t *dict; ext2_filsys fs; struct quota_handle *h = NULL; int fmt = QFMT_VFS_V1; if (!qctx) return 0; fs = qctx->fs; retval = ext2fs_get_mem(sizeof(struct quota_handle), &h); if (retval) { log_debug("Unable to allocate quota handle: %s", error_message(retval)); goto out; } retval = ext2fs_read_bitmaps(fs); if (retval) { log_debug("Couldn't read bitmaps: %s", error_message(retval)); goto out; } for (qtype = 0; qtype < MAXQUOTAS; qtype++) { if (((1 << qtype) & qtype_bits) == 0) continue; dict = qctx->quota_dict[qtype]; if (!dict) continue; retval = quota_file_create(h, fs, qtype, fmt); if (retval) { log_debug("Cannot initialize io on quotafile: %s", error_message(retval)); goto out; } write_dquots(dict, h); retval = quota_file_close(qctx, h); if (retval) { log_debug("Cannot finish IO on new quotafile: %s", strerror(errno)); if (h->qh_qf.e2_file) ext2fs_file_close(h->qh_qf.e2_file); (void) quota_inode_truncate(fs, h->qh_qf.ino); goto out; } /* Set quota inode numbers in superblock. */ quota_set_sb_inum(fs, h->qh_qf.ino, qtype); ext2fs_mark_super_dirty(fs); ext2fs_mark_bb_dirty(fs); fs->flags &= ~EXT2_FLAG_SUPER_ONLY; } retval = ext2fs_write_bitmaps(fs); if (retval) { log_debug("Couldn't write bitmaps: %s", error_message(retval)); goto out; } out: if (h) ext2fs_free_mem(&h); return retval; }
/* * Given a bad blocks bitmap, update the bad blocks inode to reflect * the map. */ errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list) { errcode_t retval; struct set_badblock_record rec; struct ext2_inode inode; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!fs->block_map) return EXT2_ET_NO_BLOCK_BITMAP; rec.bad_block_count = 0; rec.ind_blocks_size = rec.ind_blocks_ptr = 0; rec.max_ind_blocks = 10; retval = ext2fs_get_array(rec.max_ind_blocks, sizeof(blk_t), &rec.ind_blocks); if (retval) return retval; memset(rec.ind_blocks, 0, rec.max_ind_blocks * sizeof(blk_t)); retval = ext2fs_get_mem(fs->blocksize, &rec.block_buf); if (retval) goto cleanup; memset(rec.block_buf, 0, fs->blocksize); rec.err = 0; /* * First clear the old bad blocks (while saving the indirect blocks) */ retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, BLOCK_FLAG_DEPTH_TRAVERSE, 0, clear_bad_block_proc, &rec); if (retval) goto cleanup; if (rec.err) { retval = rec.err; goto cleanup; } /* * Now set the bad blocks! * * First, mark the bad blocks as used. This prevents a bad * block from being used as an indirecto block for the bad * block inode (!). */ if (bb_list) { retval = ext2fs_badblocks_list_iterate_begin(bb_list, &rec.bb_iter); if (retval) goto cleanup; retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, BLOCK_FLAG_APPEND, 0, set_bad_block_proc, &rec); ext2fs_badblocks_list_iterate_end(rec.bb_iter); if (retval) goto cleanup; if (rec.err) { retval = rec.err; goto cleanup; } } /* * Update the bad block inode's mod time and block count * field. */ retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode); if (retval) goto cleanup; inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0); if (!inode.i_ctime) inode.i_ctime = fs->now ? fs->now : time(0); ext2fs_iblk_set(fs, &inode, rec.bad_block_count); inode.i_size = rec.bad_block_count * fs->blocksize; retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode); if (retval) goto cleanup; cleanup: ext2fs_free_mem(&rec.ind_blocks); ext2fs_free_mem(&rec.block_buf); return retval; }
static errcode_t test_open(const char *name, int flags, io_channel *channel) { io_channel io = NULL; struct test_private_data *data = NULL; errcode_t retval; char *value; if (name == 0) return EXT2_ET_BAD_DEVICE_NAME; retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); if (retval) return retval; memset(io, 0, sizeof(struct struct_io_channel)); io->magic = EXT2_ET_MAGIC_IO_CHANNEL; retval = ext2fs_get_mem(sizeof(struct test_private_data), &data); if (retval) { retval = EXT2_ET_NO_MEMORY; goto cleanup; } io->manager = test_io_manager; retval = ext2fs_get_mem(strlen(name)+1, &io->name); if (retval) goto cleanup; strcpy(io->name, name); io->private_data = data; io->block_size = 1024; io->read_error = 0; io->write_error = 0; io->refcount = 1; memset(data, 0, sizeof(struct test_private_data)); data->magic = EXT2_ET_MAGIC_TEST_IO_CHANNEL; if (test_io_backing_manager) { retval = test_io_backing_manager->open(name, flags, &data->real); if (retval) goto cleanup; } else data->real = 0; data->read_blk = test_io_cb_read_blk; data->write_blk = test_io_cb_write_blk; data->set_blksize = test_io_cb_set_blksize; data->write_byte = test_io_cb_write_byte; data->outfile = NULL; if ((value = getenv("TEST_IO_LOGFILE")) != NULL) data->outfile = fopen_for_write(value); if (!data->outfile) data->outfile = stderr; data->flags = 0; if ((value = getenv("TEST_IO_FLAGS")) != NULL) data->flags = strtoul(value, NULL, 0); data->block = 0; if ((value = getenv("TEST_IO_BLOCK")) != NULL) data->block = strtoul(value, NULL, 0); data->read_abort_count = 0; if ((value = getenv("TEST_IO_READ_ABORT")) != NULL) data->read_abort_count = strtoul(value, NULL, 0); data->write_abort_count = 0; if ((value = getenv("TEST_IO_WRITE_ABORT")) != NULL) data->write_abort_count = strtoul(value, NULL, 0); *channel = io; return 0; cleanup: ext2fs_free_mem(&io); ext2fs_free_mem(&data); return retval; }
static int expand_dir_proc(ext2_filsys fs, blk64_t *blocknr, e2_blkcnt_t blockcnt, blk64_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data; blk64_t new_blk; char *block; errcode_t retval; if (*blocknr) { if (blockcnt >= 0) es->goal = *blocknr; return 0; } if (blockcnt && (EXT2FS_B2C(fs, es->goal) == EXT2FS_B2C(fs, es->goal+1))) new_blk = es->goal+1; else { es->goal &= ~EXT2FS_CLUSTER_MASK(fs); retval = ext2fs_new_block2(fs, es->goal, 0, &new_blk); if (retval) { es->err = retval; return BLOCK_ABORT; } es->newblocks++; } if (blockcnt > 0) { retval = ext2fs_new_dir_block(fs, 0, 0, &block); if (retval) { es->err = retval; return BLOCK_ABORT; } es->done = 1; retval = ext2fs_write_dir_block4(fs, new_blk, block, 0, es->dir); } else { retval = ext2fs_get_mem(fs->blocksize, &block); if (retval) { es->err = retval; return BLOCK_ABORT; } memset(block, 0, fs->blocksize); retval = io_channel_write_blk64(fs->io, new_blk, 1, block); } if (blockcnt >= 0) es->goal = new_blk; if (retval) { es->err = retval; return BLOCK_ABORT; } ext2fs_free_mem(&block); *blocknr = new_blk; ext2fs_block_alloc_stats2(fs, new_blk, +1); if (es->done) return (BLOCK_CHANGED | BLOCK_ABORT); else return BLOCK_CHANGED; }
/* * 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_open(const char *name, int flags, int superblock, int block_size, io_manager manager, ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; int i, j, groups_per_block; blk_t group_block; char *dest; struct ext2_group_desc *gdp; EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER); retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), (void **) &fs); if (retval) return retval; memset(fs, 0, sizeof(struct struct_ext2_filsys)); fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; fs->flags = flags; retval = manager->open(name, (flags & EXT2_FLAG_RW) ? IO_FLAG_RW : 0, &fs->io); if (retval) goto cleanup; fs->io->app_data = fs; retval = ext2fs_get_mem(strlen(name)+1, (void **) &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, (void **) &fs->super); if (retval) goto cleanup; if (flags & EXT2_FLAG_IMAGE_FILE) { retval = ext2fs_get_mem(sizeof(struct ext2_image_hdr), (void **) &fs->image_header); if (retval) goto cleanup; retval = io_channel_read_blk(fs->io, 0, -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 + 1; fs->orig_super = 0; } else { io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); superblock = 1; group_block = 0; retval = ext2fs_get_mem(SUPERBLOCK_SIZE, (void **) &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 EXT2FS_ENABLE_SWAPFS if ((fs->super->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) || (fs->flags & EXT2_FLAG_SWAP_BYTES)) { fs->flags |= EXT2_FLAG_SWAP_BYTES; ext2fs_swap_super(fs->super); } #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)) { if (fs->super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } if ((flags & EXT2_FLAG_RW) && (fs->super->s_feature_ro_compat & ~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; } } fs->blocksize = EXT2_BLOCK_SIZE(fs->super); if (fs->blocksize == 0) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->fragsize = EXT2_FRAG_SIZE(fs->super); fs->inode_blocks_per_group = ((fs->super->s_inodes_per_group * 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; } /* * Read group descriptors */ if ((EXT2_BLOCKS_PER_GROUP(fs->super)) == 0) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->group_desc_count = (fs->super->s_blocks_count - fs->super->s_first_data_block + EXT2_BLOCKS_PER_GROUP(fs->super) - 1) / EXT2_BLOCKS_PER_GROUP(fs->super); fs->desc_blocks = (fs->group_desc_count + EXT2_DESC_PER_BLOCK(fs->super) - 1) / EXT2_DESC_PER_BLOCK(fs->super); retval = ext2fs_get_mem(fs->desc_blocks * fs->blocksize, (void **) &fs->group_desc); if (retval) goto cleanup; if (!group_block) group_block = fs->super->s_first_data_block + 1; dest = (char *) fs->group_desc; for (i=0 ; i < fs->desc_blocks; i++) { retval = io_channel_read_blk(fs->io, group_block, 1, dest); if (retval) goto cleanup; group_block++; #ifdef EXT2FS_ENABLE_SWAPFS if (fs->flags & EXT2_FLAG_SWAP_BYTES) { gdp = (struct ext2_group_desc *) dest; groups_per_block = fs->blocksize / sizeof(struct ext2_group_desc); for (j=0; j < groups_per_block; j++) ext2fs_swap_group_desc(gdp++); } #endif dest += fs->blocksize; } *ret_fs = fs; return 0; cleanup: ext2fs_free(fs); return retval; }
errcode_t ext2fs_initialize(const char *name, int flags, struct ext2_super_block *param, io_manager manager, ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; struct ext2_super_block *super; int frags_per_block; unsigned int rem; unsigned int overhead = 0; blk_t group_block; unsigned int ipg; dgrp_t i; blk_t numblocks; int rsv_gdt; char *buf; if (!param || !param->s_blocks_count) return EXT2_ET_INVALID_ARGUMENT; 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 | EXT2_FLAG_RW; fs->umask = 022; #ifdef WORDS_BIGENDIAN fs->flags |= EXT2_FLAG_SWAP_BYTES; #endif retval = manager->open(name, IO_FLAG_RW, &fs->io); if (retval) goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super); if (retval) goto cleanup; fs->super = super; memset(super, 0, SUPERBLOCK_SIZE); #define set_field(field, default) (super->field = param->field ? \ param->field : (default)) super->s_magic = EXT2_SUPER_MAGIC; super->s_state = EXT2_VALID_FS; set_field(s_log_block_size, 0); /* default blocksize: 1024 bytes */ set_field(s_log_frag_size, 0); /* default fragsize: 1024 bytes */ set_field(s_first_data_block, super->s_log_block_size ? 0 : 1); set_field(s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT); set_field(s_errors, EXT2_ERRORS_DEFAULT); set_field(s_feature_compat, 0); set_field(s_feature_incompat, 0); set_field(s_feature_ro_compat, 0); set_field(s_first_meta_bg, 0); if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { retval = EXT2_ET_RO_UNSUPP_FEATURE; goto cleanup; } set_field(s_rev_level, EXT2_GOOD_OLD_REV); if (super->s_rev_level >= EXT2_DYNAMIC_REV) { set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO); set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE); } set_field(s_checkinterval, EXT2_DFL_CHECKINTERVAL); super->s_mkfs_time = super->s_lastcheck = time(NULL); super->s_creator_os = CREATOR_OS; fs->blocksize = EXT2_BLOCK_SIZE(super); fs->fragsize = EXT2_FRAG_SIZE(super); frags_per_block = fs->blocksize / fs->fragsize; /* default: (fs->blocksize*8) blocks/group, up to 2^16 (GDT limit) */ set_field(s_blocks_per_group, fs->blocksize * 8); if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super)) super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super); super->s_frags_per_group = super->s_blocks_per_group * frags_per_block; super->s_blocks_count = param->s_blocks_count; super->s_r_blocks_count = param->s_r_blocks_count; if (super->s_r_blocks_count >= param->s_blocks_count) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } /* * If we're creating an external journal device, we don't need * to bother with the rest. */ if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { fs->group_desc_count = 0; ext2fs_mark_super_dirty(fs); *ret_fs = fs; return 0; } retry: fs->group_desc_count = (super->s_blocks_count - super->s_first_data_block + EXT2_BLOCKS_PER_GROUP(super) - 1) / EXT2_BLOCKS_PER_GROUP(super); if (fs->group_desc_count == 0) { retval = EXT2_ET_TOOSMALL; goto cleanup; } fs->desc_blocks = (fs->group_desc_count + EXT2_DESC_PER_BLOCK(super) - 1) / EXT2_DESC_PER_BLOCK(super); i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize; set_field(s_inodes_count, super->s_blocks_count / i); /* * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so * that we have enough inodes for the filesystem(!) */ if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1) super->s_inodes_count = EXT2_FIRST_INODE(super)+1; /* * There should be at least as many inodes as the user * requested. Figure out how many inodes per group that * should be. But make sure that we don't allocate more than * one bitmap's worth of inodes each group. */ ipg = (super->s_inodes_count + fs->group_desc_count - 1) / fs->group_desc_count; if (ipg > fs->blocksize * 8) { if (super->s_blocks_per_group >= 256) { /* Try again with slightly different parameters */ super->s_blocks_per_group -= 8; super->s_blocks_count = param->s_blocks_count; super->s_frags_per_group = super->s_blocks_per_group * frags_per_block; goto retry; } else return EXT2_ET_TOO_MANY_INODES; } if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super)) ipg = EXT2_MAX_INODES_PER_GROUP(super); super->s_inodes_per_group = ipg; if (super->s_inodes_count > ipg * fs->group_desc_count) super->s_inodes_count = ipg * fs->group_desc_count; /* * Make sure the number of inodes per group completely fills * the inode table blocks in the descriptor. If not, add some * additional inodes/group. Waste not, want not... */ fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); super->s_inodes_per_group = ((fs->inode_blocks_per_group * EXT2_BLOCK_SIZE(super)) / EXT2_INODE_SIZE(super)); /* * Finally, make sure the number of inodes per group is a * multiple of 8. This is needed to simplify the bitmap * splicing code. */ super->s_inodes_per_group &= ~7; fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); /* * adjust inode count to reflect the adjusted inodes_per_group */ super->s_inodes_count = super->s_inodes_per_group * fs->group_desc_count; super->s_free_inodes_count = super->s_inodes_count; /* * check the number of reserved group descriptor table blocks */ if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) rsv_gdt = calc_reserved_gdt_blocks(fs); else rsv_gdt = 0; set_field(s_reserved_gdt_blocks, rsv_gdt); if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) { retval = EXT2_ET_RES_GDT_BLOCKS; goto cleanup; } /* * Overhead is the number of bookkeeping blocks per group. It * includes the superblock backup, the group descriptor * backups, the inode bitmap, the block bitmap, and the inode * table. */ overhead = (int) (2 + fs->inode_blocks_per_group); if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1)) overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks; /* This can only happen if the user requested too many inodes */ if (overhead > super->s_blocks_per_group) return EXT2_ET_TOO_MANY_INODES; /* * See if the last group is big enough to support the * necessary data structures. If not, we need to get rid of * it. */ rem = ((super->s_blocks_count - super->s_first_data_block) % super->s_blocks_per_group); if ((fs->group_desc_count == 1) && rem && (rem < overhead)) return EXT2_ET_TOOSMALL; if (rem && (rem < overhead+50)) { super->s_blocks_count -= rem; goto retry; } /* * At this point we know how big the filesystem will be. So * we can do any and all allocations that depend on the block * count. */ retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); if (retval) goto cleanup; sprintf(buf, "block bitmap for %s", fs->device_name); retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); if (retval) goto cleanup; sprintf(buf, "inode bitmap for %s", fs->device_name); retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); if (retval) goto cleanup; ext2fs_free_mem(&buf); retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize, &fs->group_desc); if (retval) goto cleanup; memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize); /* * Reserve the superblock and group descriptors for each * group, and fill in the correct group statistics for group. * Note that although the block bitmap, inode bitmap, and * inode table have not been allocated (and in fact won't be * by this routine), they are accounted for nevertheless. */ group_block = super->s_first_data_block; super->s_free_blocks_count = 0; for (i = 0; i < fs->group_desc_count; i++) { numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map); super->s_free_blocks_count += numblocks; fs->group_desc[i].bg_free_blocks_count = numblocks; fs->group_desc[i].bg_free_inodes_count = fs->super->s_inodes_per_group; fs->group_desc[i].bg_used_dirs_count = 0; group_block += super->s_blocks_per_group; } ext2fs_mark_super_dirty(fs); ext2fs_mark_bb_dirty(fs); ext2fs_mark_ib_dirty(fs); io_channel_set_blksize(fs->io, fs->blocksize); *ret_fs = fs; return 0; cleanup: ext2fs_free(fs); 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 = (int) EXT2_BLOCKS_PER_GROUP(fs->super) / 8; int inode_nbytes = (int) EXT2_INODES_PER_GROUP(fs->super) / 8; blk_t blk; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); fs->write_bitmaps = ext2fs_write_bitmaps; retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); if (retval) return retval; if (do_block) { ext2fs_free_block_bitmap(fs->block_map); sprintf(buf, "block bitmap for %s", fs->device_name); retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); if (retval) goto cleanup; block_bitmap = fs->block_map->bitmap; } if (do_inode) { ext2fs_free_inode_bitmap(fs->inode_map); sprintf(buf, "inode bitmap for %s", fs->device_name); retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); if (retval) goto cleanup; inode_bitmap = fs->inode_map->bitmap; } ext2fs_free_mem(&buf); if (fs->flags & EXT2_FLAG_IMAGE_FILE) { if (inode_bitmap) { blk = (fs->image_header->offset_inodemap / fs->blocksize); retval = io_channel_read_blk(fs->image_io, blk, -(inode_nbytes * fs->group_desc_count), inode_bitmap); if (retval) goto cleanup; } if (block_bitmap) { blk = (fs->image_header->offset_blockmap / fs->blocksize); retval = io_channel_read_blk(fs->image_io, blk, -(block_nbytes * fs->group_desc_count), block_bitmap); if (retval) goto cleanup; } return 0; } for (i = 0; i < fs->group_desc_count; i++) { if (block_bitmap) { blk = fs->group_desc[i].bg_block_bitmap; if (blk) { retval = io_channel_read_blk(fs->io, blk, -block_nbytes, block_bitmap); if (retval) { retval = EXT2_ET_BLOCK_BITMAP_READ; goto cleanup; } #ifdef EXT2_BIG_ENDIAN_BITMAPS if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) || (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))) ext2fs_swap_bitmap(fs, block_bitmap, block_nbytes); #endif } else memset(block_bitmap, 0, block_nbytes); block_bitmap += block_nbytes; } if (inode_bitmap) { blk = fs->group_desc[i].bg_inode_bitmap; if (blk) { retval = io_channel_read_blk(fs->io, blk, -inode_nbytes, inode_bitmap); if (retval) { retval = EXT2_ET_INODE_BITMAP_READ; goto cleanup; } #ifdef EXT2_BIG_ENDIAN_BITMAPS if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) || (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))) ext2fs_swap_bitmap(fs, inode_bitmap, inode_nbytes); #endif } else memset(inode_bitmap, 0, inode_nbytes); inode_bitmap += inode_nbytes; } } return 0; cleanup: if (do_block) { ext2fs_free_mem(&fs->block_map); } if (do_inode) { ext2fs_free_mem(&fs->inode_map); } ext2fs_free_mem(&buf); return retval; }
errcode_t ext2fs_flush(ext2_filsys fs) { dgrp_t i; errcode_t retval; unsigned long fs_state; __u32 feature_incompat; struct ext2_super_block *super_shadow = 0; struct ext2_group_desc *group_shadow = 0; #ifdef WORDS_BIGENDIAN struct ext2_group_desc *s, *t; dgrp_t j; #endif char *group_ptr; int old_desc_blocks; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); fs_state = fs->super->s_state; feature_incompat = fs->super->s_feature_incompat; fs->super->s_wtime = fs->now ? fs->now : time(NULL); fs->super->s_block_group_nr = 0; #ifdef WORDS_BIGENDIAN retval = EXT2_ET_NO_MEMORY; retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow); if (retval) goto errout; retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, &group_shadow); if (retval) goto errout; memset(group_shadow, 0, (size_t) fs->blocksize * fs->desc_blocks); /* swap the group descriptors */ for (j=0, s=fs->group_desc, t=group_shadow; j < fs->group_desc_count; j++, t++, s++) { *t = *s; ext2fs_swap_group_desc(t); } #else super_shadow = fs->super; group_shadow = fs->group_desc; #endif /* * Set the state of the FS to be non-valid. (The state has * already been backed up earlier, and will be restored after * we write out the backup superblocks.) */ fs->super->s_state &= ~EXT2_VALID_FS; fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER; #ifdef WORDS_BIGENDIAN *super_shadow = *fs->super; ext2fs_swap_super(super_shadow); #endif /* * If this is an external journal device, don't write out the * block group descriptors or any of the backup superblocks */ if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) goto write_primary_superblock_only; /* * Write out the master group descriptors, and the backup * superblocks and group descriptors. */ group_ptr = (char *) group_shadow; if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) old_desc_blocks = fs->super->s_first_meta_bg; else old_desc_blocks = fs->desc_blocks; for (i = 0; i < fs->group_desc_count; i++) { blk_t super_blk, old_desc_blk, new_desc_blk; int meta_bg; ext2fs_super_and_bgd_loc(fs, i, &super_blk, &old_desc_blk, &new_desc_blk, &meta_bg); if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) &&i && super_blk) { retval = write_backup_super(fs, i, super_blk, super_shadow); if (retval) goto errout; } if (fs->flags & EXT2_FLAG_SUPER_ONLY) continue; if ((old_desc_blk) && (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) || (i == 0))) { retval = io_channel_write_blk(fs->io, old_desc_blk, old_desc_blocks, group_ptr); if (retval) goto errout; } if (new_desc_blk) { retval = io_channel_write_blk(fs->io, new_desc_blk, 1, group_ptr + (meta_bg*fs->blocksize)); if (retval) goto errout; } } /* * If the write_bitmaps() function is present, call it to * flush the bitmaps. This is done this way so that a simple * program that doesn't mess with the bitmaps doesn't need to * drag in the bitmaps.c code. */ if (fs->write_bitmaps) { retval = fs->write_bitmaps(fs); if (retval) goto errout; } write_primary_superblock_only: /* * Write out master superblock. This has to be done * separately, since it is located at a fixed location * (SUPERBLOCK_OFFSET). We flush all other pending changes * out to disk first, just to avoid a race condition with an * insy-tinsy window.... */ fs->super->s_block_group_nr = 0; fs->super->s_state = fs_state; fs->super->s_feature_incompat = feature_incompat; #ifdef WORDS_BIGENDIAN *super_shadow = *fs->super; ext2fs_swap_super(super_shadow); #endif retval = io_channel_flush(fs->io); retval = write_primary_superblock(fs, super_shadow); if (retval) goto errout; fs->flags &= ~EXT2_FLAG_DIRTY; retval = io_channel_flush(fs->io); errout: fs->super->s_state = fs_state; #ifdef WORDS_BIGENDIAN if (super_shadow) ext2fs_free_mem(&super_shadow); if (group_shadow) ext2fs_free_mem(&group_shadow); #endif 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 * 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_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; unsigned 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 = 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); #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; } if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_BIGALLOC) && (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; } 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 (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; if (group_block == 0 && fs->blocksize == 1024) group_block = 1; /* Deal with 1024 blocksize && bigalloc */ 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++) { 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 && 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); /* 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); } fs->flags &= ~EXT2_FLAG_NOFREE_ON_ERROR; *ret_fs = fs; if ((fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_MMP) && !(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; } } return 0; cleanup: if (flags & EXT2_FLAG_NOFREE_ON_ERROR) *ret_fs = fs; else ext2fs_free(fs); return retval; }
errcode_t ext2fs_extent_get(ext2_extent_handle_t handle, int flags, struct ext2fs_extent *extent) { struct extent_path *path, *newpath; struct ext3_extent_header *eh; struct ext3_extent_idx *ix = 0; struct ext3_extent *ex; errcode_t retval; blk_t blk; blk64_t end_blk; int orig_op, op; EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); if (!handle->path) return EXT2_ET_NO_CURRENT_NODE; orig_op = op = flags & EXT2_EXTENT_MOVE_MASK; retry: path = handle->path + handle->level; if ((orig_op == EXT2_EXTENT_NEXT) || (orig_op == EXT2_EXTENT_NEXT_LEAF)) { if (handle->level < handle->max_depth) { if (path->visit_num == 0) { path->visit_num++; op = EXT2_EXTENT_DOWN; } else if (path->left > 0) op = EXT2_EXTENT_NEXT_SIB; else if (handle->level > 0) op = EXT2_EXTENT_UP; else return EXT2_ET_EXTENT_NO_NEXT; } else { if (path->left > 0) op = EXT2_EXTENT_NEXT_SIB; else if (handle->level > 0) op = EXT2_EXTENT_UP; else return EXT2_ET_EXTENT_NO_NEXT; } if (op != EXT2_EXTENT_NEXT_SIB) { #ifdef DEBUG_GET_EXTENT printf("<<<< OP = %s\n", (op == EXT2_EXTENT_DOWN) ? "down" : ((op == EXT2_EXTENT_UP) ? "up" : "unknown")); #endif } } if ((orig_op == EXT2_EXTENT_PREV) || (orig_op == EXT2_EXTENT_PREV_LEAF)) { if (handle->level < handle->max_depth) { if (path->visit_num > 0 ) { op = EXT2_EXTENT_DOWN_AND_LAST; } else if (path->left < path->entries-1) op = EXT2_EXTENT_PREV_SIB; else if (handle->level > 0) op = EXT2_EXTENT_UP; else return EXT2_ET_EXTENT_NO_PREV; } else { if (path->left < path->entries-1) op = EXT2_EXTENT_PREV_SIB; else if (handle->level > 0) op = EXT2_EXTENT_UP; else return EXT2_ET_EXTENT_NO_PREV; } if (op != EXT2_EXTENT_PREV_SIB) { #ifdef DEBUG_GET_EXTENT printf("<<<< OP = %s\n", (op == EXT2_EXTENT_DOWN_AND_LAST) ? "down/last" : ((op == EXT2_EXTENT_UP) ? "up" : "unknown")); #endif } } if (orig_op == EXT2_EXTENT_LAST_LEAF) { if ((handle->level < handle->max_depth) && (path->left == 0)) op = EXT2_EXTENT_DOWN; else op = EXT2_EXTENT_LAST_SIB; #ifdef DEBUG_GET_EXTENT printf("<<<< OP = %s\n", (op == EXT2_EXTENT_DOWN) ? "down" : "last_sib"); #endif } switch (op) { case EXT2_EXTENT_CURRENT: ix = path->curr; break; case EXT2_EXTENT_ROOT: handle->level = 0; path = handle->path + handle->level; case EXT2_EXTENT_FIRST_SIB: path->left = path->entries; path->curr = 0; case EXT2_EXTENT_NEXT_SIB: if (path->left <= 0) return EXT2_ET_EXTENT_NO_NEXT; if (path->curr) { ix = path->curr; ix++; } else { eh = (struct ext3_extent_header *) path->buf; ix = EXT_FIRST_INDEX(eh); } path->left--; path->curr = ix; path->visit_num = 0; break; case EXT2_EXTENT_PREV_SIB: if (!path->curr || path->left+1 >= path->entries) return EXT2_ET_EXTENT_NO_PREV; ix = path->curr; ix--; path->curr = ix; path->left++; if (handle->level < handle->max_depth) path->visit_num = 1; break; case EXT2_EXTENT_LAST_SIB: eh = (struct ext3_extent_header *) path->buf; path->curr = EXT_LAST_EXTENT(eh); ix = path->curr; path->left = 0; path->visit_num = 0; break; case EXT2_EXTENT_UP: if (handle->level <= 0) return EXT2_ET_EXTENT_NO_UP; handle->level--; path--; ix = path->curr; if ((orig_op == EXT2_EXTENT_PREV) || (orig_op == EXT2_EXTENT_PREV_LEAF)) path->visit_num = 0; break; case EXT2_EXTENT_DOWN: case EXT2_EXTENT_DOWN_AND_LAST: if (!path->curr ||(handle->level >= handle->max_depth)) return EXT2_ET_EXTENT_NO_DOWN; ix = path->curr; newpath = path + 1; if (!newpath->buf) { retval = ext2fs_get_mem(handle->fs->blocksize, &newpath->buf); if (retval) return retval; } blk = ext2fs_le32_to_cpu(ix->ei_leaf) + ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); if ((handle->fs->flags & EXT2_FLAG_IMAGE_FILE) && (handle->fs->io != handle->fs->image_io)) memset(newpath->buf, 0, handle->fs->blocksize); else { retval = io_channel_read_blk(handle->fs->io, blk, 1, newpath->buf); if (retval) return retval; } handle->level++; eh = (struct ext3_extent_header *) newpath->buf; retval = ext2fs_extent_header_verify(eh, handle->fs->blocksize); if (retval) { handle->level--; return retval; } newpath->left = newpath->entries = ext2fs_le16_to_cpu(eh->eh_entries); newpath->max_entries = ext2fs_le16_to_cpu(eh->eh_max); if (path->left > 0) { ix++; newpath->end_blk = ext2fs_le32_to_cpu(ix->ei_block); } else newpath->end_blk = path->end_blk; path = newpath; if (op == EXT2_EXTENT_DOWN) { ix = EXT_FIRST_INDEX((struct ext3_extent_header *) eh); path->curr = ix; path->left = path->entries - 1; path->visit_num = 0; } else { ix = EXT_LAST_INDEX((struct ext3_extent_header *) eh); path->curr = ix; path->left = 0; if (handle->level < handle->max_depth) path->visit_num = 1; } #ifdef DEBUG_GET_EXTENT printf("Down to level %d/%d, end_blk=%llu\n", handle->level, handle->max_depth, path->end_blk); #endif break; default: return EXT2_ET_OP_NOT_SUPPORTED; } if (!ix) return EXT2_ET_NO_CURRENT_NODE; extent->e_flags = 0; #ifdef DEBUG_GET_EXTENT printf("(Left %d)\n", path->left); #endif if (handle->level == handle->max_depth) { ex = (struct ext3_extent *) ix; extent->e_pblk = ext2fs_le32_to_cpu(ex->ee_start) + ((__u64) ext2fs_le16_to_cpu(ex->ee_start_hi) << 32); extent->e_lblk = ext2fs_le32_to_cpu(ex->ee_block); extent->e_len = ext2fs_le16_to_cpu(ex->ee_len); extent->e_flags |= EXT2_EXTENT_FLAGS_LEAF; if (extent->e_len > EXT_INIT_MAX_LEN) { extent->e_len -= EXT_INIT_MAX_LEN; extent->e_flags |= EXT2_EXTENT_FLAGS_UNINIT; } } else { extent->e_pblk = ext2fs_le32_to_cpu(ix->ei_leaf) + ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); extent->e_lblk = ext2fs_le32_to_cpu(ix->ei_block); if (path->left > 0) { ix++; end_blk = ext2fs_le32_to_cpu(ix->ei_block); } else end_blk = path->end_blk; extent->e_len = end_blk - extent->e_lblk; } if (path->visit_num) extent->e_flags |= EXT2_EXTENT_FLAGS_SECOND_VISIT; if (((orig_op == EXT2_EXTENT_NEXT_LEAF) || (orig_op == EXT2_EXTENT_PREV_LEAF)) && (handle->level != handle->max_depth)) goto retry; if ((orig_op == EXT2_EXTENT_LAST_LEAF) && ((handle->level != handle->max_depth) || (path->left != 0))) goto retry; return 0; }
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 > (int) fs->blocksize) || (inode_nbytes > (int) 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 = EXT2_GROUPS_TO_CLUSTERS(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; } } /* 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; }