struct ext2_qcow2_hdr *qcow2_read_header(int fd) { void *buffer = NULL; struct ext2_qcow2_hdr *hdr = NULL; size_t size; errcode_t ret; ret = ext2fs_get_mem(sizeof(struct ext2_qcow2_hdr), &buffer); if (ret) return NULL; memset(buffer, 0, sizeof(struct ext2_qcow2_hdr)); if (ext2fs_llseek(fd, 0, SEEK_SET < 0)) return NULL; size = read(fd, buffer, sizeof(struct ext2_qcow2_hdr)); if (size != sizeof(struct ext2_qcow2_hdr)) { ext2fs_free_mem(&buffer); return NULL; } hdr = (struct ext2_qcow2_hdr *)(buffer); if ((ext2fs_be32_to_cpu(hdr->magic) != QCOW_MAGIC) || (ext2fs_be32_to_cpu(hdr->version) != 2)) { ext2fs_free_mem(&hdr); return NULL; } return hdr; }
static errcode_t raw_read_blk(io_channel channel, struct unix_private_data *data, unsigned long long block, int count, void *buf) { errcode_t retval; ssize_t size; ext2_loff_t location; int actual = 0; size = (count < 0) ? -count : count * channel->block_size; data->io_stats.bytes_read += size; location = ((ext2_loff_t) block * channel->block_size) + data->offset; if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; goto error_out; } actual = read(data->dev, buf, size); if (actual != size) { if (actual < 0) actual = 0; retval = EXT2_ET_SHORT_READ; goto error_out; } return 0; error_out: memset((char *) buf+actual, 0, size-actual); if (channel->read_error) retval = (channel->read_error)(channel, block, count, buf, size, actual, retval); return retval; }
errcode_t ext2fs_mmp_read(ext2_filsys fs, blk64_t mmp_blk, void *buf) { struct mmp_struct *mmp_cmp; errcode_t retval = 0; if ((mmp_blk <= fs->super->s_first_data_block) || (mmp_blk >= fs->super->s_blocks_count)) return EXT2_ET_MMP_BAD_BLOCK; /* ext2fs_open() reserves fd0,1,2 to avoid stdio collision, so checking * mmp_fd <= 0 is OK to validate that the fd is valid. This opens its * own fd to read the MMP block to ensure that it is using O_DIRECT, * regardless of how the io_manager is doing reads, to avoid caching of * the MMP block by the io_manager or the VM. It needs to be fresh. */ if (fs->mmp_fd <= 0) { /* fs->mmp_fd = open(fs->device_name, O_RDWR | O_DIRECT); if (fs->mmp_fd < 0) { retval = EXT2_ET_MMP_OPEN_DIRECT; goto out; } */ } if (fs->mmp_cmp == NULL) { int align = ext2fs_get_dio_alignment(fs->mmp_fd); retval = ext2fs_get_memalign(fs->blocksize, align, &fs->mmp_cmp); if (retval) return retval; } if (ext2fs_llseek(fs->mmp_fd, mmp_blk * fs->blocksize, SEEK_SET) != mmp_blk * fs->blocksize) { retval = EXT2_ET_LLSEEK_FAILED; goto out; } if (read(fs->mmp_fd, fs->mmp_cmp, fs->blocksize) != fs->blocksize) { retval = EXT2_ET_SHORT_READ; goto out; } mmp_cmp = fs->mmp_cmp; #ifdef WORDS_BIGENDIAN ext2fs_swap_mmp(mmp_cmp); #endif if (buf != NULL && buf != fs->mmp_cmp) memcpy(buf, fs->mmp_cmp, fs->blocksize); if (mmp_cmp->mmp_magic != EXT4_MMP_MAGIC) { retval = EXT2_ET_MMP_MAGIC_INVALID; goto out; } out: return retval; }
static int qcow2_read_l1_table(struct ext2_qcow2_image *img) { int fd = img->fd; size_t size, l1_size = img->l1_size * sizeof(blk64_t); blk64_t *table; errcode_t ret; ret = ext2fs_get_memzero(l1_size, &table); if (ret) return ret; if (ext2fs_llseek(fd, img->l1_offset, SEEK_SET) < 0) { ext2fs_free_mem(&table); return errno; } size = read(fd, table, l1_size); if (size != l1_size) { ext2fs_free_mem(&table); return errno; } img->l1_table = table; return 0; }
/* * Here are the raw I/O functions */ static errcode_t raw_read_blk(io_channel channel, struct unix_private_data *data, unsigned long long block, int count, void *buf) { errcode_t retval; ssize_t size; ext2_loff_t location; int actual = 0; size = (count < 0) ? -count : count * channel->block_size; data->io_stats.bytes_read += size; location = ((ext2_loff_t) block * channel->block_size) + data->offset; if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; goto error_out; } if ((data->align == 0) || ((IS_ALIGNED(buf, data->align)) && IS_ALIGNED(size, data->align))) { actual = read(data->dev, buf, size); if (actual != size) { short_read: if (actual < 0) actual = 0; retval = EXT2_ET_SHORT_READ; goto error_out; } return 0; } #ifdef ALIGN_DEBUG printf("raw_read_blk: O_DIRECT fallback: %p %lu\n", buf, (unsigned long) size); #endif /* * The buffer or size which we're trying to read isn't aligned * to the O_DIRECT rules, so we need to do this the hard way... */ while (size > 0) { actual = read(data->dev, data->bounce, channel->block_size); if (actual != channel->block_size) goto short_read; actual = size; if (size > channel->block_size) actual = channel->block_size; memcpy(buf, data->bounce, actual); size -= actual; buf += actual; } return 0; error_out: memset((char *) buf+actual, 0, size-actual); if (channel->read_error) retval = (channel->read_error)(channel, block, count, buf, size, actual, retval); return retval; }
static int valid_offset (int fd, ext2_loff_t offset) { char ch; if (ext2fs_llseek (fd, offset, 0) < 0) return 0; if (read (fd, &ch, 1) < 1) return 0; return 1; }
/* * Windows and FreeBSD block devices only allow sector alignment IO in offset and size */ static errcode_t raw_read_blk(io_channel channel, struct unix_private_data *data, unsigned long block, int count, void *buf) { errcode_t retval; size_t size, alignsize, fragment; ext2_loff_t location; int total = 0, actual; #define BLOCKALIGN 512 char sector[BLOCKALIGN]; size = (count < 0) ? -count : count * channel->block_size; data->io_stats.bytes_read += size; location = ((ext2_loff_t) block * channel->block_size) + data->offset; #ifdef DEBUG printf("count=%d, size=%d, block=%lu, blk_size=%d, location=%llx\n", count, size, block, channel->block_size, (long long)location); #endif if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; goto error_out; } fragment = size % BLOCKALIGN; alignsize = size - fragment; if (alignsize) { actual = read(data->dev, buf, alignsize); if (actual != alignsize) goto short_read; } if (fragment) { actual = read(data->dev, sector, BLOCKALIGN); if (actual != BLOCKALIGN) goto short_read; memcpy(buf+alignsize, sector, fragment); } return 0; short_read: if (actual>0) total += actual; retval = EXT2_ET_SHORT_READ; error_out: memset((char *) buf+total, 0, size-actual); if (channel->read_error) retval = (channel->read_error)(channel, block, count, buf, size, actual, retval); return retval; }
static int qcow2_copy_data(int fdin, int fdout, ext2_off64_t off_in, ext2_off64_t off_out, void *buf, size_t count) { size_t size; assert(buf); if (ext2fs_llseek(fdout, off_out, SEEK_SET) < 0) return errno; if (ext2fs_llseek(fdin, off_in, SEEK_SET) < 0) return errno; size = read(fdin, buf, count); if (size != count) return errno; size = write(fdout, buf, count); if (size != count) return errno; return 0; }
static int qcow2_read_l2_table(struct ext2_qcow2_image *img, ext2_off64_t offset, blk64_t **l2_table) { int fd = img->fd; size_t size; assert(*l2_table); if (ext2fs_llseek(fd, offset, SEEK_SET) < 0) return errno; size = read(fd, *l2_table, img->cluster_size); if (size != img->cluster_size) return errno; return 0; }
static errcode_t raw_write_blk(io_channel channel, struct unix_private_data *data, unsigned long long block, int count, const void *buf) { ssize_t size; ext2_loff_t location; int actual = 0; errcode_t retval; if (count == 1) size = channel->block_size; else { if (count < 0) size = -count; else size = count * channel->block_size; } data->io_stats.bytes_written += size; location = ((ext2_loff_t) block * channel->block_size) + data->offset; if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; goto error_out; } actual = write(data->dev, buf, size); if (actual != size) { retval = EXT2_ET_SHORT_WRITE; goto error_out; } return 0; error_out: if (channel->write_error) retval = (channel->write_error)(channel, block, count, buf, size, actual, retval); return retval; }
int main (int argc, char ** argv) { errcode_t retval; ext2_filsys fs; int c; int flags = 0; int flush = 0; int force = 0; int io_flags = 0; int force_min_size = 0; int print_min_size = 0; int fd, ret; blk64_t new_size = 0; blk64_t max_size = 0; blk64_t min_size = 0; io_manager io_ptr; char *new_size_str = 0; int use_stride = -1; ext2fs_struct_stat st_buf; __s64 new_file_size; unsigned int sys_page_size = 4096; long sysval; int len, mount_flags; char *mtpt; #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); fprintf (stderr, "resize2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE); if (argc && *argv) program_name = *argv; while ((c = getopt (argc, argv, "d:fFhMPpS:")) != EOF) { switch (c) { case 'h': usage(program_name); break; case 'f': force = 1; break; case 'F': flush = 1; break; case 'M': force_min_size = 1; break; case 'P': print_min_size = 1; break; case 'd': flags |= atoi(optarg); break; case 'p': flags |= RESIZE_PERCENT_COMPLETE; break; case 'S': use_stride = atoi(optarg); break; default: usage(program_name); } } if (optind == argc) usage(program_name); device_name = argv[optind++]; if (optind < argc) new_size_str = argv[optind++]; if (optind < argc) usage(program_name); io_options = strchr(device_name, '?'); if (io_options) *io_options++ = 0; /* * Figure out whether or not the device is mounted, and if it is * where it is mounted. */ len=80; while (1) { mtpt = malloc(len); if (!mtpt) return ENOMEM; mtpt[len-1] = 0; retval = ext2fs_check_mount_point(device_name, &mount_flags, mtpt, len); if (retval) { com_err("ext2fs_check_mount_point", retval, _("while determining whether %s is mounted."), device_name); exit(1); } if (!(mount_flags & EXT2_MF_MOUNTED) || (mtpt[len-1] == 0)) break; free(mtpt); len = 2 * len; } fd = ext2fs_open_file(device_name, O_RDWR, 0); if (fd < 0) { com_err("open", errno, _("while opening %s"), device_name); exit(1); } ret = ext2fs_fstat(fd, &st_buf); if (ret < 0) { com_err("open", errno, _("while getting stat information for %s"), device_name); exit(1); } if (flush) { retval = ext2fs_sync_device(fd, 1); if (retval) { com_err(argv[0], retval, _("while trying to flush %s"), device_name); exit(1); } } if (!S_ISREG(st_buf.st_mode )) { close(fd); fd = -1; } #ifdef CONFIG_TESTIO_DEBUG if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) { io_ptr = test_io_manager; test_io_backing_manager = unix_io_manager; } else #endif io_ptr = unix_io_manager; if (!(mount_flags & EXT2_MF_MOUNTED)) io_flags = EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE; io_flags |= EXT2_FLAG_64BITS; retval = ext2fs_open2(device_name, io_options, io_flags, 0, 0, io_ptr, &fs); if (retval) { com_err (program_name, retval, _("while trying to open %s"), device_name); printf (_("Couldn't find valid filesystem superblock.\n")); exit (1); } /* * Check for compatibility with the feature sets. We need to * be more stringent than ext2fs_open(). */ if (fs->super->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) { com_err(program_name, EXT2_ET_UNSUPP_FEATURE, "(%s)", device_name); exit(1); } min_size = calculate_minimum_resize_size(fs); if (print_min_size) { if (!force && ((fs->super->s_state & EXT2_ERROR_FS) || ((fs->super->s_state & EXT2_VALID_FS) == 0))) { fprintf(stderr, _("Please run 'e2fsck -f %s' first.\n\n"), device_name); exit(1); } printf(_("Estimated minimum size of the filesystem: %llu\n"), min_size); exit(0); } /* Determine the system page size if possible */ #ifdef HAVE_SYSCONF #if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE)) #define _SC_PAGESIZE _SC_PAGE_SIZE #endif #ifdef _SC_PAGESIZE sysval = sysconf(_SC_PAGESIZE); if (sysval > 0) sys_page_size = sysval; #endif /* _SC_PAGESIZE */ #endif /* HAVE_SYSCONF */ /* * Get the size of the containing partition, and use this for * defaults and for making sure the new filesystem doesn't * exceed the partition size. */ retval = ext2fs_get_device_size2(device_name, fs->blocksize, &max_size); if (retval) { com_err(program_name, retval, _("while trying to determine filesystem size")); exit(1); } if (force_min_size) new_size = min_size; else if (new_size_str) { new_size = parse_num_blocks2(new_size_str, fs->super->s_log_block_size); if (new_size == 0) { com_err(program_name, 0, _("Invalid new size: %s\n"), new_size_str); exit(1); } } else { new_size = max_size; /* Round down to an even multiple of a pagesize */ if (sys_page_size > fs->blocksize) new_size &= ~((sys_page_size / fs->blocksize)-1); } if (!EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT4_FEATURE_INCOMPAT_64BIT)) { /* Take 16T down to 2^32-1 blocks */ if (new_size == (1ULL << 32)) new_size--; else if (new_size > (1ULL << 32)) { com_err(program_name, 0, _("New size too large to be " "expressed in 32 bits\n")); exit(1); } } if (!force && new_size < min_size) { com_err(program_name, 0, _("New size smaller than minimum (%llu)\n"), min_size); exit(1); } if (use_stride >= 0) { if (use_stride >= (int) fs->super->s_blocks_per_group) { com_err(program_name, 0, _("Invalid stride length")); exit(1); } fs->stride = fs->super->s_raid_stride = use_stride; ext2fs_mark_super_dirty(fs); } else determine_fs_stride(fs); /* * If we are resizing a plain file, and it's not big enough, * automatically extend it in a sparse fashion by writing the * last requested block. */ new_file_size = ((__u64) new_size) * fs->blocksize; if ((__u64) new_file_size > (((__u64) 1) << (sizeof(st_buf.st_size)*8 - 1)) - 1) fd = -1; if ((new_file_size > st_buf.st_size) && (fd > 0)) { if ((ext2fs_llseek(fd, new_file_size-1, SEEK_SET) >= 0) && (write(fd, "0", 1) == 1)) max_size = new_size; } if (!force && (new_size > max_size)) { fprintf(stderr, _("The containing partition (or device)" " is only %llu (%dk) blocks.\nYou requested a new size" " of %llu blocks.\n\n"), max_size, fs->blocksize / 1024, new_size); exit(1); } if (new_size == ext2fs_blocks_count(fs->super)) { fprintf(stderr, _("The filesystem is already %llu blocks " "long. Nothing to do!\n\n"), new_size); exit(0); } if (mount_flags & EXT2_MF_MOUNTED) { retval = online_resize_fs(fs, mtpt, &new_size, flags); } else { if (!force && ((fs->super->s_lastcheck < fs->super->s_mtime) || (fs->super->s_state & EXT2_ERROR_FS) || ((fs->super->s_state & EXT2_VALID_FS) == 0))) { fprintf(stderr, _("Please run 'e2fsck -f %s' first.\n\n"), device_name); exit(1); } /* * XXXX The combination of flex_bg and !resize_inode * causes major problems for resize2fs, since when the * group descriptors grow in size this can potentially * require multiple inode tables to be moved aside to * make room, and resize2fs chokes rather badly in * this scenario. It's a rare combination, except * when a filesystem is expanded more than a certain * size, so for now, we'll just prohibit that * combination. This is something we should fix * eventually, though. */ if ((fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) && !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE)) { com_err(program_name, 0, _("%s: The combination of " "flex_bg and\n\t!resize_inode features " "is not supported by resize2fs.\n"), device_name); exit(1); } printf(_("Resizing the filesystem on " "%s to %llu (%dk) blocks.\n"), device_name, new_size, fs->blocksize / 1024); retval = resize_fs(fs, &new_size, flags, ((flags & RESIZE_PERCENT_COMPLETE) ? resize_progress_func : 0)); } free(mtpt); if (retval) { com_err(program_name, retval, _("while trying to resize %s"), device_name); fprintf(stderr, _("Please run 'e2fsck -fy %s' to fix the filesystem\n" "after the aborted resize operation.\n"), device_name); ext2fs_close(fs); exit(1); } printf(_("The filesystem on %s is now %llu blocks long.\n\n"), device_name, new_size); if ((st_buf.st_size > new_file_size) && (fd > 0)) { #ifdef HAVE_FTRUNCATE64 retval = ftruncate64(fd, new_file_size); #else retval = 0; /* Only truncate if new_file_size doesn't overflow off_t */ if (((off_t) new_file_size) == new_file_size) retval = ftruncate(fd, (off_t) new_file_size); #endif if (retval) com_err(program_name, retval, _("while trying to truncate %s"), device_name); } if (fd > 0) close(fd); remove_error_table(&et_ext2_error_table); return (0); }
int qcow2_write_raw_image(int qcow2_fd, int raw_fd, struct ext2_qcow2_hdr *hdr) { struct ext2_qcow2_image img; errcode_t ret = 0; unsigned int l1_index, l2_index; ext2_off64_t offset; blk64_t *l1_table, *l2_table = 0; void *copy_buf = NULL; size_t size; if (hdr->crypt_method) return -QCOW_ENCRYPTED; img.fd = qcow2_fd; img.hdr = hdr; img.l2_cache = NULL; img.l1_table = NULL; img.cluster_bits = ext2fs_be32_to_cpu(hdr->cluster_bits); img.cluster_size = 1 << img.cluster_bits; img.l1_size = ext2fs_be32_to_cpu(hdr->l1_size); img.l1_offset = ext2fs_be64_to_cpu(hdr->l1_table_offset); img.l2_size = 1 << (img.cluster_bits - 3); img.image_size = ext2fs_be64_to_cpu(hdr->size); ret = ext2fs_get_memzero(img.cluster_size, &l2_table); if (ret) goto out; ret = ext2fs_get_memzero(1 << img.cluster_bits, ©_buf); if (ret) goto out; if (ext2fs_llseek(raw_fd, 0, SEEK_SET) < 0) { ret = errno; goto out; } ret = qcow2_read_l1_table(&img); if (ret) goto out; l1_table = img.l1_table; /* Walk through l1 table */ for (l1_index = 0; l1_index < img.l1_size; l1_index++) { ext2_off64_t off_out; offset = ext2fs_be64_to_cpu(l1_table[l1_index]) & ~QCOW_OFLAG_COPIED; if ((offset > img.image_size) || (offset <= 0)) continue; if (offset & QCOW_OFLAG_COMPRESSED) { ret = -QCOW_COMPRESSED; goto out; } ret = qcow2_read_l2_table(&img, offset, &l2_table); if (ret) break; /* Walk through l2 table and copy data blocks into raw image */ for (l2_index = 0; l2_index < img.l2_size; l2_index++) { offset = ext2fs_be64_to_cpu(l2_table[l2_index]) & ~QCOW_OFLAG_COPIED; if (offset == 0) continue; off_out = (l1_index * img.l2_size) + l2_index; off_out <<= img.cluster_bits; ret = qcow2_copy_data(qcow2_fd, raw_fd, offset, off_out, copy_buf, img.cluster_size); if (ret) goto out; } } /* Resize the output image to the filesystem size */ if (ext2fs_llseek(raw_fd, img.image_size - 1, SEEK_SET) < 0) return errno; ((char *)copy_buf)[0] = 0; size = write(raw_fd, copy_buf, 1); if (size != 1) return errno; out: if (copy_buf) ext2fs_free_mem(©_buf); if (img.l1_table) ext2fs_free_mem(&img.l1_table); if (l2_table) ext2fs_free_mem(&l2_table); return ret; }
static errcode_t raw_write_blk(io_channel channel, struct unix_private_data *data, unsigned long long block, int count, const void *bufv) { ssize_t size; ext2_loff_t location; int actual = 0; errcode_t retval; const unsigned char *buf = bufv; if (count == 1) size = channel->block_size; else { if (count < 0) size = -count; else size = count * channel->block_size; } data->io_stats.bytes_written += size; location = ((ext2_loff_t) block * channel->block_size) + data->offset; if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; goto error_out; } if ((channel->align == 0) || (IS_ALIGNED(buf, channel->align) && IS_ALIGNED(size, channel->align))) { actual = write(data->dev, buf, size); if (actual != size) { short_write: retval = EXT2_ET_SHORT_WRITE; goto error_out; } return 0; } #ifdef ALIGN_DEBUG printf("raw_write_blk: O_DIRECT fallback: %p %lu\n", buf, (unsigned long) size); #endif /* * The buffer or size which we're trying to write isn't aligned * to the O_DIRECT rules, so we need to do this the hard way... */ while (size > 0) { if (size < channel->block_size) { actual = read(data->dev, data->bounce, channel->block_size); if (actual != channel->block_size) { retval = EXT2_ET_SHORT_READ; goto error_out; } } actual = size; if (size > channel->block_size) actual = channel->block_size; memcpy(data->bounce, buf, actual); actual = write(data->dev, data->bounce, channel->block_size); if (actual != channel->block_size) goto short_write; size -= actual; buf += actual; } return 0; error_out: if (channel->write_error) retval = (channel->write_error)(channel, block, count, buf, size, actual, retval); return retval; }
int main (int argc, char ** argv) { errcode_t retval; ext2_filsys fs; int c; int flags = 0; int flush = 0; int force = 0; int io_flags = 0; int force_min_size = 0; int print_min_size = 0; int fd, ret; blk64_t new_size = 0; blk64_t max_size = 0; blk64_t min_size = 0; io_manager io_ptr; char *new_size_str = 0; int use_stride = -1; ext2fs_struct_stat st_buf; __s64 new_file_size; unsigned int sys_page_size = 4096; unsigned int blocksize; long sysval; int len, mount_flags; char *mtpt, *undo_file = NULL; #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); fprintf (stderr, "resize2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE); if (argc && *argv) program_name = *argv; while ((c = getopt(argc, argv, "d:fFhMPpS:bsz:")) != EOF) { switch (c) { case 'h': usage(program_name); break; case 'f': force = 1; break; case 'F': flush = 1; break; case 'M': force_min_size = 1; break; case 'P': print_min_size = 1; break; case 'd': flags |= atoi(optarg); break; case 'p': flags |= RESIZE_PERCENT_COMPLETE; break; case 'S': use_stride = atoi(optarg); break; case 'b': flags |= RESIZE_ENABLE_64BIT; break; case 's': flags |= RESIZE_DISABLE_64BIT; break; case 'z': undo_file = optarg; break; default: usage(program_name); } } if (optind == argc) usage(program_name); device_name = argv[optind++]; if (optind < argc) new_size_str = argv[optind++]; if (optind < argc) usage(program_name); io_options = strchr(device_name, '?'); if (io_options) *io_options++ = 0; /* * Figure out whether or not the device is mounted, and if it is * where it is mounted. */ len=80; while (1) { mtpt = malloc(len); if (!mtpt) return ENOMEM; mtpt[len-1] = 0; retval = ext2fs_check_mount_point(device_name, &mount_flags, mtpt, len); if (retval) { com_err("ext2fs_check_mount_point", retval, _("while determining whether %s is mounted."), device_name); exit(1); } if (!(mount_flags & EXT2_MF_MOUNTED) || (mtpt[len-1] == 0)) break; free(mtpt); len = 2 * len; } fd = ext2fs_open_file(device_name, O_RDWR, 0); if (fd < 0) { com_err("open", errno, _("while opening %s"), device_name); exit(1); } ret = ext2fs_fstat(fd, &st_buf); if (ret < 0) { com_err("open", errno, _("while getting stat information for %s"), device_name); exit(1); } if (flush) { retval = ext2fs_sync_device(fd, 1); if (retval) { com_err(argv[0], retval, _("while trying to flush %s"), device_name); exit(1); } } if (!S_ISREG(st_buf.st_mode )) { close(fd); fd = -1; } #ifdef CONFIG_TESTIO_DEBUG if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) { io_ptr = test_io_manager; test_io_backing_manager = unix_io_manager; } else #endif io_ptr = unix_io_manager; if (!(mount_flags & EXT2_MF_MOUNTED)) io_flags = EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE; io_flags |= EXT2_FLAG_64BITS; if (undo_file) { retval = resize2fs_setup_tdb(device_name, undo_file, &io_ptr); if (retval) exit(1); } retval = ext2fs_open2(device_name, io_options, io_flags, 0, 0, io_ptr, &fs); if (retval) { com_err(program_name, retval, _("while trying to open %s"), device_name); printf("%s", _("Couldn't find valid filesystem superblock.\n")); exit (1); } fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE; /* * Before acting on an unmounted filesystem, make sure it's ok, * unless the user is forcing it. * * We do ERROR and VALID checks even if we're only printing the * minimimum size, because traversal of a badly damaged filesystem * can cause issues as well. We don't require it to be fscked after * the last mount time in this case, though, as this is a bit less * risky. */ if (!force && !(mount_flags & EXT2_MF_MOUNTED)) { int checkit = 0; if (fs->super->s_state & EXT2_ERROR_FS) checkit = 1; if ((fs->super->s_state & EXT2_VALID_FS) == 0) checkit = 1; if ((fs->super->s_lastcheck < fs->super->s_mtime) && !print_min_size) checkit = 1; if (checkit) { fprintf(stderr, _("Please run 'e2fsck -f %s' first.\n\n"), device_name); exit(1); } } /* * Check for compatibility with the feature sets. We need to * be more stringent than ext2fs_open(). */ if (fs->super->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) { com_err(program_name, EXT2_ET_UNSUPP_FEATURE, "(%s)", device_name); exit(1); } min_size = calculate_minimum_resize_size(fs, flags); if (print_min_size) { printf(_("Estimated minimum size of the filesystem: %llu\n"), min_size); exit(0); } /* Determine the system page size if possible */ #ifdef HAVE_SYSCONF #if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE)) #define _SC_PAGESIZE _SC_PAGE_SIZE #endif #ifdef _SC_PAGESIZE sysval = sysconf(_SC_PAGESIZE); if (sysval > 0) sys_page_size = sysval; #endif /* _SC_PAGESIZE */ #endif /* HAVE_SYSCONF */ /* * Get the size of the containing partition, and use this for * defaults and for making sure the new filesystem doesn't * exceed the partition size. */ blocksize = fs->blocksize; retval = ext2fs_get_device_size2(device_name, blocksize, &max_size); if (retval) { com_err(program_name, retval, "%s", _("while trying to determine filesystem size")); exit(1); } if (force_min_size) new_size = min_size; else if (new_size_str) { new_size = parse_num_blocks2(new_size_str, fs->super->s_log_block_size); if (new_size == 0) { com_err(program_name, 0, _("Invalid new size: %s\n"), new_size_str); exit(1); } } else { new_size = max_size; /* Round down to an even multiple of a pagesize */ if (sys_page_size > blocksize) new_size &= ~((sys_page_size / blocksize)-1); } /* If changing 64bit, don't change the filesystem size. */ if (flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT)) { new_size = ext2fs_blocks_count(fs->super); } if (!ext2fs_has_feature_64bit(fs->super)) { /* Take 16T down to 2^32-1 blocks */ if (new_size == (1ULL << 32)) new_size--; else if (new_size > (1ULL << 32)) { com_err(program_name, 0, "%s", _("New size too large to be " "expressed in 32 bits\n")); exit(1); } } if (!force && new_size < min_size) { com_err(program_name, 0, _("New size smaller than minimum (%llu)\n"), min_size); exit(1); } if (use_stride >= 0) { if (use_stride >= (int) fs->super->s_blocks_per_group) { com_err(program_name, 0, "%s", _("Invalid stride length")); exit(1); } fs->stride = fs->super->s_raid_stride = use_stride; ext2fs_mark_super_dirty(fs); } else determine_fs_stride(fs); /* * If we are resizing a plain file, and it's not big enough, * automatically extend it in a sparse fashion by writing the * last requested block. */ new_file_size = ((__u64) new_size) * blocksize; if ((__u64) new_file_size > (((__u64) 1) << (sizeof(st_buf.st_size)*8 - 1)) - 1) fd = -1; if ((new_file_size > st_buf.st_size) && (fd > 0)) { if ((ext2fs_llseek(fd, new_file_size-1, SEEK_SET) >= 0) && (write(fd, "0", 1) == 1)) max_size = new_size; } if (!force && (new_size > max_size)) { fprintf(stderr, _("The containing partition (or device)" " is only %llu (%dk) blocks.\nYou requested a new size" " of %llu blocks.\n\n"), max_size, blocksize / 1024, new_size); exit(1); } if ((flags & RESIZE_DISABLE_64BIT) && (flags & RESIZE_ENABLE_64BIT)) { fprintf(stderr, _("Cannot set and unset 64bit feature.\n")); exit(1); } else if (flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT)) { if (new_size >= (1ULL << 32)) { fprintf(stderr, _("Cannot change the 64bit feature " "on a filesystem that is larger than " "2^32 blocks.\n")); exit(1); } if (mount_flags & EXT2_MF_MOUNTED) { fprintf(stderr, _("Cannot change the 64bit feature " "while the filesystem is mounted.\n")); exit(1); } if (flags & RESIZE_ENABLE_64BIT && !ext2fs_has_feature_extents(fs->super)) { fprintf(stderr, _("Please enable the extents feature " "with tune2fs before enabling the 64bit " "feature.\n")); exit(1); } } else if (new_size == ext2fs_blocks_count(fs->super)) { fprintf(stderr, _("The filesystem is already %llu (%dk) " "blocks long. Nothing to do!\n\n"), new_size, blocksize / 1024); exit(0); } if ((flags & RESIZE_ENABLE_64BIT) && ext2fs_has_feature_64bit(fs->super)) { fprintf(stderr, _("The filesystem is already 64-bit.\n")); exit(0); } if ((flags & RESIZE_DISABLE_64BIT) && !ext2fs_has_feature_64bit(fs->super)) { fprintf(stderr, _("The filesystem is already 32-bit.\n")); exit(0); } if (mount_flags & EXT2_MF_MOUNTED) { bigalloc_check(fs, force); retval = online_resize_fs(fs, mtpt, &new_size, flags); } else { bigalloc_check(fs, force); if (flags & RESIZE_ENABLE_64BIT) printf(_("Converting the filesystem to 64-bit.\n")); else if (flags & RESIZE_DISABLE_64BIT) printf(_("Converting the filesystem to 32-bit.\n")); else printf(_("Resizing the filesystem on " "%s to %llu (%dk) blocks.\n"), device_name, new_size, blocksize / 1024); retval = resize_fs(fs, &new_size, flags, ((flags & RESIZE_PERCENT_COMPLETE) ? resize_progress_func : 0)); } free(mtpt); if (retval) { com_err(program_name, retval, _("while trying to resize %s"), device_name); fprintf(stderr, _("Please run 'e2fsck -fy %s' to fix the filesystem\n" "after the aborted resize operation.\n"), device_name); ext2fs_close_free(&fs); exit(1); } printf(_("The filesystem on %s is now %llu (%dk) blocks long.\n\n"), device_name, new_size, blocksize / 1024); if ((st_buf.st_size > new_file_size) && (fd > 0)) { #ifdef HAVE_FTRUNCATE64 retval = ftruncate64(fd, new_file_size); #else retval = 0; /* Only truncate if new_file_size doesn't overflow off_t */ if (((off_t) new_file_size) == new_file_size) retval = ftruncate(fd, (off_t) new_file_size); #endif if (retval) com_err(program_name, retval, _("while trying to truncate %s"), device_name); } if (fd > 0) close(fd); remove_error_table(&et_ext2_error_table); return (0); }