diff options
Diffstat (limited to 'misc')
-rw-r--r-- | misc/Makefile.in | 37 | ||||
-rw-r--r-- | misc/badblocks.c | 6 | ||||
-rw-r--r-- | misc/dumpe2fs.c | 69 | ||||
-rw-r--r-- | misc/e2freefrag.c | 16 | ||||
-rw-r--r-- | misc/e2image.8.in | 50 | ||||
-rw-r--r-- | misc/e2image.c | 884 | ||||
-rw-r--r-- | misc/e2undo.c | 17 | ||||
-rw-r--r-- | misc/e4defrag.8.in | 80 | ||||
-rw-r--r-- | misc/e4defrag.c | 2051 | ||||
-rw-r--r-- | misc/filefrag.c | 1 | ||||
-rw-r--r-- | misc/findsuper.c | 15 | ||||
-rw-r--r-- | misc/mke2fs.8.in | 44 | ||||
-rw-r--r-- | misc/mke2fs.c | 666 | ||||
-rw-r--r-- | misc/mke2fs.conf | 1 | ||||
-rw-r--r-- | misc/mke2fs.conf.5.in | 43 | ||||
-rw-r--r-- | misc/tune2fs.8.in | 3 | ||||
-rw-r--r-- | misc/tune2fs.c | 161 | ||||
-rw-r--r-- | misc/util.c | 4 |
18 files changed, 3570 insertions, 578 deletions
diff --git a/misc/Makefile.in b/misc/Makefile.in index def66f0e..5f62323a 100644 --- a/misc/Makefile.in +++ b/misc/Makefile.in @@ -11,6 +11,9 @@ INSTALL = @INSTALL@ @MCONFIG@ +@DEFRAG_CMT@@LINUX_CMT@E4DEFRAG_PROG= e4defrag +@DEFRAG_CMT@@LINUX_CMT@E4DEFRAG_MAN= e4defrag.8 + @IMAGER_CMT@E2IMAGE_PROG= e2image @IMAGER_CMT@E2IMAGE_MAN= e2image.8 @@ -25,10 +28,11 @@ INSTALL = @INSTALL@ SPROGS= mke2fs badblocks tune2fs dumpe2fs $(BLKID_PROG) logsave \ $(E2IMAGE_PROG) @FSCK_PROG@ e2undo -USPROGS= mklost+found filefrag e2freefrag $(UUIDD_PROG) +USPROGS= mklost+found filefrag e2freefrag $(UUIDD_PROG) $(E4DEFRAG_PROG) SMANPAGES= tune2fs.8 mklost+found.8 mke2fs.8 dumpe2fs.8 badblocks.8 \ e2label.8 $(FINDFS_MAN) $(BLKID_MAN) $(E2IMAGE_MAN) \ - logsave.8 filefrag.8 e2freefrag.8 e2undo.8 $(UUIDD_MAN) @FSCK_MAN@ + logsave.8 filefrag.8 e2freefrag.8 e2undo.8 \ + $(UUIDD_MAN) $(E4DEFRAG_MAN) @FSCK_MAN@ FMANPAGES= mke2fs.conf.5 UPROGS= chattr lsattr @UUID_CMT@ uuidgen @@ -50,6 +54,7 @@ FSCK_OBJS= fsck.o base_device.o ismounted.o BLKID_OBJS= blkid.o FILEFRAG_OBJS= filefrag.o E2UNDO_OBJS= e2undo.o +E4DEFRAG_OBJS= e4defrag.o E2FREEFRAG_OBJS= e2freefrag.o PROFILED_TUNE2FS_OBJS= profiled/tune2fs.o profiled/util.o @@ -68,7 +73,7 @@ PROFILED_FSCK_OBJS= profiled/fsck.o profiled/base_device.o \ PROFILED_BLKID_OBJS= profiled/blkid.o PROFILED_FILEFRAG_OBJS= profiled/filefrag.o PROFILED_E2UNDO_OBJS= profiled/e2undo.o - +PROFILED_E4DEFRAG_OBJS= profiled/e4defrag.o XTRA_CFLAGS= -I$(srcdir)/../e2fsck -I. @@ -99,12 +104,12 @@ COMPILE_ET=$(top_builddir)/lib/et/compile_et --build-tree @PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $< all:: profiled $(SPROGS) $(UPROGS) $(USPROGS) $(SMANPAGES) $(UMANPAGES) \ - $(FMANPAGES) $(LPROGS) + $(FMANPAGES) $(LPROGS) $(E4DEFRAG_PROG) @PROFILE_CMT@all:: tune2fs.profiled blkid.profiled e2image.profiled \ e2undo.profiled mke2fs.profiled dumpe2fs.profiled fsck.profiled \ logsave.profiled filefrag.profiled uuidgen.profiled uuidd.profiled \ - e2image.profiled + e2image.profiled e4defrag.profiled profiled: @PROFILE_CMT@ $(E) " MKDIR $@" @@ -126,7 +131,7 @@ profile.o: findsuper: findsuper.o $(E) " LD $@" - $(Q) $(CC) $(ALL_LDFLAGS) -o findsuper findsuper.o + $(Q) $(CC) $(ALL_LDFLAGS) -o findsuper findsuper.o $(LIBS) partinfo: partinfo.o $(E) " LD $@" @@ -191,6 +196,15 @@ e2undo.profiled: $(PROFILED_E2UNDO_OBJS) $(PROFILED_DEPLIBS) $(Q) $(CC) $(ALL_LDFLAGS) -g -pg -o e2undo.profiled \ $(PROFILED_E2UNDO_OBJS) $(PROFILED_LIBS) $(LIBINTL) +e4defrag: $(E4DEFRAG_OBJS) $(DEPLIBS) + $(E) " LD $@" + $(Q) $(CC) $(ALL_LDFLAGS) -o e4defrag $(E4DEFRAG_OBJS) $(LIBS) + +e4defrag.profiled: $(PROFILED_E4DEFRAG_OBJS) $(PROFILED_DEPLIBS) + $(E) " LD $@" + $(Q) $(CC) $(ALL_LDFLAGS) -g -pg -o e4defrag.profiled \ + $(PROFILED_E4DEFRAG_OBJS) $(PROFILED_LIBS) + base_device: base_device.c $(E) " LD $@" $(Q) $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(srcdir)/base_device.c \ @@ -338,6 +352,10 @@ e2image.8: $(DEP_SUBSTITUTE) $(srcdir)/e2image.8.in $(E) " SUBST $@" $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2image.8.in e2image.8 +e4defrag.8: $(DEP_SUBSTITUTE) $(srcdir)/e4defrag.8.in + $(E) " SUBST $@" + $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e4defrag.8.in e4defrag.8 + dumpe2fs.8: $(DEP_SUBSTITUTE) $(srcdir)/dumpe2fs.8.in $(E) " SUBST $@" $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/dumpe2fs.8.in dumpe2fs.8 @@ -561,11 +579,12 @@ mklost+found.o: $(srcdir)/mklost+found.c $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(top_srcdir)/version.h \ $(srcdir)/nls-enable.h mke2fs.o: $(srcdir)/mke2fs.c $(top_srcdir)/lib/ext2fs/ext2_fs.h \ - $(top_builddir)/lib/ext2fs/ext2_types.h $(top_srcdir)/lib/et/com_err.h \ - $(top_srcdir)/lib/e2p/e2p.h $(top_srcdir)/lib/ext2fs/ext2fs.h \ - $(top_srcdir)/lib/ext2fs/ext3_extents.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ + $(top_builddir)/lib/ext2fs/ext2_types.h $(top_srcdir)/lib/ext2fs/ext2fsP.h \ + $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext3_extents.h \ + $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h \ $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/bitops.h \ + $(top_srcdir)/lib/e2p/e2p.h $(top_srcdir)/lib/ext2fs/ext2fs.h \ $(srcdir)/util.h $(srcdir)/../e2fsck/profile.h prof_err.h \ $(top_srcdir)/version.h $(srcdir)/nls-enable.h chattr.o: $(srcdir)/chattr.c $(top_srcdir)/lib/ext2fs/ext2_fs.h \ diff --git a/misc/badblocks.c b/misc/badblocks.c index 9c1e2a31..1e7e1c84 100644 --- a/misc/badblocks.c +++ b/misc/badblocks.c @@ -29,7 +29,9 @@ * list. (Work done by David Beattie) */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE /* for O_DIRECT */ +#endif #ifndef O_LARGEFILE #define O_LARGEFILE 0 @@ -1034,7 +1036,7 @@ int main (int argc, char ** argv) FILE * in = NULL; int block_size = 1024; unsigned int blocks_at_once = 64; - blk_t last_block, first_block; + blk64_t last_block, first_block; int num_passes = 0; int passes_clean = 0; int dev; @@ -1172,7 +1174,7 @@ int main (int argc, char ** argv) usage(); device_name = argv[optind++]; if (optind > argc - 1) { - errcode = ext2fs_get_device_size(device_name, + errcode = ext2fs_get_device_size2(device_name, block_size, &last_block); if (errcode == EXT2_ET_UNIMPLEMENTED) { diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c index 7a7f506d..9a0dd46b 100644 --- a/misc/dumpe2fs.c +++ b/misc/dumpe2fs.c @@ -63,12 +63,12 @@ static void print_number(unsigned long num) printf("%lu", num); } -static void print_range(unsigned long a, unsigned long b) +static void print_range(unsigned long long a, unsigned long long b) { if (hex_format) - printf("0x%04lx-0x%04lx", a, b); + printf("0x%llx-0x%llx", a, b); else - printf("%lu-%lu", a, b); + printf("%llu-%llu", a, b); } static void print_free(unsigned long group, char * bitmap, @@ -114,7 +114,7 @@ static void print_bg_opts(ext2_filsys fs, dgrp_t i) int first = 1, bg_flags = 0; if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) - bg_flags = fs->group_desc[i].bg_flags; + bg_flags = ext2fs_bg_flags(fs, i); print_bg_opt(bg_flags, EXT2_BG_INODE_UNINIT, "INODE_UNINIT", &first); @@ -145,13 +145,13 @@ static void print_bg_rel_offset(ext2_filsys fs, blk64_t block, int itable, static void list_desc (ext2_filsys fs) { unsigned long i; - blk_t first_block, last_block; - blk_t super_blk, old_desc_blk, new_desc_blk; + blk64_t first_block, last_block; + blk64_t super_blk, old_desc_blk, new_desc_blk; char *block_bitmap=NULL, *inode_bitmap=NULL; int inode_blocks_per_group, old_desc_blocks, reserved_gdt; int block_nbytes, inode_nbytes; int has_super; - blk_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block); + blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block); ext2_ino_t ino_itr = 1; block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8; @@ -174,20 +174,20 @@ static void list_desc (ext2_filsys fs) else old_desc_blocks = fs->desc_blocks; for (i = 0; i < fs->group_desc_count; i++) { - first_block = ext2fs_group_first_block(fs, i); - last_block = ext2fs_group_last_block(fs, i); + first_block = ext2fs_group_first_block2(fs, i); + last_block = ext2fs_group_last_block2(fs, i); - ext2fs_super_and_bgd_loc(fs, i, &super_blk, - &old_desc_blk, &new_desc_blk, 0); + ext2fs_super_and_bgd_loc2(fs, i, &super_blk, + &old_desc_blk, &new_desc_blk, 0); printf (_("Group %lu: (Blocks "), i); print_range(first_block, last_block); fputs(")", stdout); print_bg_opts(fs, i); if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) - printf(_(" Checksum 0x%04x, unused inodes %d\n"), - fs->group_desc[i].bg_checksum, - fs->group_desc[i].bg_itable_unused); + printf(_(" Checksum 0x%04x, unused inodes %u\n"), + ext2fs_bg_checksum(fs, i), + ext2fs_bg_itable_unused(fs, i)); has_super = ((i==0) || super_blk); if (has_super) { printf (_(" %s superblock at "), @@ -213,34 +213,31 @@ static void list_desc (ext2_filsys fs) if (has_super) fputc('\n', stdout); fputs(_(" Block bitmap at "), stdout); - print_number(fs->group_desc[i].bg_block_bitmap); - print_bg_rel_offset(fs, fs->group_desc[i].bg_block_bitmap, 0, + print_number(ext2fs_block_bitmap_loc(fs, i)); + print_bg_rel_offset(fs, ext2fs_block_bitmap_loc(fs, i), 0, first_block, last_block); - fputs(_(", Inode bitmap at "), stdout); - print_number(fs->group_desc[i].bg_inode_bitmap); - print_bg_rel_offset(fs, fs->group_desc[i].bg_inode_bitmap, 0, + print_number(ext2fs_inode_bitmap_loc(fs, i)); + print_bg_rel_offset(fs, ext2fs_inode_bitmap_loc(fs, i), 0, first_block, last_block); - fputs(_("\n Inode table at "), stdout); - print_range(fs->group_desc[i].bg_inode_table, - fs->group_desc[i].bg_inode_table + + print_range(ext2fs_inode_table_loc(fs, i), + ext2fs_inode_table_loc(fs, i) + inode_blocks_per_group - 1); - print_bg_rel_offset(fs, fs->group_desc[i].bg_inode_table, 1, + print_bg_rel_offset(fs, ext2fs_inode_table_loc(fs, i), 1, first_block, last_block); - printf (_("\n %u free blocks, %u free inodes, " "%u directories%s"), - fs->group_desc[i].bg_free_blocks_count, - fs->group_desc[i].bg_free_inodes_count, - fs->group_desc[i].bg_used_dirs_count, - fs->group_desc[i].bg_itable_unused ? "" : "\n"); - if (fs->group_desc[i].bg_itable_unused) + ext2fs_bg_free_blocks_count(fs, i), + ext2fs_bg_free_inodes_count(fs, i), + ext2fs_bg_used_dirs_count(fs, i), + ext2fs_bg_itable_unused(fs, i) ? "" : "\n"); + if (ext2fs_bg_itable_unused(fs, i)) printf (_(", %u unused inodes\n"), - fs->group_desc[i].bg_itable_unused); + ext2fs_bg_itable_unused(fs, i)); if (block_bitmap) { fputs(_(" Free blocks: "), stdout); - ext2fs_get_block_bitmap_range(fs->block_map, + ext2fs_get_block_bitmap_range2(fs->block_map, blk_itr, block_nbytes << 3, block_bitmap); print_free(i, block_bitmap, fs->super->s_clusters_per_group, @@ -251,7 +248,7 @@ static void list_desc (ext2_filsys fs) } if (inode_bitmap) { fputs(_(" Free inodes: "), stdout); - ext2fs_get_inode_bitmap_range(fs->inode_map, + ext2fs_get_inode_bitmap_range2(fs->inode_map, ino_itr, inode_nbytes << 3, inode_bitmap); print_free(i, inode_bitmap, fs->super->s_inodes_per_group, 1, 1); @@ -377,7 +374,7 @@ static void print_journal_information(ext2_filsys fs) journal_superblock_t *jsb; /* Get the journal superblock */ - if ((retval = io_channel_read_blk(fs->io, fs->super->s_first_data_block+1, -1024, buf))) { + if ((retval = io_channel_read_blk64(fs->io, fs->super->s_first_data_block+1, -1024, buf))) { com_err(program_name, retval, _("while reading journal superblock")); exit(1); @@ -409,7 +406,7 @@ static void print_journal_information(ext2_filsys fs) } } -static void parse_extended_opts(const char *opts, blk_t *superblock, +static void parse_extended_opts(const char *opts, blk64_t *superblock, int *blocksize) { char *buf, *token, *next, *p, *arg, *badopt = 0; @@ -491,7 +488,7 @@ int main (int argc, char ** argv) errcode_t retval; ext2_filsys fs; int print_badblocks = 0; - blk_t use_superblock = 0; + blk64_t use_superblock = 0; int use_blocksize = 0; int image_dump = 0; int force = 0; @@ -544,7 +541,7 @@ int main (int argc, char ** argv) if (optind > argc - 1) usage(); device_name = argv[optind++]; - flags = EXT2_FLAG_JOURNAL_DEV_OK | EXT2_FLAG_SOFTSUPP_FEATURES; + flags = EXT2_FLAG_JOURNAL_DEV_OK | EXT2_FLAG_SOFTSUPP_FEATURES | EXT2_FLAG_64BITS; if (force) flags |= EXT2_FLAG_FORCE; if (image_dump) diff --git a/misc/e2freefrag.c b/misc/e2freefrag.c index 50bb9863..e6fd67eb 100644 --- a/misc/e2freefrag.c +++ b/misc/e2freefrag.c @@ -91,7 +91,7 @@ void update_chunk_stats(struct chunk_info *info, unsigned long chunk_size) void scan_block_bitmap(ext2_filsys fs, struct chunk_info *info) { - unsigned long long blocks_count = fs->super->s_blocks_count; + unsigned long long blocks_count = ext2fs_blocks_count(fs->super); unsigned long long chunks = (blocks_count + info->blks_in_chunk) >> (info->chunkbits - info->blocksize_bits); unsigned long long chunk_num; @@ -119,7 +119,7 @@ void scan_block_bitmap(ext2_filsys fs, struct chunk_info *info) blk = fs->super->s_first_data_block; chunk_start_blk = blk; } - used = ext2fs_fast_test_block_bitmap(fs->block_map, + used = ext2fs_fast_test_block_bitmap2(fs->block_map, chunk_start_blk); if (!used) { last_chunk_size++; @@ -144,20 +144,20 @@ errcode_t get_chunk_info(ext2_filsys fs, struct chunk_info *info) unsigned long total_chunks; char *unitp = "KMGTPEZY"; int units = 10; - unsigned long start = 0, end, cum; + unsigned long start = 0, end; int i, retval = 0; scan_block_bitmap(fs, info); - printf("Total blocks: %u\nFree blocks: %u (%0.1f%%)\n", - fs->super->s_blocks_count, fs->super->s_free_blocks_count, + printf("Total blocks: %llu\nFree blocks: %u (%0.1f%%)\n", + ext2fs_blocks_count(fs->super), fs->super->s_free_blocks_count, (double)fs->super->s_free_blocks_count * 100 / - fs->super->s_blocks_count); + ext2fs_blocks_count(fs->super)); if (info->chunkbytes) { printf("\nChunksize: %lu bytes (%u blocks)\n", info->chunkbytes, info->blks_in_chunk); - total_chunks = (fs->super->s_blocks_count + + total_chunks = (ext2fs_blocks_count(fs->super) + info->blks_in_chunk) >> (info->chunkbits - info->blocksize_bits); printf("Total chunks: %lu\nFree chunks: %lu (%0.1f%%)\n", @@ -218,7 +218,7 @@ void close_device(char *device_name, ext2_filsys fs) void collect_info(ext2_filsys fs, struct chunk_info *chunk_info) { - unsigned int retval = 0, i, free_blks; + unsigned int retval = 0; printf("Device: %s\n", fs->device_name); printf("Blocksize: %u bytes\n", fs->blocksize); diff --git a/misc/e2image.8.in b/misc/e2image.8.in index e18a30b5..74d2a0bb 100644 --- a/misc/e2image.8.in +++ b/misc/e2image.8.in @@ -40,7 +40,8 @@ another program, such as (Note that this is currently only supported when creating a raw image file using the .B \-r -option, since the process of creating a normal image file currently +option, since the process of creating a normal image file, or QCOW2 +image currently requires random access to the file, which cannot be done using a pipe. This restriction will hopefully be lifted in a future version of .BR e2image .) @@ -56,13 +57,14 @@ accessible in the case where the filesystem has been badly damaged. .PP To save disk space, .B e2image -creates the image file as a sparse file. -Hence, if the image file +creates the image file as a sparse file, or in QCOW2 format. +Hence, if the sparse image file needs to be copied to another location, it should either be compressed first or copied using the .B \-\-sparse=always option to the GNU version of -.BR cp . +.BR cp . +This does not apply to the QCOW2 image, which is not sparse. .PP The size of an ext2 image file depends primarily on the size of the filesystems and how many inodes are in use. For a typical 10 gigabyte @@ -129,6 +131,46 @@ the option will prevent analysis of problems related to hash-tree indexed directories. .PP +Note that this will work even if you substitute "/dev/hda1" for another raw +disk image, or QCOW2 image previously created by +.BR e2image . +.PP +.SH QCOW2 IMAGE FILES +The +.B \-Q +option will create a QCOW2 image file instead of a normal, or raw image file. +A QCOW2 image contains all the information the raw image does, however unlike +the raw image it is not sparse. The QCOW2 image minimize the amount of disk +space by storing data in special format with pack data closely together, hence +avoiding holes while still minimizing size. +.PP +In order to send filesystem to the maintainer as a part of bug report to +e2fsprogs, use following commands (replace hda1 with the appropriate device): +.PP +.br +\ \fBe2image \-Q /dev/hda1 hda1.qcow2\fR +.br +\ \fBbzip2 -z hda1.qcow2\fR +.PP +This will only send the metadata information, without any data blocks. +However, the filenames in the directory blocks can still reveal +information about the contents of the filesystem that the bug reporter +may wish to keep confidential. To address this concern, the +.B \-s +option can be specified. This will cause +.B e2image +to scramble directory entries and zero out any unused portions +of the directory blocks before writing the image file. However, the +.B \-s +option will prevent analysis of problems related to hash-tree indexed +directories. +.PP +Note that QCOW2 image created by +.B e2image +is regular QCOW2 image and can be processed by tools aware of QCOW2 format +such as for example +.BR qemu-img . +.PP .SH AUTHOR .B e2image was written by Theodore Ts'o (tytso@mit.edu). diff --git a/misc/e2image.c b/misc/e2image.c index 83c1cca9..83a9d025 100644 --- a/misc/e2image.c +++ b/misc/e2image.c @@ -33,6 +33,7 @@ extern int optind; #include <errno.h> #include <sys/stat.h> #include <sys/types.h> +#include <assert.h> #include "ext2fs/ext2_fs.h" #include "ext2fs/ext2fs.h" @@ -40,51 +41,118 @@ extern int optind; #include "uuid/uuid.h" #include "e2p/e2p.h" #include "ext2fs/e2image.h" +#include "ext2fs/qcow2.h" #include "../version.h" #include "nls-enable.h" +#define QCOW_OFLAG_COPIED (1LL << 63) + + const char * program_name = "e2image"; char * device_name = NULL; +static void lseek_error_and_exit(int errnum) +{ + perror("seek"); + exit(1); +} + +static blk64_t align_offset(blk64_t offset, int n) +{ + return (offset + n - 1) & ~(n - 1); +} + +static int get_bits_from_size(size_t size) +{ + int res = 0; + + if (size == 0) + return -1; + + while (size != 1) { + /* Not a power of two */ + if (size & 1) + return -1; + + size >>= 1; + res++; + } + return res; +} + static void usage(void) { - fprintf(stderr, _("Usage: %s [-rsI] device image_file\n"), + fprintf(stderr, _("Usage: %s [-rsIQ] device image_file\n"), program_name); exit (1); } -static void write_header(int fd, struct ext2_image_hdr *hdr, int blocksize) +static void generic_write(int fd, void *buf, int blocksize, blk64_t block) { - char *header_buf; - int actual; + int count, free_buf = 0; + errcode_t err; - header_buf = malloc(blocksize); - if (!header_buf) { - fputs(_("Couldn't allocate header buffer\n"), stderr); - exit(1); + if (!blocksize) + return; + + if (!buf) { + free_buf = 1; + err = ext2fs_get_arrayzero(1, blocksize, &buf); + if (err) { + com_err(program_name, err, "while allocating buffer"); + exit(1); + } } - if (lseek(fd, 0, SEEK_SET) < 0) { - perror("lseek while writing header"); + count = write(fd, buf, blocksize); + if (count != blocksize) { + if (count == -1) + err = errno; + else + err = 0; + + if (block) + com_err(program_name, err, "error writing block %llu", + block); + else + com_err(program_name, err, "error in write()"); + exit(1); } - memset(header_buf, 0, blocksize); + if (free_buf) + ext2fs_free_mem(&buf); +} - if (hdr) - memcpy(header_buf, hdr, sizeof(struct ext2_image_hdr)); +static void write_header(int fd, void *hdr, int hdr_size, int wrt_size) +{ + char *header_buf; + int ret; + + /* Sanity check */ + if (hdr_size > wrt_size) { + fprintf(stderr, _("Error: header size is bigger than " + "wrt_size\n")); + } - actual = write(fd, header_buf, blocksize); - if (actual < 0) { - perror("write header"); + ret = ext2fs_get_mem(wrt_size, &header_buf); + if (ret) { + fputs(_("Couldn't allocate header buffer\n"), stderr); exit(1); } - if (actual != blocksize) { - fprintf(stderr, _("short write (only %d bytes) for " - "writing image header"), actual); + + if (ext2fs_llseek(fd, 0, SEEK_SET) < 0) { + perror("ext2fs_llseek while writing header"); exit(1); } - free(header_buf); + memset(header_buf, 0, wrt_size); + + if (hdr) + memcpy(header_buf, hdr, hdr_size); + + generic_write(fd, header_buf, wrt_size, 0); + + ext2fs_free_mem(&header_buf); } static void write_image_file(ext2_filsys fs, int fd) @@ -93,17 +161,17 @@ static void write_image_file(ext2_filsys fs, int fd) struct stat st; errcode_t retval; - write_header(fd, NULL, fs->blocksize); + write_header(fd, NULL, fs->blocksize, fs->blocksize); memset(&hdr, 0, sizeof(struct ext2_image_hdr)); - hdr.offset_super = lseek(fd, 0, SEEK_CUR); + hdr.offset_super = ext2fs_llseek(fd, 0, SEEK_CUR); retval = ext2fs_image_super_write(fs, fd, 0); if (retval) { com_err(program_name, retval, _("while writing superblock")); exit(1); } - hdr.offset_inode = lseek(fd, 0, SEEK_CUR); + hdr.offset_inode = ext2fs_llseek(fd, 0, SEEK_CUR); retval = ext2fs_image_inode_write(fs, fd, (fd != 1) ? IMAGER_FLAG_SPARSEWRITE : 0); if (retval) { @@ -111,14 +179,14 @@ static void write_image_file(ext2_filsys fs, int fd) exit(1); } - hdr.offset_blockmap = lseek(fd, 0, SEEK_CUR); + hdr.offset_blockmap = ext2fs_llseek(fd, 0, SEEK_CUR); retval = ext2fs_image_bitmap_write(fs, fd, 0); if (retval) { com_err(program_name, retval, _("while writing block bitmap")); exit(1); } - hdr.offset_inodemap = lseek(fd, 0, SEEK_CUR); + hdr.offset_inodemap = ext2fs_llseek(fd, 0, SEEK_CUR); retval = ext2fs_image_bitmap_write(fs, fd, IMAGER_FLAG_INODEMAP); if (retval) { com_err(program_name, retval, _("while writing inode bitmap")); @@ -142,7 +210,7 @@ static void write_image_file(ext2_filsys fs, int fd) memcpy(hdr.fs_uuid, fs->super->s_uuid, sizeof(hdr.fs_uuid)); hdr.image_time = time(0); - write_header(fd, &hdr, fs->blocksize); + write_header(fd, &hdr, fs->blocksize, fs->blocksize); } /* @@ -150,6 +218,7 @@ static void write_image_file(ext2_filsys fs, int fd) */ ext2fs_block_bitmap meta_block_map; ext2fs_block_bitmap scramble_block_map; /* Directory blocks to be scrambled */ +blk64_t meta_blocks_count; struct process_block_struct { ext2_ino_t ino; @@ -215,9 +284,9 @@ static void use_inode_shortcuts(ext2_filsys fs, int bool) } static int process_dir_block(ext2_filsys fs EXT2FS_ATTR((unused)), - blk_t *block_nr, + blk64_t *block_nr, e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), - blk_t ref_block EXT2FS_ATTR((unused)), + blk64_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data EXT2FS_ATTR((unused))) { @@ -225,69 +294,76 @@ static int process_dir_block(ext2_filsys fs EXT2FS_ATTR((unused)), p = (struct process_block_struct *) priv_data; - ext2fs_mark_block_bitmap(meta_block_map, *block_nr); + ext2fs_mark_block_bitmap2(meta_block_map, *block_nr); + meta_blocks_count++; if (scramble_block_map && p->is_dir && blockcnt >= 0) - ext2fs_mark_block_bitmap(scramble_block_map, *block_nr); + ext2fs_mark_block_bitmap2(scramble_block_map, *block_nr); return 0; } static int process_file_block(ext2_filsys fs EXT2FS_ATTR((unused)), - blk_t *block_nr, + blk64_t *block_nr, e2_blkcnt_t blockcnt, - blk_t ref_block EXT2FS_ATTR((unused)), + blk64_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data EXT2FS_ATTR((unused))) { if (blockcnt < 0) { - ext2fs_mark_block_bitmap(meta_block_map, *block_nr); + ext2fs_mark_block_bitmap2(meta_block_map, *block_nr); + meta_blocks_count++; } return 0; } static void mark_table_blocks(ext2_filsys fs) { - blk_t first_block, b; + blk64_t first_block, b; unsigned int i,j; first_block = fs->super->s_first_data_block; /* * Mark primary superblock */ - ext2fs_mark_block_bitmap(meta_block_map, first_block); + ext2fs_mark_block_bitmap2(meta_block_map, first_block); + meta_blocks_count++; /* * Mark the primary superblock descriptors */ for (j = 0; j < fs->desc_blocks; j++) { - ext2fs_mark_block_bitmap(meta_block_map, - ext2fs_descriptor_block_loc(fs, first_block, j)); + ext2fs_mark_block_bitmap2(meta_block_map, + ext2fs_descriptor_block_loc2(fs, first_block, j)); } + meta_blocks_count += fs->desc_blocks; for (i = 0; i < fs->group_desc_count; i++) { /* * Mark the blocks used for the inode table */ - if (fs->group_desc[i].bg_inode_table) { - for (j = 0, b = fs->group_desc[i].bg_inode_table; + if (ext2fs_inode_table_loc(fs, i)) { + for (j = 0, b = ext2fs_inode_table_loc(fs, i); j < (unsigned) fs->inode_blocks_per_group; j++, b++) - ext2fs_mark_block_bitmap(meta_block_map, b); + ext2fs_mark_block_bitmap2(meta_block_map, b); + meta_blocks_count += fs->inode_blocks_per_group; } /* * Mark block used for the block bitmap */ - if (fs->group_desc[i].bg_block_bitmap) { - ext2fs_mark_block_bitmap(meta_block_map, - fs->group_desc[i].bg_block_bitmap); + if (ext2fs_block_bitmap_loc(fs, i)) { + ext2fs_mark_block_bitmap2(meta_block_map, + ext2fs_block_bitmap_loc(fs, i)); + meta_blocks_count++; } /* * Mark block used for the inode bitmap */ - if (fs->group_desc[i].bg_inode_bitmap) { - ext2fs_mark_block_bitmap(meta_block_map, - fs->group_desc[i].bg_inode_bitmap); + if (ext2fs_inode_bitmap_loc(fs, i)) { + ext2fs_mark_block_bitmap2(meta_block_map, + ext2fs_inode_bitmap_loc(fs, i)); + meta_blocks_count++; } } } @@ -309,39 +385,23 @@ static int check_zero_block(char *buf, int blocksize) } static void write_block(int fd, char *buf, int sparse_offset, - int blocksize, blk_t block) + int blocksize, blk64_t block) { - int count; - errcode_t err; + ext2_loff_t ret = 0; - if (sparse_offset) { -#ifdef HAVE_LSEEK64 - if (lseek64(fd, sparse_offset, SEEK_CUR) < 0) - perror("lseek"); -#else - if (lseek(fd, sparse_offset, SEEK_CUR) < 0) - perror("lseek"); -#endif - } - if (blocksize) { - count = write(fd, buf, blocksize); - if (count != blocksize) { - if (count == -1) - err = errno; - else - err = 0; - com_err(program_name, err, "error writing block %u", - block); - exit(1); - } - } + if (sparse_offset) + ret = ext2fs_llseek(fd, sparse_offset, SEEK_CUR); + + if (ret < 0) + lseek_error_and_exit(errno); + generic_write(fd, buf, blocksize, block); } int name_id[256]; #define EXT4_MAX_REC_LEN ((1<<16)-1) -static void scramble_dir_block(ext2_filsys fs, blk_t blk, char *buf) +static void scramble_dir_block(ext2_filsys fs, blk64_t blk, char *buf) { char *p, *end, *cp; struct ext2_dir_entry_2 *dirent; @@ -406,31 +466,30 @@ static void scramble_dir_block(ext2_filsys fs, blk_t blk, char *buf) static void output_meta_data_blocks(ext2_filsys fs, int fd) { errcode_t retval; - blk_t blk; + blk64_t blk; char *buf, *zero_buf; int sparse = 0; - buf = malloc(fs->blocksize); - if (!buf) { - com_err(program_name, ENOMEM, "while allocating buffer"); + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) { + com_err(program_name, retval, "while allocating buffer"); exit(1); } - zero_buf = malloc(fs->blocksize); - if (!zero_buf) { - com_err(program_name, ENOMEM, "while allocating buffer"); + retval = ext2fs_get_memzero(fs->blocksize, &zero_buf); + if (retval) { + com_err(program_name, retval, "while allocating buffer"); exit(1); } - memset(zero_buf, 0, fs->blocksize); - for (blk = 0; blk < fs->super->s_blocks_count; blk++) { + for (blk = 0; blk < ext2fs_blocks_count(fs->super); blk++) { if ((blk >= fs->super->s_first_data_block) && - ext2fs_test_block_bitmap(meta_block_map, blk)) { - retval = io_channel_read_blk(fs->io, blk, 1, buf); + ext2fs_test_block_bitmap2(meta_block_map, blk)) { + retval = io_channel_read_blk64(fs->io, blk, 1, buf); if (retval) { com_err(program_name, retval, - "error reading block %u", blk); + "error reading block %llu", blk); } if (scramble_block_map && - ext2fs_test_block_bitmap(scramble_block_map, blk)) + ext2fs_test_block_bitmap2(scramble_block_map, blk)) scramble_dir_block(fs, blk, buf); if ((fd != 1) && check_zero_block(buf, fs->blocksize)) goto sparse_write; @@ -445,6 +504,7 @@ static void output_meta_data_blocks(ext2_filsys fs, int fd) } sparse += fs->blocksize; if (sparse >= 1024*1024) { + write_block(fd, 0, sparse, 0, 0); sparse = 0; } @@ -452,11 +512,524 @@ static void output_meta_data_blocks(ext2_filsys fs, int fd) } if (sparse) write_block(fd, zero_buf, sparse-1, 1, -1); - free(zero_buf); - free(buf); + ext2fs_free_mem(&zero_buf); + ext2fs_free_mem(&buf); +} + +static void init_l1_table(struct ext2_super_block *sb, + struct ext2_qcow2_image *image) +{ + __u64 *l1_table; + errcode_t ret; + + ret = ext2fs_get_arrayzero(image->l1_size, sizeof(__u64), &l1_table); + if (ret) { + com_err(program_name, ret, "while allocating l1 table"); + exit(1); + } + + image->l1_table = l1_table; +} + +static void init_l2_cache(struct ext2_qcow2_image *image) +{ + unsigned int count, i; + struct ext2_qcow2_l2_cache *cache; + struct ext2_qcow2_l2_table *table; + errcode_t ret; + + ret = ext2fs_get_arrayzero(1, sizeof(struct ext2_qcow2_l2_cache), + &cache); + if (ret) + goto alloc_err; + + count = (image->l1_size > L2_CACHE_PREALLOC) ? L2_CACHE_PREALLOC : + image->l1_size; + + cache->count = count; + cache->free = count; + cache->next_offset = image->l2_offset; + + for (i = 0; i < count; i++) { + ret = ext2fs_get_arrayzero(1, + sizeof(struct ext2_qcow2_l2_table), &table); + if (ret) + goto alloc_err; + + ret = ext2fs_get_arrayzero(image->l2_size, + sizeof(__u64), &table->data); + if (ret) + goto alloc_err; + + table->next = cache->free_head; + cache->free_head = table; + } + + image->l2_cache = cache; + return; + +alloc_err: + com_err(program_name, ret, "while allocating l2 cache"); + exit(1); +} + +static void put_l2_cache(struct ext2_qcow2_image *image) +{ + struct ext2_qcow2_l2_cache *cache = image->l2_cache; + struct ext2_qcow2_l2_table *tmp, *table; + + if (!cache) + return; + + table = cache->free_head; + cache->free_head = NULL; +again: + while (table) { + tmp = table; + table = table->next; + ext2fs_free_mem(&tmp->data); + ext2fs_free_mem(&tmp); + } + + if (cache->free != cache->count) { + fprintf(stderr, "Warning: There are still tables in the " + "cache while putting the cache, data will " + "be lost so the image may not be valid.\n"); + table = cache->used_head; + cache->used_head = NULL; + goto again; + } + + ext2fs_free_mem(&cache); +} + +static int init_refcount(struct ext2_qcow2_image *img, blk64_t table_offset) +{ + struct ext2_qcow2_refcount *ref; + blk64_t table_clusters; + errcode_t ret; + + ref = &(img->refcount); + + /* + * One refcount block addresses 2048 clusters, one refcount table + * addresses cluster/sizeof(__u64) refcount blocks, and we need + * to address meta_blocks_count clusters + qcow2 metadata clusters + * in the worst case. + */ + table_clusters = meta_blocks_count + (table_offset >> + img->cluster_bits); + table_clusters >>= (img->cluster_bits + 6 - 1); + table_clusters = (table_clusters == 0) ? 1 : table_clusters; + + ref->refcount_table_offset = table_offset; + ref->refcount_table_clusters = table_clusters; + ref->refcount_table_index = 0; + ref->refcount_block_index = 0; + + /* Allocate refcount table */ + ret = ext2fs_get_arrayzero(ref->refcount_table_clusters, + img->cluster_size, &ref->refcount_table); + if (ret) + return ret; + + /* Allocate refcount block */ + ret = ext2fs_get_arrayzero(1, img->cluster_size, &ref->refcount_block); + if (ret) + ext2fs_free_mem(&ref->refcount_table); + + return ret; +} + +static int initialize_qcow2_image(int fd, ext2_filsys fs, + struct ext2_qcow2_image *image) +{ + struct ext2_qcow2_hdr *header; + blk64_t total_size, offset; + int shift, l2_bits, header_size, l1_size, ret; + int cluster_bits = get_bits_from_size(fs->blocksize); + struct ext2_super_block *sb = fs->super; + + /* Allocate header */ + ret = ext2fs_get_memzero(sizeof(struct ext2_qcow2_hdr), &header); + if (ret) + return ret; + + total_size = ext2fs_blocks_count(sb) << cluster_bits; + image->cluster_size = fs->blocksize; + image->l2_size = 1 << (cluster_bits - 3); + image->cluster_bits = cluster_bits; + image->fd = fd; + + header->magic = ext2fs_cpu_to_be32(QCOW_MAGIC); + header->version = ext2fs_cpu_to_be32(QCOW_VERSION); + header->size = ext2fs_cpu_to_be64(total_size); + header->cluster_bits = ext2fs_cpu_to_be32(cluster_bits); + + header_size = (sizeof(struct ext2_qcow2_hdr) + 7) & ~7; + offset = align_offset(header_size, image->cluster_size); + + header->l1_table_offset = ext2fs_cpu_to_be64(offset); + image->l1_offset = offset; + + l2_bits = cluster_bits - 3; + shift = cluster_bits + l2_bits; + l1_size = ((total_size + (1LL << shift) - 1) >> shift); + header->l1_size = ext2fs_cpu_to_be32(l1_size); + image->l1_size = l1_size; + + /* Make space for L1 table */ + offset += align_offset(l1_size * sizeof(blk64_t), image->cluster_size); + + /* Initialize refcounting */ + ret = init_refcount(image, offset); + if (ret) { + ext2fs_free_mem(&header); + return ret; + } + header->refcount_table_offset = ext2fs_cpu_to_be64(offset); + header->refcount_table_clusters = + ext2fs_cpu_to_be32(image->refcount.refcount_table_clusters); + offset += image->cluster_size; + offset += image->refcount.refcount_table_clusters << + image->cluster_bits; + + /* Make space for L2 tables */ + image->l2_offset = offset; + offset += image->cluster_size; + + /* Make space for first refcount block */ + image->refcount.refcount_block_offset = offset; + + image->hdr = header; + /* Initialize l1 and l2 tables */ + init_l1_table(sb, image); + init_l2_cache(image); + + return 0; +} + +static void free_qcow2_image(struct ext2_qcow2_image *img) +{ + if (!img) + return; + + if (img->hdr) + ext2fs_free_mem(&img->hdr); + + if (img->l1_table) + ext2fs_free_mem(&img->l1_table); + + if (img->refcount.refcount_table) + ext2fs_free_mem(&img->refcount.refcount_table); + if (img->refcount.refcount_block) + ext2fs_free_mem(&img->refcount.refcount_block); + + put_l2_cache(img); + + ext2fs_free_mem(&img); +} + +/** + * Put table from used list (used_head) into free list (free_head). + * l2_table is used to return pointer to the next used table (used_head). + */ +static void put_used_table(struct ext2_qcow2_image *img, + struct ext2_qcow2_l2_table **l2_table) +{ + struct ext2_qcow2_l2_cache *cache = img->l2_cache; + struct ext2_qcow2_l2_table *table; + + table = cache->used_head; + cache->used_head = table->next; + + assert(table); + if (!table->next) + cache->used_tail = NULL; + + /* Clean the table for case we will need to use it again */ + memset(table->data, 0, img->cluster_size); + table->next = cache->free_head; + cache->free_head = table; + + cache->free++; + + *l2_table = cache->used_head; +} + +static void flush_l2_cache(struct ext2_qcow2_image *image) +{ + blk64_t offset, seek = 0; + struct ext2_qcow2_l2_cache *cache = image->l2_cache; + struct ext2_qcow2_l2_table *table = cache->used_head; + int fd = image->fd; + + /* Store current position */ + if ((offset = ext2fs_llseek(fd, 0, SEEK_CUR)) < 0) + lseek_error_and_exit(errno); + + assert(table); + while (cache->free < cache->count) { + if (seek != table->offset) { + if (ext2fs_llseek(fd, table->offset, SEEK_SET) < 0) + lseek_error_and_exit(errno); + seek = table->offset; + } + + generic_write(fd, (char *)table->data, image->cluster_size , 0); + put_used_table(image, &table); + seek += image->cluster_size; + } + + /* Restore previous position */ + if (ext2fs_llseek(fd, offset, SEEK_SET) < 0) + lseek_error_and_exit(errno); +} + +/** + * Get first free table (from free_head) and put it into tail of used list + * (to used_tail). + * l2_table is used to return pointer to moved table. + * Returns 1 if the cache is full, 0 otherwise. + */ +static void get_free_table(struct ext2_qcow2_image *image, + struct ext2_qcow2_l2_table **l2_table) +{ + struct ext2_qcow2_l2_table *table; + struct ext2_qcow2_l2_cache *cache = image->l2_cache; + + if (0 == cache->free) + flush_l2_cache(image); + + table = cache->free_head; + assert(table); + cache->free_head = table->next; + + if (cache->used_tail) + cache->used_tail->next = table; + else + /* First item in the used list */ + cache->used_head = table; + + cache->used_tail = table; + cache->free--; + + *l2_table = table; +} + +static int add_l2_item(struct ext2_qcow2_image *img, blk64_t blk, + blk64_t data, blk64_t next) +{ + struct ext2_qcow2_l2_cache *cache = img->l2_cache; + struct ext2_qcow2_l2_table *table = cache->used_tail; + blk64_t l1_index = blk / img->l2_size; + blk64_t l2_index = blk & (img->l2_size - 1); + int ret = 0; + + /* + * Need to create new table if it does not exist, + * or if it is full + */ + if (!table || (table->l1_index != l1_index)) { + get_free_table(img, &table); + table->l1_index = l1_index; + table->offset = cache->next_offset; + cache->next_offset = next; + img->l1_table[l1_index] = + ext2fs_cpu_to_be64(table->offset | QCOW_OFLAG_COPIED); + ret++; + } + + table->data[l2_index] = ext2fs_cpu_to_be64(data | QCOW_OFLAG_COPIED); + return ret; +} + +static int update_refcount(int fd, struct ext2_qcow2_image *img, + blk64_t offset, blk64_t rfblk_pos) +{ + struct ext2_qcow2_refcount *ref; + __u32 table_index; + int ret = 0; + + ref = &(img->refcount); + table_index = offset >> (2 * img->cluster_bits - 1); + + /* + * Need to create new refcount block when the offset addresses + * another item in the refcount table + */ + if (table_index != ref->refcount_table_index) { + + if (ext2fs_llseek(fd, ref->refcount_block_offset, SEEK_SET) < 0) + lseek_error_and_exit(errno); + + generic_write(fd, (char *)ref->refcount_block, + img->cluster_size, 0); + memset(ref->refcount_block, 0, img->cluster_size); + + ref->refcount_table[ref->refcount_table_index] = + ext2fs_cpu_to_be64(ref->refcount_block_offset); + ref->refcount_block_offset = rfblk_pos; + ref->refcount_block_index = 0; + ref->refcount_table_index = table_index; + ret++; + } + + /* + * We are relying on the fact that we are creating the qcow2 + * image sequentially, hence we will always allocate refcount + * block items sequentialy. + */ + ref->refcount_block[ref->refcount_block_index] = ext2fs_cpu_to_be16(1); + ref->refcount_block_index++; + return ret; +} + +static int sync_refcount(int fd, struct ext2_qcow2_image *img) +{ + struct ext2_qcow2_refcount *ref; + + ref = &(img->refcount); + + ref->refcount_table[ref->refcount_table_index] = + ext2fs_cpu_to_be64(ref->refcount_block_offset); + if (ext2fs_llseek(fd, ref->refcount_table_offset, SEEK_SET) < 0) + lseek_error_and_exit(errno); + generic_write(fd, (char *)ref->refcount_table, + ref->refcount_table_clusters << img->cluster_bits, 0); + + if (ext2fs_llseek(fd, ref->refcount_block_offset, SEEK_SET) < 0) + lseek_error_and_exit(errno); + generic_write(fd, (char *)ref->refcount_block, img->cluster_size, 0); + return 0; } -static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag) +static void output_qcow2_meta_data_blocks(ext2_filsys fs, int fd) +{ + errcode_t retval; + blk64_t blk, offset, size, end; + char *buf; + struct ext2_qcow2_image *img; + unsigned int header_size; + + /* allocate struct ext2_qcow2_image */ + retval = ext2fs_get_mem(sizeof(struct ext2_qcow2_image), &img); + if (retval) { + com_err(program_name, retval, + "while allocating ext2_qcow2_image"); + exit(1); + } + + retval = initialize_qcow2_image(fd, fs, img); + if (retval) { + com_err(program_name, retval, + "while initializing ext2_qcow2_image"); + exit(1); + } + header_size = align_offset(sizeof(struct ext2_qcow2_hdr), + img->cluster_size); + write_header(fd, img->hdr, sizeof(struct ext2_qcow2_hdr), header_size); + + /* Refcount all qcow2 related metadata up to refcount_block_offset */ + end = img->refcount.refcount_block_offset; + if (ext2fs_llseek(fd, end, SEEK_SET) < 0) + lseek_error_and_exit(errno); + blk = end + img->cluster_size; + for (offset = 0; offset <= end; offset += img->cluster_size) { + if (update_refcount(fd, img, offset, blk)) { + blk += img->cluster_size; + /* + * If we create new refcount block, we need to refcount + * it as well. + */ + end += img->cluster_size; + } + } + if (ext2fs_llseek(fd, offset, SEEK_SET) < 0) + lseek_error_and_exit(errno); + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) { + com_err(program_name, retval, "while allocating buffer"); + exit(1); + } + /* Write qcow2 data blocks */ + for (blk = 0; blk < ext2fs_blocks_count(fs->super); blk++) { + if ((blk >= fs->super->s_first_data_block) && + ext2fs_test_block_bitmap2(meta_block_map, blk)) { + retval = io_channel_read_blk64(fs->io, blk, 1, buf); + if (retval) { + com_err(program_name, retval, + "error reading block %llu", blk); + continue; + } + if (scramble_block_map && + ext2fs_test_block_bitmap2(scramble_block_map, blk)) + scramble_dir_block(fs, blk, buf); + if (check_zero_block(buf, fs->blocksize)) + continue; + + if (update_refcount(fd, img, offset, offset)) { + /* Make space for another refcount block */ + offset += img->cluster_size; + if (ext2fs_llseek(fd, offset, SEEK_SET) < 0) + lseek_error_and_exit(errno); + /* + * We have created the new refcount block, this + * means that we need to refcount it as well. + * So the previous update_refcount refcounted + * the block itself and now we are going to + * create refcount for data. New refcount + * block should not be created! + */ + if (update_refcount(fd, img, offset, offset)) { + fprintf(stderr, "Programming error: " + "multiple sequential refcount " + "blocks created!\n"); + exit(1); + } + } + + generic_write(fd, buf, fs->blocksize, 0); + + if (add_l2_item(img, blk, offset, + offset + img->cluster_size)) { + offset += img->cluster_size; + if (update_refcount(fd, img, offset, + offset + img->cluster_size)) { + offset += img->cluster_size; + if (update_refcount(fd, img, offset, + offset)) { + fprintf(stderr, + "Programming error: multiple sequential refcount " + "blocks created!\n"); + exit(1); + } + } + offset += img->cluster_size; + if (ext2fs_llseek(fd, offset, SEEK_SET) < 0) + lseek_error_and_exit(errno); + continue; + } + + offset += img->cluster_size; + } + } + update_refcount(fd, img, offset, offset); + flush_l2_cache(img); + sync_refcount(fd, img); + + /* Write l1_table*/ + if (ext2fs_llseek(fd, img->l1_offset, SEEK_SET) < 0) + lseek_error_and_exit(errno); + size = img->l1_size * sizeof(__u64); + generic_write(fd, (char *)img->l1_table, size, 0); + + ext2fs_free_mem(&buf); + free_qcow2_image(img); +} + +static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags) { struct process_block_struct pb; struct ext2_inode inode; @@ -465,6 +1038,7 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag) errcode_t retval; char * block_buf; + meta_blocks_count = 0; retval = ext2fs_allocate_block_bitmap(fs, "in-use block map", &meta_block_map); if (retval) { @@ -472,7 +1046,7 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag) exit(1); } - if (scramble_flag) { + if (flags & E2IMAGE_SCRAMBLE_FLAG) { retval = ext2fs_allocate_block_bitmap(fs, "scramble block map", &scramble_block_map); if (retval) { @@ -490,8 +1064,8 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag) exit(1); } - block_buf = malloc(fs->blocksize * 3); - if (!block_buf) { + retval = ext2fs_get_mem(fs->blocksize * 3, &block_buf); + if (retval) { com_err(program_name, 0, "Can't allocate block buffer"); exit(1); } @@ -511,9 +1085,10 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag) break; if (!inode.i_links_count) continue; - if (inode.i_file_acl) { - ext2fs_mark_block_bitmap(meta_block_map, - inode.i_file_acl); + if (ext2fs_file_acl_block(&inode)) { + ext2fs_mark_block_bitmap2(meta_block_map, + ext2fs_file_acl_block(&inode)); + meta_blocks_count++; } if (!ext2fs_inode_has_valid_blocks(&inode)) continue; @@ -525,7 +1100,7 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag) (LINUX_S_ISLNK(inode.i_mode) && ext2fs_inode_has_valid_blocks(&inode)) || ino == fs->super->s_journal_inum) { - retval = ext2fs_block_iterate2(fs, ino, + retval = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_READ_ONLY, block_buf, process_dir_block, &pb); if (retval) { @@ -539,7 +1114,7 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag) inode.i_block[EXT2_IND_BLOCK] || inode.i_block[EXT2_DIND_BLOCK] || inode.i_block[EXT2_TIND_BLOCK]) { - retval = ext2fs_block_iterate2(fs, + retval = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_READ_ONLY, block_buf, process_file_block, &pb); if (retval) { @@ -551,21 +1126,31 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag) } } use_inode_shortcuts(fs, 0); - output_meta_data_blocks(fs, fd); - free(block_buf); + + if (type & E2IMAGE_QCOW2) + output_qcow2_meta_data_blocks(fs, fd); + else + output_meta_data_blocks(fs, fd); + + ext2fs_free_mem(&block_buf); + ext2fs_close_inode_scan(scan); + ext2fs_free_block_bitmap(meta_block_map); + if (type & E2IMAGE_SCRAMBLE_FLAG) + ext2fs_free_block_bitmap(scramble_block_map); } -static void install_image(char *device, char *image_fn, int raw_flag) +static void install_image(char *device, char *image_fn, int type) { errcode_t retval; ext2_filsys fs; int open_flag = EXT2_FLAG_IMAGE_FILE; int fd = 0; io_manager io_ptr; - io_channel io, image_io; + io_channel io; - if (raw_flag) { - com_err(program_name, 0, "Raw images cannot be installed"); + if (type) { + com_err(program_name, 0, "Raw and qcow2 images cannot" + "be installed"); exit(1); } @@ -607,12 +1192,10 @@ static void install_image(char *device, char *image_fn, int raw_flag) exit(1); } - image_io = fs->io; - ext2fs_rewrite_to_io(fs, io); - if (lseek(fd, fs->image_header->offset_inode, SEEK_SET) < 0) { - perror("lseek"); + if (ext2fs_llseek(fd, fs->image_header->offset_inode, SEEK_SET) < 0) { + perror("ext2fs_llseek"); exit(1); } @@ -626,17 +1209,33 @@ static void install_image(char *device, char *image_fn, int raw_flag) exit (0); } +static struct ext2_qcow2_hdr *check_qcow2_image(int *fd, char *name) +{ + +#ifdef HAVE_OPEN64 + *fd = open64(name, O_RDONLY, 0600); +#else + *fd = open(name, O_RDONLY, 0600); +#endif + if (*fd < 0) + return NULL; + + return qcow2_read_header(*fd); +} + int main (int argc, char ** argv) { int c; errcode_t retval; ext2_filsys fs; char *image_fn; - int open_flag = 0; - int raw_flag = 0; - int install_flag = 0; - int scramble_flag = 0; + struct ext2_qcow2_hdr *header = NULL; + int open_flag = EXT2_FLAG_64BITS; + int img_type = 0; + int flags = 0; + int qcow2_fd = 0; int fd = 0; + int ret = 0; #ifdef ENABLE_NLS setlocale(LC_MESSAGES, ""); @@ -649,16 +1248,23 @@ int main (int argc, char ** argv) if (argc && *argv) program_name = *argv; add_error_table(&et_ext2_error_table); - while ((c = getopt (argc, argv, "rsI")) != EOF) + while ((c = getopt(argc, argv, "rsIQ")) != EOF) switch (c) { + case 'I': + flags |= E2IMAGE_INSTALL_FLAG; + break; + case 'Q': + if (img_type) + usage(); + img_type |= E2IMAGE_QCOW2; + break; case 'r': - raw_flag++; + if (img_type) + usage(); + img_type |= E2IMAGE_RAW; break; case 's': - scramble_flag++; - break; - case 'I': - install_flag++; + flags |= E2IMAGE_SCRAMBLE_FLAG; break; default: usage(); @@ -668,11 +1274,19 @@ int main (int argc, char ** argv) device_name = argv[optind]; image_fn = argv[optind+1]; - if (install_flag) { - install_image(device_name, image_fn, raw_flag); + if (flags & E2IMAGE_INSTALL_FLAG) { + install_image(device_name, image_fn, img_type); exit (0); } + if (img_type & E2IMAGE_RAW) { + header = check_qcow2_image(&qcow2_fd, device_name); + if (header) { + flags |= E2IMAGE_IS_QCOW2_FLAG; + goto skip_device; + } + } + retval = ext2fs_open (device_name, open_flag, 0, 0, unix_io_manager, &fs); if (retval) { @@ -682,6 +1296,7 @@ int main (int argc, char ** argv) exit(1); } +skip_device: if (strcmp(image_fn, "-") == 0) fd = 1; else { @@ -697,12 +1312,41 @@ int main (int argc, char ** argv) } } - if (raw_flag) - write_raw_image_file(fs, fd, scramble_flag); + if ((img_type & E2IMAGE_QCOW2) && (fd == 1)) { + com_err(program_name, 0, "QCOW2 image can not be written to " + "the stdout!\n"); + exit(1); + } + + if (flags & E2IMAGE_IS_QCOW2_FLAG) { + ret = qcow2_write_raw_image(qcow2_fd, fd, header); + if (ret) { + if (ret == -QCOW_COMPRESSED) + fprintf(stderr, "Image (%s) is compressed\n", + image_fn); + if (ret == -QCOW_ENCRYPTED) + fprintf(stderr, "Image (%s) is encrypted\n", + image_fn); + com_err(program_name, ret, + _("while trying to convert qcow2 image" + " (%s) into raw image (%s)"), + device_name, image_fn); + } + goto out; + } + + + if (img_type) + write_raw_image_file(fs, fd, img_type, flags); else write_image_file(fs, fd); ext2fs_close (fs); +out: + if (header) + free(header); + if (qcow2_fd) + close(qcow2_fd); remove_error_table(&et_ext2_error_table); - exit (0); + return ret; } diff --git a/misc/e2undo.c b/misc/e2undo.c index 2c9d5364..04d0b170 100644 --- a/misc/e2undo.c +++ b/misc/e2undo.c @@ -29,12 +29,11 @@ unsigned char blksize_key[] = "filesystem BLKSIZE"; char *prg_name; -static void usage(char *prg_name) +static void usage(void) { fprintf(stderr, _("Usage: %s <transaction file> <filesystem>\n"), prg_name); exit(1); - } static int check_filesystem(TDB_CONTEXT *tdb, io_channel channel) @@ -46,7 +45,7 @@ static int check_filesystem(TDB_CONTEXT *tdb, io_channel channel) struct ext2_super_block super; io_channel_set_blksize(channel, SUPERBLOCK_OFFSET); - retval = io_channel_read_blk(channel, 1, -SUPERBLOCK_SIZE, &super); + retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super); if (retval) { com_err(prg_name, retval, _("Failed to read the file system data \n")); @@ -126,7 +125,7 @@ int main(int argc, char *argv[]) io_channel channel; errcode_t retval; int mount_flags; - unsigned long blk_num; + blk64_t blk_num; char *device_name, *tdb_file; io_manager manager = unix_io_manager; @@ -145,12 +144,12 @@ int main(int argc, char *argv[]) force = 1; break; default: - usage(prg_name); + usage(); } } - if (argc != optind+2) - usage(prg_name); + if (argc != optind + 2) + usage(); tdb_file = argv[optind]; device_name = argv[optind+1]; @@ -206,9 +205,9 @@ int main(int argc, char *argv[]) exit(1); } blk_num = *(unsigned long *)key.dptr; - printf(_("Replayed transaction of size %zd at location %ld\n"), + printf(_("Replayed transaction of size %zd at location %llu\n"), data.dsize, blk_num); - retval = io_channel_write_blk(channel, blk_num, + retval = io_channel_write_blk64(channel, blk_num, -data.dsize, data.dptr); if (retval == -1) { com_err(prg_name, retval, diff --git a/misc/e4defrag.8.in b/misc/e4defrag.8.in new file mode 100644 index 00000000..75e1bc9f --- /dev/null +++ b/misc/e4defrag.8.in @@ -0,0 +1,80 @@ +.TH E4DEFRAG 8 "May 2009" "e4defrag version 2.0" +.SH NAME +e4defrag \- online defragmenter for ext4 filesystem +.SH SYNOPSIS +.B e4defrag +[ +.B \-c +] +[ +.B \-v +] +.I target +\&... +.SH DESCRIPTION +.B e4defrag +reduces fragmentation of extent based file. The file targeted by +.B e4defrag +is created on ext4 filesystem made with "-O extent" option (see +.BR mke2fs (8)). +The targeted file gets more contiguous blocks and improves the file access +speed. +.PP +.I target +is a regular file, a directory, or a device that is mounted as ext4 filesystem. +If +.I target +is a directory, +.B e4defrag +reduces fragmentation of all files in it. If +.I target +is a device, +.B e4defrag +gets the mount point of it and reduces fragmentation of all files in this mount +point. +.SH OPTIONS +.TP +.B \-c +Get a current fragmentation count and an ideal fragmentation count, and +calculate fragmentation score based on them. By seeing this score, we can +determine whether we should execute +.B e4defrag +to +.IR target . +When used with +.B \-v +option, the current fragmentation count and the ideal fragmentation count are +printed for each file. +.IP +Also this option outputs the average data size in one extent. If you see it, +you'll find the file has ideal extents or not. Note that the maximum extent +size is 131072KB in ext4 filesystem (if block size is 4KB). +.IP +If this option is specified, +.I target +is never defragmented. +.TP +.B \-v +Print error messages and the fragmentation count before and after defrag for +each file. +.SH NOTES +.B e4defrag +does not support swap file, files in lost+found directory, and files allocated +in indirect blocks. When +.I target +is a device or a mount point, +.B e4defrag +doesn't defragment files in mount point of other device. +.PP +Non-privileged users can execute +.B e4defrag +to their own file, but the score is not printed if +.B \-c +option is specified. Therefore, it is desirable to be executed by root user. +.SH AUTHOR +Written by Akira Fujita <a-fujita@rs.jp.nec.com> and Takashi Sato +<t-sato@yk.jp.nec.com>. +.SH SEE ALSO +.BR mke2fs (8), +.BR mount (8). + diff --git a/misc/e4defrag.c b/misc/e4defrag.c new file mode 100644 index 00000000..eea30571 --- /dev/null +++ b/misc/e4defrag.c @@ -0,0 +1,2051 @@ +/* + * e4defrag.c - ext4 filesystem defragmenter + * + * Copyright (C) 2009 NEC Software Tohoku, Ltd. + * + * Author: Akira Fujita <a-fujita@rs.jp.nec.com> + * Takashi Sato <t-sato@yk.jp.nec.com> + */ + +#ifndef _LARGEFILE_SOURCE +#define _LARGEFILE_SOURCE +#endif + +#ifndef _LARGEFILE64_SOURCE +#define _LARGEFILE64_SOURCE +#endif + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <ctype.h> +#include <dirent.h> +#include <endian.h> +#include <errno.h> +#include <fcntl.h> +#include <ftw.h> +#include <limits.h> +#include <mntent.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ext2fs/ext2_types.h> +#include <ext2fs/ext2fs.h> +#include <linux/fs.h> +#include <sys/ioctl.h> +#include <ext2fs/fiemap.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/statfs.h> +#include <sys/syscall.h> +#include <sys/vfs.h> + +/* A relatively new ioctl interface ... */ +#ifndef EXT4_IOC_MOVE_EXT +#define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent) +#endif + +/* Macro functions */ +#define PRINT_ERR_MSG(msg) fprintf(stderr, "%s\n", (msg)) +#define IN_FTW_PRINT_ERR_MSG(msg) \ + fprintf(stderr, "\t%s\t\t[ NG ]\n", (msg)) +#define PRINT_FILE_NAME(file) fprintf(stderr, " \"%s\"\n", (file)) +#define PRINT_ERR_MSG_WITH_ERRNO(msg) \ + fprintf(stderr, "\t%s:%s\t[ NG ]\n", (msg), strerror(errno)) +#define STATISTIC_ERR_MSG(msg) \ + fprintf(stderr, "\t%s\n", (msg)) +#define STATISTIC_ERR_MSG_WITH_ERRNO(msg) \ + fprintf(stderr, "\t%s:%s\n", (msg), strerror(errno)) +#define min(x, y) (((x) > (y)) ? (y) : (x)) +#define CALC_SCORE(ratio) \ + ((ratio) > 10 ? (80 + 20 * (ratio) / 100) : (8 * (ratio))) +/* Wrap up the free function */ +#define FREE(tmp) \ + do { \ + if ((tmp) != NULL) \ + free(tmp); \ + } while (0) \ +/* Insert list2 after list1 */ +#define insert(list1, list2) \ + do { \ + list2->next = list1->next; \ + list1->next->prev = list2; \ + list2->prev = list1; \ + list1->next = list2; \ + } while (0) + +/* To delete unused warning */ +#ifdef __GNUC__ +#define EXT2FS_ATTR(x) __attribute__(x) +#else +#define EXT2FS_ATTR(x) +#endif + +/* The mode of defrag */ +#define DETAIL 0x01 +#define STATISTIC 0x02 + +#define DEVNAME 0 +#define DIRNAME 1 +#define FILENAME 2 + +#define FTW_OPEN_FD 2000 + +#define FS_EXT4 "ext4" +#define ROOT_UID 0 + +#define BOUND_SCORE 55 +#define SHOW_FRAG_FILES 5 + +/* Magic number for ext4 */ +#define EXT4_SUPER_MAGIC 0xEF53 + +/* Definition of flex_bg */ +#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 + +/* The following macro is used for ioctl FS_IOC_FIEMAP + * EXTENT_MAX_COUNT: the maximum number of extents for exchanging between + * kernel-space and user-space per ioctl + */ +#define EXTENT_MAX_COUNT 512 + +/* The following macros are error message */ +#define MSG_USAGE \ +"Usage : e4defrag [-v] file...| directory...| device...\n\ + : e4defrag -c file...| directory...| device...\n" + +#define NGMSG_EXT4 "Filesystem is not ext4 filesystem" +#define NGMSG_FILE_EXTENT "Failed to get file extents" +#define NGMSG_FILE_INFO "Failed to get file information" +#define NGMSG_FILE_OPEN "Failed to open" +#define NGMSG_FILE_UNREG "File is not regular file" +#define NGMSG_LOST_FOUND "Can not process \"lost+found\"" + +/* Data type for filesystem-wide blocks number */ +typedef unsigned long long ext4_fsblk_t; + +struct fiemap_extent_data { + __u64 len; /* blocks count */ + __u64 logical; /* start logical block number */ + ext4_fsblk_t physical; /* start physical block number */ +}; + +struct fiemap_extent_list { + struct fiemap_extent_list *prev; + struct fiemap_extent_list *next; + struct fiemap_extent_data data; /* extent belong to file */ +}; + +struct fiemap_extent_group { + struct fiemap_extent_group *prev; + struct fiemap_extent_group *next; + __u64 len; /* length of this continuous region */ + struct fiemap_extent_list *start; /* start ext */ + struct fiemap_extent_list *end; /* end ext */ +}; + +struct move_extent { + __s32 reserved; /* original file descriptor */ + __u32 donor_fd; /* donor file descriptor */ + __u64 orig_start; /* logical start offset in block for orig */ + __u64 donor_start; /* logical start offset in block for donor */ + __u64 len; /* block length to be moved */ + __u64 moved_len; /* moved block length */ +}; + +struct frag_statistic_ino { + int now_count; /* the file's extents count of before defrag */ + int best_count; /* the best file's extents count */ + __u64 size_per_ext; /* size(KB) per extent */ + float ratio; /* the ratio of fragmentation */ + char msg_buffer[PATH_MAX + 1]; /* pathname of the file */ +}; + +char lost_found_dir[PATH_MAX + 1]; +int block_size; +int extents_before_defrag; +int extents_after_defrag; +int mode_flag; +unsigned int current_uid; +unsigned int defraged_file_count; +unsigned int frag_files_before_defrag; +unsigned int frag_files_after_defrag; +unsigned int regular_count; +unsigned int succeed_cnt; +unsigned int total_count; +__u8 log_groups_per_flex; +__u32 blocks_per_group; +__u32 feature_incompat; +ext4_fsblk_t files_block_count; +struct frag_statistic_ino frag_rank[SHOW_FRAG_FILES]; + + +/* Local definitions of some syscalls glibc may not yet have */ + +#ifndef HAVE_POSIX_FADVISE +#warning Using locally defined posix_fadvise interface. + +#ifndef __NR_fadvise64_64 +#error Your kernel headers dont define __NR_fadvise64_64 +#endif + +/* + * fadvise() - Give advice about file access. + * + * @fd: defrag target file's descriptor. + * @offset: file offset. + * @len: area length. + * @advise: process flag. + */ +static int posix_fadvise(int fd, loff_t offset, size_t len, int advise) +{ + return syscall(__NR_fadvise64_64, fd, offset, len, advise); +} +#endif /* ! HAVE_FADVISE64_64 */ + +#ifndef HAVE_SYNC_FILE_RANGE +#warning Using locally defined sync_file_range interface. + +#ifndef __NR_sync_file_range +#ifndef __NR_sync_file_range2 /* ppc */ +#error Your kernel headers dont define __NR_sync_file_range +#endif +#endif + +/* + * sync_file_range() - Sync file region. + * + * @fd: defrag target file's descriptor. + * @offset: file offset. + * @length: area length. + * @flag: process flag. + */ +int sync_file_range(int fd, loff_t offset, loff_t length, unsigned int flag) +{ +#ifdef __NR_sync_file_range + return syscall(__NR_sync_file_range, fd, offset, length, flag); +#else + return syscall(__NR_sync_file_range2, fd, flag, offset, length); +#endif +} +#endif /* ! HAVE_SYNC_FILE_RANGE */ + +#ifndef HAVE_FALLOCATE64 +#warning Using locally defined fallocate syscall interface. + +#ifndef __NR_fallocate +#error Your kernel headers dont define __NR_fallocate +#endif + +/* + * fallocate64() - Manipulate file space. + * + * @fd: defrag target file's descriptor. + * @mode: process flag. + * @offset: file offset. + * @len: file size. + */ +static int fallocate64(int fd, int mode, loff_t offset, loff_t len) +{ + return syscall(__NR_fallocate, fd, mode, offset, len); +} +#endif /* ! HAVE_FALLOCATE */ + +/* + * get_mount_point() - Get device's mount point. + * + * @devname: the device's name. + * @mount_point: the mount point. + * @dir_path_len: the length of directory. + */ +static int get_mount_point(const char *devname, char *mount_point, + int dir_path_len) +{ + /* Refer to /etc/mtab */ + const char *mtab = MOUNTED; + FILE *fp = NULL; + struct mntent *mnt = NULL; + + fp = setmntent(mtab, "r"); + if (fp == NULL) { + perror("Couldn't access /etc/mtab"); + return -1; + } + + while ((mnt = getmntent(fp)) != NULL) { + if (strcmp(devname, mnt->mnt_fsname) != 0) + continue; + + endmntent(fp); + if (strcmp(mnt->mnt_type, FS_EXT4) == 0) { + strncpy(mount_point, mnt->mnt_dir, + dir_path_len); + return 0; + } + PRINT_ERR_MSG(NGMSG_EXT4); + return -1; + } + endmntent(fp); + PRINT_ERR_MSG("Filesystem is not mounted"); + return -1; +} + +/* + * is_ext4() - Whether on an ext4 filesystem. + * + * @file: the file's name. + */ +static int is_ext4(const char *file, char *devname) +{ + int maxlen = 0; + int len, ret; + FILE *fp = NULL; + char *mnt_type = NULL; + /* Refer to /etc/mtab */ + const char *mtab = MOUNTED; + char file_path[PATH_MAX + 1]; + struct mntent *mnt = NULL; + struct statfs64 fsbuf; + + /* Get full path */ + if (realpath(file, file_path) == NULL) { + perror("Couldn't get full path"); + PRINT_FILE_NAME(file); + return -1; + } + + if (statfs64(file_path, &fsbuf) < 0) { + perror("Failed to get filesystem information"); + PRINT_FILE_NAME(file); + return -1; + } + + if (fsbuf.f_type != EXT4_SUPER_MAGIC) { + PRINT_ERR_MSG(NGMSG_EXT4); + return -1; + } + + fp = setmntent(mtab, "r"); + if (fp == NULL) { + perror("Couldn't access /etc/mtab"); + return -1; + } + + while ((mnt = getmntent(fp)) != NULL) { + if (mnt->mnt_fsname[0] != '/') + continue; + len = strlen(mnt->mnt_dir); + ret = memcmp(file_path, mnt->mnt_dir, len); + if (ret != 0) + continue; + + if (maxlen >= len) + continue; + + maxlen = len; + + mnt_type = realloc(mnt_type, strlen(mnt->mnt_type) + 1); + if (mnt_type == NULL) { + endmntent(fp); + return -1; + } + memset(mnt_type, 0, strlen(mnt->mnt_type) + 1); + strncpy(mnt_type, mnt->mnt_type, strlen(mnt->mnt_type)); + strncpy(lost_found_dir, mnt->mnt_dir, PATH_MAX); + strncpy(devname, mnt->mnt_fsname, strlen(mnt->mnt_fsname) + 1); + } + + endmntent(fp); + if (strcmp(mnt_type, FS_EXT4) == 0) { + FREE(mnt_type); + return 0; + } else { + FREE(mnt_type); + PRINT_ERR_MSG(NGMSG_EXT4); + return -1; + } +} + +/* + * calc_entry_counts() - Calculate file counts. + * + * @file: file name. + * @buf: file info. + * @flag: file type. + * @ftwbuf: the pointer of a struct FTW. + */ +static int calc_entry_counts(const char *file EXT2FS_ATTR((unused)), + const struct stat64 *buf, int flag EXT2FS_ATTR((unused)), + struct FTW *ftwbuf EXT2FS_ATTR((unused))) +{ + if (S_ISREG(buf->st_mode)) + regular_count++; + + total_count++; + + return 0; +} + +/* + * page_in_core() - Get information on whether pages are in core. + * + * @fd: defrag target file's descriptor. + * @defrag_data: data used for defrag. + * @vec: page state array. + * @page_num: page number. + */ +static int page_in_core(int fd, struct move_extent defrag_data, + unsigned char **vec, unsigned int *page_num) +{ + long pagesize = sysconf(_SC_PAGESIZE); + void *page = NULL; + loff_t offset, end_offset, length; + + if (vec == NULL || *vec != NULL) + return -1; + + /* In mmap, offset should be a multiple of the page size */ + offset = (loff_t)defrag_data.orig_start * block_size; + length = (loff_t)defrag_data.len * block_size; + end_offset = offset + length; + /* Round the offset down to the nearest multiple of pagesize */ + offset = (offset / pagesize) * pagesize; + length = end_offset - offset; + + page = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, offset); + if (page == MAP_FAILED) + return -1; + + *page_num = 0; + *page_num = (length + pagesize - 1) / pagesize; + *vec = (unsigned char *)calloc(*page_num, 1); + if (*vec == NULL) + return -1; + + /* Get information on whether pages are in core */ + if (mincore(page, (size_t)length, *vec) == -1 || + munmap(page, length) == -1) { + FREE(*vec); + return -1; + } + + return 0; +} + +/* + * defrag_fadvise() - Predeclare an access pattern for file data. + * + * @fd: defrag target file's descriptor. + * @defrag_data: data used for defrag. + * @vec: page state array. + * @page_num: page number. + */ +static int defrag_fadvise(int fd, struct move_extent defrag_data, + unsigned char *vec, unsigned int page_num) +{ + int flag = 1; + long pagesize = sysconf(_SC_PAGESIZE); + int fadvise_flag = POSIX_FADV_DONTNEED; + int sync_flag = SYNC_FILE_RANGE_WAIT_BEFORE | + SYNC_FILE_RANGE_WRITE | + SYNC_FILE_RANGE_WAIT_AFTER; + unsigned int i; + loff_t offset; + + offset = (loff_t)defrag_data.orig_start * block_size; + offset = (offset / pagesize) * pagesize; + + /* Sync file for fadvise process */ + if (sync_file_range(fd, offset, + (loff_t)pagesize * page_num, sync_flag) < 0) + return -1; + + /* Try to release buffer cache which this process used, + * then other process can use the released buffer + */ + for (i = 0; i < page_num; i++) { + if ((vec[i] & 0x1) == 0) { + offset += pagesize; + continue; + } + if (posix_fadvise(fd, offset, pagesize, fadvise_flag) < 0) { + if ((mode_flag & DETAIL) && flag) { + perror("\tFailed to fadvise"); + flag = 0; + } + } + offset += pagesize; + } + + return 0; +} + +/* + * check_free_size() - Check if there's enough disk space. + * + * @fd: defrag target file's descriptor. + * @file: file name. + * @blk_count: file blocks. + */ +static int check_free_size(int fd, const char *file, ext4_fsblk_t blk_count) +{ + ext4_fsblk_t free_blk_count; + struct statfs64 fsbuf; + + if (fstatfs64(fd, &fsbuf) < 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + PRINT_ERR_MSG_WITH_ERRNO( + "Failed to get filesystem information"); + } + return -1; + } + + /* Compute free space for root and normal user separately */ + if (current_uid == ROOT_UID) + free_blk_count = fsbuf.f_bfree; + else + free_blk_count = fsbuf.f_bavail; + + if (free_blk_count >= blk_count) + return 0; + + return -ENOSPC; +} + +/* + * file_frag_count() - Get file fragment count. + * + * @fd: defrag target file's descriptor. + */ +static int file_frag_count(int fd) +{ + int ret; + struct fiemap fiemap_buf; + + /* When fm_extent_count is 0, + * ioctl just get file fragment count. + */ + memset(&fiemap_buf, 0, sizeof(struct fiemap)); + fiemap_buf.fm_start = 0; + fiemap_buf.fm_length = FIEMAP_MAX_OFFSET; + fiemap_buf.fm_flags |= FIEMAP_FLAG_SYNC; + + ret = ioctl(fd, FS_IOC_FIEMAP, &fiemap_buf); + if (ret < 0) + return ret; + + return fiemap_buf.fm_mapped_extents; +} + +/* + * file_check() - Check file's attributes. + * + * @fd: defrag target file's descriptor. + * @buf: a pointer of the struct stat64. + * @file: file name. + * @extents: file extents. + * @blk_count: file blocks. + */ +static int file_check(int fd, const struct stat64 *buf, const char *file, + int extents, ext4_fsblk_t blk_count) +{ + int ret; + struct flock lock; + + /* Write-lock check is more reliable */ + lock.l_type = F_WRLCK; + lock.l_start = 0; + lock.l_whence = SEEK_SET; + lock.l_len = 0; + + /* Free space */ + ret = check_free_size(fd, file, blk_count); + if (ret < 0) { + if ((mode_flag & DETAIL) && ret == -ENOSPC) { + printf("\033[79;0H\033[K[%u/%u] \"%s\"\t\t" + " extents: %d -> %d\n", defraged_file_count, + total_count, file, extents, extents); + IN_FTW_PRINT_ERR_MSG( + "Defrag size is larger than filesystem's free space"); + } + return -1; + } + + /* Access authority */ + if (current_uid != ROOT_UID && + buf->st_uid != current_uid) { + if (mode_flag & DETAIL) { + printf("\033[79;0H\033[K[%u/%u] \"%s\"\t\t" + " extents: %d -> %d\n", defraged_file_count, + total_count, file, extents, extents); + IN_FTW_PRINT_ERR_MSG( + "File is not current user's file" + " or current user is not root"); + } + return -1; + } + + /* Lock status */ + if (fcntl(fd, F_GETLK, &lock) < 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + PRINT_ERR_MSG_WITH_ERRNO( + "Failed to get lock information"); + } + return -1; + } else if (lock.l_type != F_UNLCK) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + IN_FTW_PRINT_ERR_MSG("File has been locked"); + } + return -1; + } + + return 0; +} + +/* + * insert_extent_by_logical() - Sequentially insert extent by logical. + * + * @ext_list_head: the head of logical extent list. + * @ext: the extent element which will be inserted. + */ +static int insert_extent_by_logical(struct fiemap_extent_list **ext_list_head, + struct fiemap_extent_list *ext) +{ + struct fiemap_extent_list *ext_list_tmp = *ext_list_head; + + if (ext == NULL) + goto out; + + /* First element */ + if (*ext_list_head == NULL) { + (*ext_list_head) = ext; + (*ext_list_head)->prev = *ext_list_head; + (*ext_list_head)->next = *ext_list_head; + return 0; + } + + if (ext->data.logical <= ext_list_tmp->data.logical) { + /* Insert before head */ + if (ext_list_tmp->data.logical < + ext->data.logical + ext->data.len) + /* Overlap */ + goto out; + /* Adjust head */ + *ext_list_head = ext; + } else { + /* Insert into the middle or last of the list */ + do { + if (ext->data.logical < ext_list_tmp->data.logical) + break; + ext_list_tmp = ext_list_tmp->next; + } while (ext_list_tmp != (*ext_list_head)); + if (ext->data.logical < + ext_list_tmp->prev->data.logical + + ext_list_tmp->prev->data.len) + /* Overlap */ + goto out; + + if (ext_list_tmp != *ext_list_head && + ext_list_tmp->data.logical < + ext->data.logical + ext->data.len) + /* Overlap */ + goto out; + } + ext_list_tmp = ext_list_tmp->prev; + /* Insert "ext" after "ext_list_tmp" */ + insert(ext_list_tmp, ext); + return 0; +out: + errno = EINVAL; + return -1; +} + +/* + * insert_extent_by_physical() - Sequentially insert extent by physical. + * + * @ext_list_head: the head of physical extent list. + * @ext: the extent element which will be inserted. + */ +static int insert_extent_by_physical(struct fiemap_extent_list **ext_list_head, + struct fiemap_extent_list *ext) +{ + struct fiemap_extent_list *ext_list_tmp = *ext_list_head; + + if (ext == NULL) + goto out; + + /* First element */ + if (*ext_list_head == NULL) { + (*ext_list_head) = ext; + (*ext_list_head)->prev = *ext_list_head; + (*ext_list_head)->next = *ext_list_head; + return 0; + } + + if (ext->data.physical <= ext_list_tmp->data.physical) { + /* Insert before head */ + if (ext_list_tmp->data.physical < + ext->data.physical + ext->data.len) + /* Overlap */ + goto out; + /* Adjust head */ + *ext_list_head = ext; + } else { + /* Insert into the middle or last of the list */ + do { + if (ext->data.physical < ext_list_tmp->data.physical) + break; + ext_list_tmp = ext_list_tmp->next; + } while (ext_list_tmp != (*ext_list_head)); + if (ext->data.physical < + ext_list_tmp->prev->data.physical + + ext_list_tmp->prev->data.len) + /* Overlap */ + goto out; + + if (ext_list_tmp != *ext_list_head && + ext_list_tmp->data.physical < + ext->data.physical + ext->data.len) + /* Overlap */ + goto out; + } + ext_list_tmp = ext_list_tmp->prev; + /* Insert "ext" after "ext_list_tmp" */ + insert(ext_list_tmp, ext); + return 0; +out: + errno = EINVAL; + return -1; +} + +/* + * insert_exts_group() - Insert a exts_group. + * + * @ext_group_head: the head of a exts_group list. + * @exts_group: the exts_group element which will be inserted. + */ +static int insert_exts_group(struct fiemap_extent_group **ext_group_head, + struct fiemap_extent_group *exts_group) +{ + struct fiemap_extent_group *ext_group_tmp = NULL; + + if (exts_group == NULL) { + errno = EINVAL; + return -1; + } + + /* Initialize list */ + if (*ext_group_head == NULL) { + (*ext_group_head) = exts_group; + (*ext_group_head)->prev = *ext_group_head; + (*ext_group_head)->next = *ext_group_head; + return 0; + } + + ext_group_tmp = (*ext_group_head)->prev; + insert(ext_group_tmp, exts_group); + + return 0; +} + +/* + * join_extents() - Find continuous region(exts_group). + * + * @ext_list_head: the head of the extent list. + * @ext_group_head: the head of the target exts_group list. + */ +static int join_extents(struct fiemap_extent_list *ext_list_head, + struct fiemap_extent_group **ext_group_head) +{ + __u64 len = ext_list_head->data.len; + struct fiemap_extent_list *ext_list_start = ext_list_head; + struct fiemap_extent_list *ext_list_tmp = ext_list_head->next; + + do { + struct fiemap_extent_group *ext_group_tmp = NULL; + + /* This extent and previous extent are not continuous, + * so, all previous extents are treated as an extent group. + */ + if ((ext_list_tmp->prev->data.logical + + ext_list_tmp->prev->data.len) + != ext_list_tmp->data.logical) { + ext_group_tmp = + malloc(sizeof(struct fiemap_extent_group)); + if (ext_group_tmp == NULL) + return -1; + + memset(ext_group_tmp, 0, + sizeof(struct fiemap_extent_group)); + ext_group_tmp->len = len; + ext_group_tmp->start = ext_list_start; + ext_group_tmp->end = ext_list_tmp->prev; + + if (insert_exts_group(ext_group_head, + ext_group_tmp) < 0) { + FREE(ext_group_tmp); + return -1; + } + ext_list_start = ext_list_tmp; + len = ext_list_tmp->data.len; + ext_list_tmp = ext_list_tmp->next; + continue; + } + + /* This extent and previous extent are continuous, + * so, they belong to the same extent group, and we check + * if the next extent belongs to the same extent group. + */ + len += ext_list_tmp->data.len; + ext_list_tmp = ext_list_tmp->next; + } while (ext_list_tmp != ext_list_head->next); + + return 0; +} + +/* + * get_file_extents() - Get file's extent list. + * + * @fd: defrag target file's descriptor. + * @ext_list_head: the head of the extent list. + */ +static int get_file_extents(int fd, struct fiemap_extent_list **ext_list_head) +{ + __u32 i; + int ret; + int ext_buf_size, fie_buf_size; + __u64 pos = 0; + struct fiemap *fiemap_buf = NULL; + struct fiemap_extent *ext_buf = NULL; + struct fiemap_extent_list *ext_list = NULL; + + /* Convert units, in bytes. + * Be careful : now, physical block number in extent is 48bit, + * and the maximum blocksize for ext4 is 4K(12bit), + * so there is no overflow, but in future it may be changed. + */ + + /* Alloc space for fiemap */ + ext_buf_size = EXTENT_MAX_COUNT * sizeof(struct fiemap_extent); + fie_buf_size = sizeof(struct fiemap) + ext_buf_size; + + fiemap_buf = malloc(fie_buf_size); + if (fiemap_buf == NULL) + return -1; + + ext_buf = fiemap_buf->fm_extents; + memset(fiemap_buf, 0, fie_buf_size); + fiemap_buf->fm_length = FIEMAP_MAX_OFFSET; + fiemap_buf->fm_flags |= FIEMAP_FLAG_SYNC; + fiemap_buf->fm_extent_count = EXTENT_MAX_COUNT; + + do { + fiemap_buf->fm_start = pos; + memset(ext_buf, 0, ext_buf_size); + ret = ioctl(fd, FS_IOC_FIEMAP, fiemap_buf); + if (ret < 0 || fiemap_buf->fm_mapped_extents == 0) + goto out; + for (i = 0; i < fiemap_buf->fm_mapped_extents; i++) { + ext_list = NULL; + ext_list = malloc(sizeof(struct fiemap_extent_list)); + if (ext_list == NULL) + goto out; + + ext_list->data.physical = ext_buf[i].fe_physical + / block_size; + ext_list->data.logical = ext_buf[i].fe_logical + / block_size; + ext_list->data.len = ext_buf[i].fe_length + / block_size; + + ret = insert_extent_by_physical( + ext_list_head, ext_list); + if (ret < 0) { + FREE(ext_list); + goto out; + } + } + /* Record file's logical offset this time */ + pos = ext_buf[EXTENT_MAX_COUNT-1].fe_logical + + ext_buf[EXTENT_MAX_COUNT-1].fe_length; + /* + * If fm_extents array has been filled and + * there are extents left, continue to cycle. + */ + } while (fiemap_buf->fm_mapped_extents + == EXTENT_MAX_COUNT && + !(ext_buf[EXTENT_MAX_COUNT-1].fe_flags + & FIEMAP_EXTENT_LAST)); + + FREE(fiemap_buf); + return 0; +out: + FREE(fiemap_buf); + return -1; +} + +/* + * get_logical_count() - Get the file logical extents count. + * + * @logical_list_head: the head of the logical extent list. + */ +static int get_logical_count(struct fiemap_extent_list *logical_list_head) +{ + int ret = 0; + struct fiemap_extent_list *ext_list_tmp = logical_list_head; + + do { + ret++; + ext_list_tmp = ext_list_tmp->next; + } while (ext_list_tmp != logical_list_head); + + return ret; +} + +/* + * get_physical_count() - Get the file physical extents count. + * + * @physical_list_head: the head of the physical extent list. + */ +static int get_physical_count(struct fiemap_extent_list *physical_list_head) +{ + int ret = 0; + struct fiemap_extent_list *ext_list_tmp = physical_list_head; + + do { + if ((ext_list_tmp->data.physical + ext_list_tmp->data.len) + != ext_list_tmp->next->data.physical) { + /* This extent and next extent are not continuous. */ + ret++; + } + + ext_list_tmp = ext_list_tmp->next; + } while (ext_list_tmp != physical_list_head); + + return ret; +} + +/* + * change_physical_to_logical() - Change list from physical to logical. + * + * @physical_list_head: the head of physical extent list. + * @logical_list_head: the head of logical extent list. + */ +static int change_physical_to_logical( + struct fiemap_extent_list **physical_list_head, + struct fiemap_extent_list **logical_list_head) +{ + int ret; + struct fiemap_extent_list *ext_list_tmp = *physical_list_head; + struct fiemap_extent_list *ext_list_next = ext_list_tmp->next; + + while (1) { + if (ext_list_tmp == ext_list_next) { + ret = insert_extent_by_logical( + logical_list_head, ext_list_tmp); + if (ret < 0) + return -1; + + *physical_list_head = NULL; + break; + } + + ext_list_tmp->prev->next = ext_list_tmp->next; + ext_list_tmp->next->prev = ext_list_tmp->prev; + *physical_list_head = ext_list_next; + + ret = insert_extent_by_logical( + logical_list_head, ext_list_tmp); + if (ret < 0) { + FREE(ext_list_tmp); + return -1; + } + ext_list_tmp = ext_list_next; + ext_list_next = ext_list_next->next; + } + + return 0; +} + +/* get_file_blocks() - Get total file blocks. + * + * @ext_list_head: the extent list head of the target file + */ +static ext4_fsblk_t get_file_blocks(struct fiemap_extent_list *ext_list_head) +{ + ext4_fsblk_t blk_count = 0; + struct fiemap_extent_list *ext_list_tmp = ext_list_head; + + do { + blk_count += ext_list_tmp->data.len; + ext_list_tmp = ext_list_tmp->next; + } while (ext_list_tmp != ext_list_head); + + return blk_count; +} + +/* + * free_ext() - Free the extent list. + * + * @ext_list_head: the extent list head of which will be free. + */ +static void free_ext(struct fiemap_extent_list *ext_list_head) +{ + struct fiemap_extent_list *ext_list_tmp = NULL; + + if (ext_list_head == NULL) + return; + + while (ext_list_head->next != ext_list_head) { + ext_list_tmp = ext_list_head; + ext_list_head->prev->next = ext_list_head->next; + ext_list_head->next->prev = ext_list_head->prev; + ext_list_head = ext_list_head->next; + free(ext_list_tmp); + } + free(ext_list_head); +} + +/* + * free_exts_group() - Free the exts_group. + * + * @*ext_group_head: the exts_group list head which will be free. + */ +static void free_exts_group(struct fiemap_extent_group *ext_group_head) +{ + struct fiemap_extent_group *ext_group_tmp = NULL; + + if (ext_group_head == NULL) + return; + + while (ext_group_head->next != ext_group_head) { + ext_group_tmp = ext_group_head; + ext_group_head->prev->next = ext_group_head->next; + ext_group_head->next->prev = ext_group_head->prev; + ext_group_head = ext_group_head->next; + free(ext_group_tmp); + } + free(ext_group_head); +} + +/* + * get_best_count() - Get the file best extents count. + * + * @block_count: the file's physical block count. + */ +static int get_best_count(ext4_fsblk_t block_count) +{ + int ret; + unsigned int flex_bg_num; + + /* Calcuate best extents count */ + if (feature_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) { + flex_bg_num = 1 << log_groups_per_flex; + ret = ((block_count - 1) / + ((ext4_fsblk_t)blocks_per_group * + flex_bg_num)) + 1; + } else + ret = ((block_count - 1) / blocks_per_group) + 1; + + return ret; +} + + +/* + * file_statistic() - Get statistic info of the file's fragments. + * + * @file: the file's name. + * @buf: the pointer of the struct stat64. + * @flag: file type. + * @ftwbuf: the pointer of a struct FTW. + */ +static int file_statistic(const char *file, const struct stat64 *buf, + int flag EXT2FS_ATTR((unused)), + struct FTW *ftwbuf EXT2FS_ATTR((unused))) +{ + int fd; + int ret; + int now_ext_count, best_ext_count = 0, physical_ext_count; + int i, j; + __u64 size_per_ext = 0; + float ratio = 0.0; + ext4_fsblk_t blk_count = 0; + char msg_buffer[PATH_MAX + 24]; + struct fiemap_extent_list *physical_list_head = NULL; + struct fiemap_extent_list *logical_list_head = NULL; + + defraged_file_count++; + + if (mode_flag & DETAIL) { + if (total_count == 1 && regular_count == 1) + printf("<File>\n"); + else { + printf("[%u/%u]", defraged_file_count, total_count); + fflush(stdout); + } + } + if (lost_found_dir[0] != '\0' && + !memcmp(file, lost_found_dir, strnlen(lost_found_dir, PATH_MAX))) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + STATISTIC_ERR_MSG(NGMSG_LOST_FOUND); + } + return 0; + } + + if (!S_ISREG(buf->st_mode)) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + STATISTIC_ERR_MSG(NGMSG_FILE_UNREG); + } + return 0; + } + + /* Access authority */ + if (current_uid != ROOT_UID && + buf->st_uid != current_uid) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + STATISTIC_ERR_MSG( + "File is not current user's file" + " or current user is not root"); + } + return 0; + } + + /* Empty file */ + if (buf->st_size == 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + STATISTIC_ERR_MSG("File size is 0"); + } + return 0; + } + + /* Has no blocks */ + if (buf->st_blocks == 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + STATISTIC_ERR_MSG("File has no blocks"); + } + return 0; + } + + fd = open64(file, O_RDONLY); + if (fd < 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + STATISTIC_ERR_MSG_WITH_ERRNO(NGMSG_FILE_OPEN); + } + return 0; + } + + /* Get file's physical extents */ + ret = get_file_extents(fd, &physical_list_head); + if (ret < 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + STATISTIC_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT); + } + goto out; + } + + /* Get the count of file's continuous physical region */ + physical_ext_count = get_physical_count(physical_list_head); + + /* Change list from physical to logical */ + ret = change_physical_to_logical(&physical_list_head, + &logical_list_head); + if (ret < 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + STATISTIC_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT); + } + goto out; + } + + /* Count file fragments before defrag */ + now_ext_count = get_logical_count(logical_list_head); + + if (current_uid == ROOT_UID) { + /* Calculate the size per extent */ + blk_count = get_file_blocks(logical_list_head); + + best_ext_count = get_best_count(blk_count); + + /* e4defrag rounds size_per_ext up to a block size boundary */ + size_per_ext = blk_count * (buf->st_blksize / 1024) / + now_ext_count; + + ratio = (float)(physical_ext_count - best_ext_count) * 100 / + blk_count; + + extents_before_defrag += now_ext_count; + extents_after_defrag += best_ext_count; + files_block_count += blk_count; + } + + if (total_count == 1 && regular_count == 1) { + /* File only */ + if (mode_flag & DETAIL) { + int count = 0; + struct fiemap_extent_list *ext_list_tmp = + logical_list_head; + + /* Print extents info */ + do { + count++; + printf("[ext %d]:\tstart %llu:\tlogical " + "%llu:\tlen %llu\n", count, + ext_list_tmp->data.physical, + ext_list_tmp->data.logical, + ext_list_tmp->data.len); + ext_list_tmp = ext_list_tmp->next; + } while (ext_list_tmp != logical_list_head); + + } else { + printf("%-40s%10s/%-10s%9s\n", + "<File>", "now", "best", "size/ext"); + if (current_uid == ROOT_UID) { + if (strlen(file) > 40) + printf("%s\n%50d/%-10d%6llu KB\n", + file, now_ext_count, + best_ext_count, size_per_ext); + else + printf("%-40s%10d/%-10d%6llu KB\n", + file, now_ext_count, + best_ext_count, size_per_ext); + } else { + if (strlen(file) > 40) + printf("%s\n%50d/%-10s%7s\n", + file, now_ext_count, + "-", "-"); + else + printf("%-40s%10d/%-10s%7s\n", + file, now_ext_count, + "-", "-"); + } + } + succeed_cnt++; + goto out; + } + + if (mode_flag & DETAIL) { + /* Print statistic info */ + sprintf(msg_buffer, "[%u/%u]%s", + defraged_file_count, total_count, file); + if (current_uid == ROOT_UID) { + if (strlen(msg_buffer) > 40) + printf("\033[79;0H\033[K%s\n" + "%50d/%-10d%6llu KB\n", + msg_buffer, now_ext_count, + best_ext_count, size_per_ext); + else + printf("\033[79;0H\033[K%-40s" + "%10d/%-10d%6llu KB\n", + msg_buffer, now_ext_count, + best_ext_count, size_per_ext); + } else { + if (strlen(msg_buffer) > 40) + printf("\033[79;0H\033[K%s\n%50d/%-10s%7s\n", + msg_buffer, now_ext_count, + "-", "-"); + else + printf("\033[79;0H\033[K%-40s%10d/%-10s%7s\n", + msg_buffer, now_ext_count, + "-", "-"); + } + } + + for (i = 0; i < SHOW_FRAG_FILES; i++) { + if (ratio >= frag_rank[i].ratio) { + for (j = SHOW_FRAG_FILES - 1; j > i; j--) { + memset(&frag_rank[j], 0, + sizeof(struct frag_statistic_ino)); + strncpy(frag_rank[j].msg_buffer, + frag_rank[j - 1].msg_buffer, + strnlen(frag_rank[j - 1].msg_buffer, + PATH_MAX)); + frag_rank[j].now_count = + frag_rank[j - 1].now_count; + frag_rank[j].best_count = + frag_rank[j - 1].best_count; + frag_rank[j].size_per_ext = + frag_rank[j - 1].size_per_ext; + frag_rank[j].ratio = + frag_rank[j - 1].ratio; + } + memset(&frag_rank[i], 0, + sizeof(struct frag_statistic_ino)); + strncpy(frag_rank[i].msg_buffer, file, + strnlen(file, PATH_MAX)); + frag_rank[i].now_count = now_ext_count; + frag_rank[i].best_count = best_ext_count; + frag_rank[i].size_per_ext = size_per_ext; + frag_rank[i].ratio = ratio; + break; + } + } + + succeed_cnt++; + +out: + close(fd); + free_ext(physical_list_head); + free_ext(logical_list_head); + return 0; +} + +/* + * print_progress - Print defrag progress + * + * @file: file name. + * @start: logical offset for defrag target file + * @file_size: defrag target filesize + */ +static void print_progress(const char *file, loff_t start, loff_t file_size) +{ + int percent = (start * 100) / file_size; + printf("\033[79;0H\033[K[%u/%u]%s:\t%3d%%", + defraged_file_count, total_count, file, min(percent, 100)); + fflush(stdout); + + return; +} + +/* + * call_defrag() - Execute the defrag program. + * + * @fd: target file descriptor. + * @donor_fd: donor file descriptor. + * @file: target file name. + * @buf: pointer of the struct stat64. + * @ext_list_head: head of the extent list. + */ +static int call_defrag(int fd, int donor_fd, const char *file, + const struct stat64 *buf, struct fiemap_extent_list *ext_list_head) +{ + loff_t start = 0; + unsigned int page_num; + unsigned char *vec = NULL; + int defraged_ret = 0; + int ret; + struct move_extent move_data; + struct fiemap_extent_list *ext_list_tmp = NULL; + + memset(&move_data, 0, sizeof(struct move_extent)); + move_data.donor_fd = donor_fd; + + /* Print defrag progress */ + print_progress(file, start, buf->st_size); + + ext_list_tmp = ext_list_head; + do { + move_data.orig_start = ext_list_tmp->data.logical; + /* Logical offset of orig and donor should be same */ + move_data.donor_start = move_data.orig_start; + move_data.len = ext_list_tmp->data.len; + move_data.moved_len = 0; + + ret = page_in_core(fd, move_data, &vec, &page_num); + if (ret < 0) { + if (mode_flag & DETAIL) { + printf("\n"); + PRINT_ERR_MSG_WITH_ERRNO( + "Failed to get file map"); + } else { + printf("\t[ NG ]\n"); + } + return -1; + } + + /* EXT4_IOC_MOVE_EXT */ + defraged_ret = + ioctl(fd, EXT4_IOC_MOVE_EXT, &move_data); + + /* Free pages */ + ret = defrag_fadvise(fd, move_data, vec, page_num); + if (vec) { + free(vec); + vec = NULL; + } + if (ret < 0) { + if (mode_flag & DETAIL) { + printf("\n"); + PRINT_ERR_MSG_WITH_ERRNO( + "Failed to free page"); + } else { + printf("\t[ NG ]\n"); + } + return -1; + } + + if (defraged_ret < 0) { + if (mode_flag & DETAIL) { + printf("\n"); + PRINT_ERR_MSG_WITH_ERRNO( + "Failed to defrag with " + "EXT4_IOC_MOVE_EXT ioctl"); + if (errno == ENOTTY) + printf("\tAt least 2.6.31-rc1 of " + "vanilla kernel is required\n"); + } else { + printf("\t[ NG ]\n"); + } + return -1; + } + /* Adjust logical offset for next ioctl */ + move_data.orig_start += move_data.moved_len; + move_data.donor_start = move_data.orig_start; + + start = move_data.orig_start * buf->st_blksize; + + /* Print defrag progress */ + print_progress(file, start, buf->st_size); + + /* End of file */ + if (start >= buf->st_size) + break; + + ext_list_tmp = ext_list_tmp->next; + } while (ext_list_tmp != ext_list_head); + + return 0; +} + +/* + * file_defrag() - Check file attributes and call ioctl to defrag. + * + * @file: the file's name. + * @buf: the pointer of the struct stat64. + * @flag: file type. + * @ftwbuf: the pointer of a struct FTW. + */ +static int file_defrag(const char *file, const struct stat64 *buf, + int flag EXT2FS_ATTR((unused)), + struct FTW *ftwbuf EXT2FS_ATTR((unused))) +{ + int fd; + int donor_fd = -1; + int ret; + int best; + int file_frags_start, file_frags_end; + int orig_physical_cnt, donor_physical_cnt = 0; + char tmp_inode_name[PATH_MAX + 8]; + ext4_fsblk_t blk_count = 0; + struct fiemap_extent_list *orig_list_physical = NULL; + struct fiemap_extent_list *orig_list_logical = NULL; + struct fiemap_extent_list *donor_list_physical = NULL; + struct fiemap_extent_list *donor_list_logical = NULL; + struct fiemap_extent_group *orig_group_head = NULL; + struct fiemap_extent_group *orig_group_tmp = NULL; + + defraged_file_count++; + + if (mode_flag & DETAIL) { + printf("[%u/%u]", defraged_file_count, total_count); + fflush(stdout); + } + + if (lost_found_dir[0] != '\0' && + !memcmp(file, lost_found_dir, strnlen(lost_found_dir, PATH_MAX))) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + IN_FTW_PRINT_ERR_MSG(NGMSG_LOST_FOUND); + } + return 0; + } + + if (!S_ISREG(buf->st_mode)) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + IN_FTW_PRINT_ERR_MSG(NGMSG_FILE_UNREG); + } + return 0; + } + + /* Empty file */ + if (buf->st_size == 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + IN_FTW_PRINT_ERR_MSG("File size is 0"); + } + return 0; + } + + /* Has no blocks */ + if (buf->st_blocks == 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + STATISTIC_ERR_MSG("File has no blocks"); + } + return 0; + } + + fd = open64(file, O_RDWR); + if (fd < 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_OPEN); + } + return 0; + } + + /* Get file's extents */ + ret = get_file_extents(fd, &orig_list_physical); + if (ret < 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT); + } + goto out; + } + + /* Get the count of file's continuous physical region */ + orig_physical_cnt = get_physical_count(orig_list_physical); + + /* Change list from physical to logical */ + ret = change_physical_to_logical(&orig_list_physical, + &orig_list_logical); + if (ret < 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT); + } + goto out; + } + + /* Count file fragments before defrag */ + file_frags_start = get_logical_count(orig_list_logical); + + blk_count = get_file_blocks(orig_list_logical); + if (file_check(fd, buf, file, file_frags_start, blk_count) < 0) + goto out; + + if (fsync(fd) < 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + PRINT_ERR_MSG_WITH_ERRNO("Failed to sync(fsync)"); + } + goto out; + } + + if (current_uid == ROOT_UID) + best = get_best_count(blk_count); + else + best = 1; + + if (file_frags_start <= best) + goto check_improvement; + + /* Combine extents to group */ + ret = join_extents(orig_list_logical, &orig_group_head); + if (ret < 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT); + } + goto out; + } + + /* Create donor inode */ + memset(tmp_inode_name, 0, PATH_MAX + 8); + sprintf(tmp_inode_name, "%.*s.defrag", + (int)strnlen(file, PATH_MAX), file); + donor_fd = open64(tmp_inode_name, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR); + if (donor_fd < 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + if (errno == EEXIST) + PRINT_ERR_MSG_WITH_ERRNO( + "File is being defraged by other program"); + else + PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_OPEN); + } + goto out; + } + + /* Unlink donor inode */ + ret = unlink(tmp_inode_name); + if (ret < 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + PRINT_ERR_MSG_WITH_ERRNO("Failed to unlink"); + } + goto out; + } + + /* Allocate space for donor inode */ + orig_group_tmp = orig_group_head; + do { + ret = fallocate64(donor_fd, 0, + (loff_t)orig_group_tmp->start->data.logical * block_size, + (loff_t)orig_group_tmp->len * block_size); + if (ret < 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + PRINT_ERR_MSG_WITH_ERRNO("Failed to fallocate"); + } + goto out; + } + + orig_group_tmp = orig_group_tmp->next; + } while (orig_group_tmp != orig_group_head); + + /* Get donor inode's extents */ + ret = get_file_extents(donor_fd, &donor_list_physical); + if (ret < 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT); + } + goto out; + } + + /* Calcuate donor inode's continuous physical region */ + donor_physical_cnt = get_physical_count(donor_list_physical); + + /* Change donor extent list from physical to logical */ + ret = change_physical_to_logical(&donor_list_physical, + &donor_list_logical); + if (ret < 0) { + if (mode_flag & DETAIL) { + PRINT_FILE_NAME(file); + PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT); + } + goto out; + } + +check_improvement: + if (mode_flag & DETAIL) { + if (file_frags_start != 1) + frag_files_before_defrag++; + + extents_before_defrag += file_frags_start; + } + + if (file_frags_start <= best || + orig_physical_cnt <= donor_physical_cnt) { + printf("\033[79;0H\033[K[%u/%u]%s:\t%3d%%", + defraged_file_count, total_count, file, 100); + if (mode_flag & DETAIL) + printf(" extents: %d -> %d", + file_frags_start, file_frags_start); + + printf("\t[ OK ]\n"); + succeed_cnt++; + + if (file_frags_start != 1) + frag_files_after_defrag++; + + extents_after_defrag += file_frags_start; + goto out; + } + + /* Defrag the file */ + ret = call_defrag(fd, donor_fd, file, buf, donor_list_logical); + + /* Count file fragments after defrag and print extents info */ + if (mode_flag & DETAIL) { + file_frags_end = file_frag_count(fd); + if (file_frags_end < 0) { + printf("\n"); + PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_INFO); + goto out; + } + + if (file_frags_end != 1) + frag_files_after_defrag++; + + extents_after_defrag += file_frags_end; + + if (ret < 0) + goto out; + + printf(" extents: %d -> %d", + file_frags_start, file_frags_end); + fflush(stdout); + } + + if (ret < 0) + goto out; + + printf("\t[ OK ]\n"); + fflush(stdout); + succeed_cnt++; + +out: + close(fd); + if (donor_fd != -1) + close(donor_fd); + free_ext(orig_list_physical); + free_ext(orig_list_logical); + free_ext(donor_list_physical); + free_exts_group(orig_group_head); + return 0; +} + +/* + * main() - Ext4 online defrag. + * + * @argc: the number of parameter. + * @argv[]: the pointer array of parameter. + */ +int main(int argc, char *argv[]) +{ + int opt; + int i, j, ret = 0; + int flags = FTW_PHYS | FTW_MOUNT; + int arg_type = -1; + int success_flag = 0; + char dir_name[PATH_MAX + 1]; + char dev_name[PATH_MAX + 1]; + struct stat64 buf; + ext2_filsys fs = NULL; + + /* Parse arguments */ + if (argc == 1) + goto out; + + while ((opt = getopt(argc, argv, "vc")) != EOF) { + switch (opt) { + case 'v': + mode_flag |= DETAIL; + break; + case 'c': + mode_flag |= STATISTIC; + break; + default: + goto out; + } + } + + if (argc == optind) + goto out; + + current_uid = getuid(); + + /* Main process */ + for (i = optind; i < argc; i++) { + succeed_cnt = 0; + regular_count = 0; + total_count = 0; + frag_files_before_defrag = 0; + frag_files_after_defrag = 0; + extents_before_defrag = 0; + extents_after_defrag = 0; + defraged_file_count = 0; + files_block_count = 0; + blocks_per_group = 0; + feature_incompat = 0; + log_groups_per_flex = 0; + + memset(dir_name, 0, PATH_MAX + 1); + memset(dev_name, 0, PATH_MAX + 1); + memset(lost_found_dir, 0, PATH_MAX + 1); + memset(frag_rank, 0, + sizeof(struct frag_statistic_ino) * SHOW_FRAG_FILES); + + if ((mode_flag & STATISTIC) && i > optind) + printf("\n"); + +#if BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN + PRINT_ERR_MSG("Endian's type is not big/little endian"); + PRINT_FILE_NAME(argv[i]); + continue; +#endif + + if (lstat64(argv[i], &buf) < 0) { + perror(NGMSG_FILE_INFO); + PRINT_FILE_NAME(argv[i]); + continue; + } + + if (S_ISBLK(buf.st_mode)) { + /* Block device */ + strncpy(dev_name, argv[i], strnlen(argv[i], PATH_MAX)); + if (get_mount_point(argv[i], dir_name, PATH_MAX) < 0) + continue; + if (lstat64(dir_name, &buf) < 0) { + perror(NGMSG_FILE_INFO); + PRINT_FILE_NAME(argv[i]); + continue; + } + arg_type = DEVNAME; + if (!(mode_flag & STATISTIC)) + printf("ext4 defragmentation for device(%s)\n", + argv[i]); + } else if (S_ISDIR(buf.st_mode)) { + /* Directory */ + if (access(argv[i], R_OK) < 0) { + perror(argv[i]); + continue; + } + arg_type = DIRNAME; + strncpy(dir_name, argv[i], strnlen(argv[i], PATH_MAX)); + } else if (S_ISREG(buf.st_mode)) { + /* Regular file */ + arg_type = FILENAME; + } else { + /* Irregular file */ + PRINT_ERR_MSG(NGMSG_FILE_UNREG); + PRINT_FILE_NAME(argv[i]); + continue; + } + + /* Set blocksize */ + block_size = buf.st_blksize; + + /* For device case, + * filesystem type checked in get_mount_point() + */ + if (arg_type == FILENAME || arg_type == DIRNAME) { + if (is_ext4(argv[i], dev_name) < 0) + continue; + if (realpath(argv[i], dir_name) == NULL) { + perror("Couldn't get full path"); + PRINT_FILE_NAME(argv[i]); + continue; + } + } + + if (current_uid == ROOT_UID) { + /* Get super block info */ + ret = ext2fs_open(dev_name, 0, 0, block_size, + unix_io_manager, &fs); + if (ret) { + if (mode_flag & DETAIL) { + perror("Can't get super block info"); + PRINT_FILE_NAME(argv[i]); + } + continue; + } + + blocks_per_group = fs->super->s_blocks_per_group; + feature_incompat = fs->super->s_feature_incompat; + log_groups_per_flex = fs->super->s_log_groups_per_flex; + + ext2fs_close(fs); + } + + switch (arg_type) { + case DIRNAME: + if (!(mode_flag & STATISTIC)) + printf("ext4 defragmentation " + "for directory(%s)\n", argv[i]); + + int mount_dir_len = 0; + mount_dir_len = strnlen(lost_found_dir, PATH_MAX); + + strncat(lost_found_dir, "/lost+found", + PATH_MAX - strnlen(lost_found_dir, PATH_MAX)); + + /* Not the case("e4defrag mount_piont_dir") */ + if (dir_name[mount_dir_len] != '\0') { + /* + * "e4defrag mount_piont_dir/lost+found" + * or "e4defrag mount_piont_dir/lost+found/" + */ + if (strncmp(lost_found_dir, dir_name, + strnlen(lost_found_dir, + PATH_MAX)) == 0 && + (dir_name[strnlen(lost_found_dir, + PATH_MAX)] == '\0' || + dir_name[strnlen(lost_found_dir, + PATH_MAX)] == '/')) { + PRINT_ERR_MSG(NGMSG_LOST_FOUND); + PRINT_FILE_NAME(argv[i]); + continue; + } + + /* "e4defrag mount_piont_dir/else_dir" */ + memset(lost_found_dir, 0, PATH_MAX + 1); + } + case DEVNAME: + if (arg_type == DEVNAME) { + strncpy(lost_found_dir, dir_name, + strnlen(dir_name, PATH_MAX)); + strncat(lost_found_dir, "/lost+found/", + PATH_MAX - strnlen(lost_found_dir, + PATH_MAX)); + } + + nftw64(dir_name, calc_entry_counts, FTW_OPEN_FD, flags); + + if (mode_flag & STATISTIC) { + if (mode_flag & DETAIL) + printf("%-40s%10s/%-10s%9s\n", + "<File>", "now", "best", "size/ext"); + + if (!(mode_flag & DETAIL) && + current_uid != ROOT_UID) { + printf(" Done.\n"); + success_flag = 1; + continue; + } + + nftw64(dir_name, file_statistic, + FTW_OPEN_FD, flags); + + if (succeed_cnt != 0 && + current_uid == ROOT_UID) { + if (mode_flag & DETAIL) + printf("\n"); + printf("%-40s%10s/%-10s%9s\n", + "<Fragmented files>", "now", + "best", "size/ext"); + for (j = 0; j < SHOW_FRAG_FILES; j++) { + if (strlen(frag_rank[j]. + msg_buffer) > 37) { + printf("%d. %s\n%50d/" + "%-10d%6llu KB\n", + j + 1, + frag_rank[j].msg_buffer, + frag_rank[j].now_count, + frag_rank[j].best_count, + frag_rank[j]. + size_per_ext); + } else if (strlen(frag_rank[j]. + msg_buffer) > 0) { + printf("%d. %-37s%10d/" + "%-10d%6llu KB\n", + j + 1, + frag_rank[j].msg_buffer, + frag_rank[j].now_count, + frag_rank[j].best_count, + frag_rank[j]. + size_per_ext); + } else + break; + } + } + break; + } + /* File tree walk */ + nftw64(dir_name, file_defrag, FTW_OPEN_FD, flags); + printf("\n\tSuccess:\t\t\t[ %u/%u ]\n", succeed_cnt, + total_count); + printf("\tFailure:\t\t\t[ %u/%u ]\n", + total_count - succeed_cnt, total_count); + if (mode_flag & DETAIL) { + printf("\tTotal extents:\t\t\t%4d->%d\n", + extents_before_defrag, + extents_after_defrag); + printf("\tFragmented percentage:\t\t" + "%3llu%%->%llu%%\n", + !regular_count ? 0 : + ((unsigned long long) + frag_files_before_defrag * 100) / + regular_count, + !regular_count ? 0 : + ((unsigned long long) + frag_files_after_defrag * 100) / + regular_count); + } + break; + case FILENAME: + total_count = 1; + regular_count = 1; + strncat(lost_found_dir, "/lost+found/", + PATH_MAX - strnlen(lost_found_dir, + PATH_MAX)); + if (strncmp(lost_found_dir, dir_name, + strnlen(lost_found_dir, + PATH_MAX)) == 0) { + PRINT_ERR_MSG(NGMSG_LOST_FOUND); + PRINT_FILE_NAME(argv[i]); + continue; + } + + if (mode_flag & STATISTIC) { + file_statistic(argv[i], &buf, FTW_F, NULL); + break; + } else + printf("ext4 defragmentation for %s\n", + argv[i]); + /* Defrag single file process */ + file_defrag(argv[i], &buf, FTW_F, NULL); + if (succeed_cnt != 0) + printf(" Success:\t\t\t[1/1]\n"); + else + printf(" Success:\t\t\t[0/1]\n"); + + break; + } + + if (succeed_cnt != 0) + success_flag = 1; + if (mode_flag & STATISTIC) { + if (current_uid != ROOT_UID) { + printf(" Done.\n"); + continue; + } + + if (!succeed_cnt) { + if (mode_flag & DETAIL) + printf("\n"); + + if (arg_type == DEVNAME) + printf(" In this device(%s), " + "none can be defragmented.\n", argv[i]); + else if (arg_type == DIRNAME) + printf(" In this directory(%s), " + "none can be defragmented.\n", argv[i]); + else + printf(" This file(%s) " + "can't be defragmented.\n", argv[i]); + } else { + float files_ratio = 0.0; + float score = 0.0; + __u64 size_per_ext = files_block_count * + (buf.st_blksize / 1024) / + extents_before_defrag; + files_ratio = (float)(extents_before_defrag - + extents_after_defrag) * + 100 / files_block_count; + score = CALC_SCORE(files_ratio); + printf("\n Total/best extents\t\t\t\t%d/%d\n" + " Average size per extent" + "\t\t\t%llu KB\n" + " Fragmentation score\t\t\t\t%.0f\n", + extents_before_defrag, + extents_after_defrag, + size_per_ext, score); + printf(" [0-30 no problem:" + " 31-55 a little bit fragmented:" + " 56- needs defrag]\n"); + + if (arg_type == DEVNAME) + printf(" This device (%s) ", argv[i]); + else if (arg_type == DIRNAME) + printf(" This directory (%s) ", + argv[i]); + else + printf(" This file (%s) ", argv[i]); + + if (score > BOUND_SCORE) + printf("needs defragmentation.\n"); + else + printf("does not need " + "defragmentation.\n"); + } + printf(" Done.\n"); + } + + } + + if (success_flag) + return 0; + + exit(1); + +out: + printf(MSG_USAGE); + exit(1); +} + diff --git a/misc/filefrag.c b/misc/filefrag.c index 27f48ea0..2795e158 100644 --- a/misc/filefrag.c +++ b/misc/filefrag.c @@ -55,7 +55,6 @@ unsigned long long filesize; #define FIBMAP _IO(0x00, 1) /* bmap access */ #define FIGETBSZ _IO(0x00, 2) /* get the block size used for bmap */ -#define FS_IOC_FIEMAP _IOWR('f', 11, struct fiemap) #define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ #define EXT3_IOC_GETFLAGS _IOR('f', 1, long) diff --git a/misc/findsuper.c b/misc/findsuper.c index fbc28a37..e793e3b1 100644 --- a/misc/findsuper.c +++ b/misc/findsuper.c @@ -92,6 +92,7 @@ #include <time.h> #include "ext2fs/ext2_fs.h" +#include "ext2fs/ext2fs.h" #include "nls-enable.h" #undef DEBUG @@ -215,12 +216,14 @@ int main(int argc, char *argv[]) continue; if (ext2.s_log_block_size > 6) WHY("log block size > 6 (%u)\n", ext2.s_log_block_size); - if (ext2.s_r_blocks_count > ext2.s_blocks_count) + if (ext2fs_r_blocks_count(&ext2) > ext2fs_blocks_count(&ext2)) WHY("r_blocks_count > blocks_count (%u > %u)\n", - ext2.s_r_blocks_count, ext2.s_blocks_count); - if (ext2.s_free_blocks_count > ext2.s_blocks_count) + ext2fs_r_blocks_count(&ext2), + ext2fs_blocks_count(&ext2)); + if (ext2fs_free_blocks_count(&ext2) > ext2fs_blocks_count(&ext2)) WHY("free_blocks_count > blocks_count\n (%u > %u)\n", - ext2.s_free_blocks_count, ext2.s_blocks_count); + ext2fs_free_blocks_count(&ext2), + ext2fs_blocks_count(&ext2)); if (ext2.s_free_inodes_count > ext2.s_inodes_count) WHY("free_inodes_count > inodes_count (%u > %u)\n", ext2.s_free_inodes_count, ext2.s_inodes_count); @@ -246,9 +249,9 @@ int main(int argc, char *argv[]) printf("\r%11Lu %11Lu%s %11Lu%s %9u %5Lu %4u%s %s %02x%02x%02x%02x %s\n", sk, sk - ext2.s_block_group_nr * grpsize - sb_offset, jnl_copy ? "*":" ", - sk + ext2.s_blocks_count * bsize - + sk + ext2fs_blocks_count(&ext2) * bsize - ext2.s_block_group_nr * grpsize - sb_offset, - jnl_copy ? "*" : " ", ext2.s_blocks_count, bsize, + jnl_copy ? "*" : " ", ext2fs_blocks_count(&ext2), bsize, ext2.s_block_group_nr, jnl_copy ? "*" : " ", s, ext2.s_uuid[0], ext2.s_uuid[1], ext2.s_uuid[2], ext2.s_uuid[3], ext2.s_volume_name); diff --git a/misc/mke2fs.8.in b/misc/mke2fs.8.in index a3b0dd64..0d4b046a 100644 --- a/misc/mke2fs.8.in +++ b/misc/mke2fs.8.in @@ -232,7 +232,15 @@ This speeds up filesystem initialization noticeably, but it requires the kernel to finish initializing the filesystem in the background when the filesystem is first mounted. If the option value is omitted, it defaults to 1 to -enable lazy inode table initialization. +enable lazy inode table zeroing. +.TP +.B lazy_journal_init\fR[\fB= \fI<0 to disable, 1 to enable>\fR] +If enabled, the journal inode will not be fully zeroed out by +.BR mke2fs . +This speeds up filesystem initialization noticeably, but carries some +small risk if the system crashes before the journal has been overwritten +entirely one time. If the option value is omitted, it defaults to 1 to +enable lazy journal inode zeroing. .TP .B test_fs Set a flag in the filesystem superblock indicating that it may be @@ -620,8 +628,17 @@ will use the filesystem type If the filesystem size is greater than 3 but less than or equal to 512 megabytes, .BR mke2fs (8) -will use the filesystem +will use the filesystem type .IR small . +If the filesystem size is greater than or equal to 4 terabytes but less than +16 terabytes, +.BR mke2fs (8) +will use the filesystem type +.IR big . +If the filesystem size is greater than or equal to 16 terabytes, +.BR mke2fs (8) +will use the filesystem type +.IR huge . Otherwise, .BR mke2fs (8) will use the default filesystem type @@ -637,6 +654,29 @@ Verbose execution. Print the version number of .B mke2fs and exit. +.SH ENVIRONMENT +.TP +.BI MKE2FS_SYNC +If set to non-zero integer value, its value is used to determine how often +.BR sync (2) +is called during inode table initialization. +.TP +.BI MKE2FS_CONFIG +Determines the location of the configuration file (see +.BR mke2fs.conf (5)). +.TP +.BI MKE2FS_FIRST_META_BG +If set to non-zero integer value, its value is used to determine first meta +block group. This is mostly for debugging purposes. +.TP +.BI MKE2FS_DEVICE_SECTSIZE +If set to non-zero integer value, its value is used to determine physical +sector size of the +.IR device . +.TP +.BI MKE2FS_SKIP_CHECK_MSG +If set, do not show the message of filesystem automatic check caused by +mount count or check interval. .SH AUTHOR This version of .B mke2fs diff --git a/misc/mke2fs.c b/misc/mke2fs.c index 93ae4bcc..e062bda0 100644 --- a/misc/mke2fs.c +++ b/misc/mke2fs.c @@ -53,6 +53,7 @@ extern int optind; #include <blkid/blkid.h> #include "ext2fs/ext2_fs.h" +#include "ext2fs/ext2fsP.h" #include "et/com_err.h" #include "uuid/uuid.h" #include "e2p/e2p.h" @@ -65,10 +66,14 @@ extern int optind; #define STRIDE_LENGTH 8 +#define MAX_32_NUM ((((unsigned long long) 1) << 32) - 1) + #ifndef __sparc__ #define ZAP_BOOTBLOCK #endif +#define DISCARD_STEP_MB (2048) + extern int isatty(int); extern FILE *fpopen(const char *cmd, const char *mode); @@ -118,7 +123,7 @@ static void usage(void) exit(1); } -static int int_log2(int arg) +static int int_log2(unsigned long long arg) { int l = 0; @@ -130,7 +135,7 @@ static int int_log2(int arg) return l; } -static int int_log10(unsigned int arg) +static int int_log10(unsigned long long arg) { int l; @@ -203,9 +208,9 @@ static void test_disk(ext2_filsys fs, badblocks_list *bb_list) errcode_t retval; char buf[1024]; - sprintf(buf, "badblocks -b %d -X %s%s%s %u", fs->blocksize, + sprintf(buf, "badblocks -b %d -X %s%s%s %llu", fs->blocksize, quiet ? "" : "-s ", (cflag > 1) ? "-w " : "", - fs->device_name, fs->super->s_blocks_count-1); + fs->device_name, ext2fs_blocks_count(fs->super)-1); if (verbose) printf(_("Running command: %s\n"), buf); f = popen(buf, "r"); @@ -274,10 +279,10 @@ _("Warning: the backup superblock/group descriptors at block %u contain\n" " bad blocks.\n\n"), group_block); group_bad++; - group = ext2fs_group_of_blk(fs, group_block+j); - fs->group_desc[group].bg_free_blocks_count++; + group = ext2fs_group_of_blk2(fs, group_block+j); + ext2fs_bg_free_blocks_count_set(fs, group, ext2fs_bg_free_blocks_count(fs, group) + 1); ext2fs_group_desc_csum_set(fs, group); - fs->super->s_free_blocks_count++; + ext2fs_free_blocks_count_add(fs->super, 1); } } group_block += fs->super->s_blocks_per_group; @@ -293,100 +298,42 @@ _("Warning: the backup superblock/group descriptors at block %u contain\n" exit(1); } while (ext2fs_badblocks_list_iterate(bb_iter, &blk)) - ext2fs_mark_block_bitmap(fs->block_map, blk); + ext2fs_mark_block_bitmap2(fs->block_map, EXT2FS_B2C(fs, blk)); ext2fs_badblocks_list_iterate_end(bb_iter); } -/* - * These functions implement a generalized progress meter. - */ -struct progress_struct { - char format[20]; - char backup[80]; - __u32 max; - int skip_progress; -}; - -static void progress_init(struct progress_struct *progress, - const char *label,__u32 max) -{ - int i; - - memset(progress, 0, sizeof(struct progress_struct)); - if (quiet) - return; - - /* - * Figure out how many digits we need - */ - i = int_log10(max); - sprintf(progress->format, "%%%dd/%%%dld", i, i); - memset(progress->backup, '\b', sizeof(progress->backup)-1); - progress->backup[sizeof(progress->backup)-1] = 0; - if ((2*i)+1 < (int) sizeof(progress->backup)) - progress->backup[(2*i)+1] = 0; - progress->max = max; - - progress->skip_progress = 0; - if (getenv("MKE2FS_SKIP_PROGRESS")) - progress->skip_progress++; - - fputs(label, stdout); - fflush(stdout); -} - -static void progress_update(struct progress_struct *progress, __u32 val) -{ - if ((progress->format[0] == 0) || progress->skip_progress) - return; - printf(progress->format, val, progress->max); - fputs(progress->backup, stdout); -} - -static void progress_close(struct progress_struct *progress) -{ - if (progress->format[0] == 0) - return; - fputs(_("done \n"), stdout); -} - static void write_inode_tables(ext2_filsys fs, int lazy_flag, int itable_zeroed) { errcode_t retval; - blk_t blk; + blk64_t blk; dgrp_t i; - int num, ipb; - struct progress_struct progress; + int num; + struct ext2fs_numeric_progress_struct progress; - if (quiet) - memset(&progress, 0, sizeof(progress)); - else - progress_init(&progress, _("Writing inode tables: "), - fs->group_desc_count); + ext2fs_numeric_progress_init(fs, &progress, + _("Writing inode tables: "), + fs->group_desc_count); for (i = 0; i < fs->group_desc_count; i++) { - progress_update(&progress, i); + ext2fs_numeric_progress_update(fs, &progress, i); - blk = fs->group_desc[i].bg_inode_table; + blk = ext2fs_inode_table_loc(fs, i); num = fs->inode_blocks_per_group; - if (lazy_flag) { - ipb = fs->blocksize / EXT2_INODE_SIZE(fs->super); - num = ((((fs->super->s_inodes_per_group - - fs->group_desc[i].bg_itable_unused) * - EXT2_INODE_SIZE(fs->super)) + - EXT2_BLOCK_SIZE(fs->super) - 1) / - EXT2_BLOCK_SIZE(fs->super)); - } + if (lazy_flag) + num = ext2fs_div_ceil((fs->super->s_inodes_per_group - + ext2fs_bg_itable_unused(fs, i)) * + EXT2_INODE_SIZE(fs->super), + EXT2_BLOCK_SIZE(fs->super)); if (!lazy_flag || itable_zeroed) { /* The kernel doesn't need to zero the itable blocks */ - fs->group_desc[i].bg_flags |= EXT2_BG_INODE_ZEROED; + ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_ZEROED); ext2fs_group_desc_csum_set(fs, i); } - retval = ext2fs_zero_blocks(fs, blk, num, &blk, &num); + retval = ext2fs_zero_blocks2(fs, blk, num, &blk, &num); if (retval) { fprintf(stderr, _("\nCould not write %d " - "blocks in inode table starting at %u: %s\n"), + "blocks in inode table starting at %llu: %s\n"), num, blk, error_message(retval)); exit(1); } @@ -397,8 +344,9 @@ static void write_inode_tables(ext2_filsys fs, int lazy_flag, int itable_zeroed) sync(); } } - ext2fs_zero_blocks(0, 0, 0, 0, 0); - progress_close(&progress); + ext2fs_zero_blocks2(0, 0, 0, 0, 0); + ext2fs_numeric_progress_close(fs, &progress, + _("done \n")); } static void create_root_dir(ext2_filsys fs) @@ -478,7 +426,7 @@ static void create_bad_block_inode(ext2_filsys fs, badblocks_list bb_list) { errcode_t retval; - ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_BAD_INO); + ext2fs_mark_inode_bitmap2(fs->inode_map, EXT2_BAD_INO); ext2fs_inode_alloc_stats2(fs, EXT2_BAD_INO, +1, 0); retval = ext2fs_update_bb_inode(fs, bb_list); if (retval) { @@ -517,7 +465,7 @@ static void zap_sector(ext2_filsys fs, int sect, int nsect) if (sect == 0) { /* Check for a BSD disklabel, and don't erase it if so */ - retval = io_channel_read_blk(fs->io, 0, -512, buf); + retval = io_channel_read_blk64(fs->io, 0, -512, buf); if (retval) fprintf(stderr, _("Warning: could not read block 0: %s\n"), @@ -532,7 +480,7 @@ static void zap_sector(ext2_filsys fs, int sect, int nsect) memset(buf, 0, 512*nsect); io_channel_set_blksize(fs->io, 512); - retval = io_channel_write_blk(fs->io, sect, -512*nsect, buf); + retval = io_channel_write_blk64(fs->io, sect, -512*nsect, buf); io_channel_set_blksize(fs->io, fs->blocksize); free(buf); if (retval) @@ -542,55 +490,57 @@ static void zap_sector(ext2_filsys fs, int sect, int nsect) static void create_journal_dev(ext2_filsys fs) { - struct progress_struct progress; + struct ext2fs_numeric_progress_struct progress; errcode_t retval; char *buf; - blk_t blk, err_blk; + blk64_t blk, err_blk; int c, count, err_count; retval = ext2fs_create_journal_superblock(fs, - fs->super->s_blocks_count, 0, &buf); + ext2fs_blocks_count(fs->super), 0, &buf); if (retval) { com_err("create_journal_dev", retval, _("while initializing journal superblock")); exit(1); } - if (quiet) - memset(&progress, 0, sizeof(progress)); - else - progress_init(&progress, _("Zeroing journal device: "), - fs->super->s_blocks_count); + if (journal_flags & EXT2_MKJOURNAL_LAZYINIT) + goto write_superblock; + + ext2fs_numeric_progress_init(fs, &progress, + _("Zeroing journal device: "), + ext2fs_blocks_count(fs->super)); blk = 0; - count = fs->super->s_blocks_count; + count = ext2fs_blocks_count(fs->super); while (count > 0) { if (count > 1024) c = 1024; else c = count; - retval = ext2fs_zero_blocks(fs, blk, c, &err_blk, &err_count); + retval = ext2fs_zero_blocks2(fs, blk, c, &err_blk, &err_count); if (retval) { com_err("create_journal_dev", retval, _("while zeroing journal device " - "(block %u, count %d)"), + "(block %llu, count %d)"), err_blk, err_count); exit(1); } blk += c; count -= c; - progress_update(&progress, blk); + ext2fs_numeric_progress_update(fs, &progress, blk); } - ext2fs_zero_blocks(0, 0, 0, 0, 0); + ext2fs_zero_blocks2(0, 0, 0, 0, 0); - retval = io_channel_write_blk(fs->io, - fs->super->s_first_data_block+1, - 1, buf); + ext2fs_numeric_progress_close(fs, &progress, NULL); +write_superblock: + retval = io_channel_write_blk64(fs->io, + fs->super->s_first_data_block+1, + 1, buf); if (retval) { com_err("create_journal_dev", retval, _("while writing journal superblock")); exit(1); } - progress_close(&progress); } static void show_stats(ext2_filsys fs) @@ -598,22 +548,21 @@ static void show_stats(ext2_filsys fs) struct ext2_super_block *s = fs->super; char buf[80]; char *os; - blk_t group_block; + blk64_t group_block; dgrp_t i; int need, col_left; - if (fs_param.s_blocks_count != s->s_blocks_count) - fprintf(stderr, _("warning: %u blocks unused.\n\n"), - fs_param.s_blocks_count - s->s_blocks_count); + if (ext2fs_blocks_count(&fs_param) != ext2fs_blocks_count(s)) + fprintf(stderr, _("warning: %llu blocks unused.\n\n"), + ext2fs_blocks_count(&fs_param) - ext2fs_blocks_count(s)); memset(buf, 0, sizeof(buf)); strncpy(buf, s->s_volume_name, sizeof(s->s_volume_name)); printf(_("Filesystem label=%s\n"), buf); - fputs(_("OS type: "), stdout); - os = e2p_os2string(fs->super->s_creator_os); - fputs(os, stdout); + os = e2p_os2string(fs->super->s_creator_os); + if (os) + printf(_("OS type: %s\n"), os); free(os); - printf("\n"); printf(_("Block size=%u (log=%u)\n"), fs->blocksize, s->s_log_block_size); if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, @@ -626,11 +575,11 @@ static void show_stats(ext2_filsys fs) s->s_log_cluster_size); printf(_("Stride=%u blocks, Stripe width=%u blocks\n"), s->s_raid_stride, s->s_raid_stripe_width); - printf(_("%u inodes, %u blocks\n"), s->s_inodes_count, - s->s_blocks_count); - printf(_("%u blocks (%2.2f%%) reserved for the super user\n"), - s->s_r_blocks_count, - 100.0 * s->s_r_blocks_count / s->s_blocks_count); + printf(_("%u inodes, %llu blocks\n"), s->s_inodes_count, + ext2fs_blocks_count(s)); + printf(_("%llu blocks (%2.2f%%) reserved for the super user\n"), + ext2fs_r_blocks_count(s), + 100.0 * ext2fs_r_blocks_count(s) / ext2fs_blocks_count(s)); printf(_("First data block=%u\n"), s->s_first_data_block); if (s->s_reserved_gdt_blocks) printf(_("Maximum filesystem blocks=%lu\n"), @@ -669,7 +618,7 @@ static void show_stats(ext2_filsys fs) col_left = 72; } col_left -= need; - printf("%u", group_block); + printf("%llu", group_block); } printf("\n\n"); } @@ -754,7 +703,8 @@ static void parse_extended_opts(struct ext2_super_block *param, continue; } } else if (!strcmp(token, "resize")) { - unsigned long resize, bpg, rsv_groups; + blk64_t resize; + unsigned long bpg, rsv_groups; unsigned long group_desc_count, desc_blocks; unsigned int gdpb, blocksize; int rsv_gdb; @@ -765,8 +715,8 @@ static void parse_extended_opts(struct ext2_super_block *param, continue; } - resize = parse_num_blocks(arg, - param->s_log_block_size); + resize = parse_num_blocks2(arg, + param->s_log_block_size); if (resize == 0) { fprintf(stderr, @@ -775,7 +725,7 @@ static void parse_extended_opts(struct ext2_super_block *param, r_usage++; continue; } - if (resize <= param->s_blocks_count) { + if (resize <= ext2fs_blocks_count(param)) { fprintf(stderr, _("The resize maximum must be greater " "than the filesystem size.\n")); @@ -788,11 +738,11 @@ static void parse_extended_opts(struct ext2_super_block *param, if (!bpg) bpg = blocksize * 8; gdpb = EXT2_DESC_PER_BLOCK(param); - group_desc_count = - ext2fs_div_ceil(param->s_blocks_count, bpg); + group_desc_count = (__u32) ext2fs_div64_ceil( + ext2fs_blocks_count(param), bpg); desc_blocks = (group_desc_count + gdpb - 1) / gdpb; - rsv_groups = ext2fs_div_ceil(resize, bpg); + rsv_groups = ext2fs_div64_ceil(resize, bpg); rsv_gdb = ext2fs_div_ceil(rsv_groups, gdpb) - desc_blocks; if (rsv_gdb > (int) EXT2_ADDR_PER_BLOCK(param)) @@ -817,6 +767,12 @@ static void parse_extended_opts(struct ext2_super_block *param, lazy_itable_init = strtoul(arg, &p, 0); else lazy_itable_init = 1; + } else if (!strcmp(token, "lazy_journal_init")) { + if (arg) + journal_flags |= strtoul(arg, &p, 0) ? + EXT2_MKJOURNAL_LAZYINIT : 0; + else + journal_flags |= EXT2_MKJOURNAL_LAZYINIT; } else if (!strcmp(token, "discard")) { discard = 1; } else if (!strcmp(token, "nodiscard")) { @@ -836,6 +792,7 @@ static void parse_extended_opts(struct ext2_super_block *param, "\tstripe-width=<RAID stride * data disks in blocks>\n" "\tresize=<resize maximum size in blocks>\n" "\tlazy_itable_init=<0 to disable, 1 to enable>\n" + "\tlazy_journal_init=<0 to disable, 1 to enable>\n" "\ttest_fs\n" "\tdiscard\n" "\tnodiscard\n\n"), @@ -863,14 +820,16 @@ static __u32 ok_features[3] = { EXT3_FEATURE_INCOMPAT_EXTENTS| EXT3_FEATURE_INCOMPAT_JOURNAL_DEV| EXT2_FEATURE_INCOMPAT_META_BG| - EXT4_FEATURE_INCOMPAT_FLEX_BG, + EXT4_FEATURE_INCOMPAT_FLEX_BG| + EXT4_FEATURE_INCOMPAT_64BIT, /* R/O compat */ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| EXT4_FEATURE_RO_COMPAT_HUGE_FILE| EXT4_FEATURE_RO_COMPAT_DIR_NLINK| EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE| EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| - EXT4_FEATURE_RO_COMPAT_GDT_CSUM + EXT4_FEATURE_RO_COMPAT_GDT_CSUM| + EXT4_FEATURE_RO_COMPAT_BIGALLOC }; @@ -989,6 +948,7 @@ static int profile_has_subsection(profile_t profile, const char *section, static char **parse_fs_type(const char *fs_type, const char *usage_types, struct ext2_super_block *fs_param, + blk64_t fs_blocks_count, char *progname) { const char *ext_type = 0; @@ -997,7 +957,7 @@ static char **parse_fs_type(const char *fs_type, char *cp, *t; const char *size_type; struct str_list list; - unsigned long meg; + unsigned long long meg; int is_hurd = 0; if (init_list(&list)) @@ -1053,12 +1013,16 @@ static char **parse_fs_type(const char *fs_type, } meg = (1024 * 1024) / EXT2_BLOCK_SIZE(fs_param); - if (fs_param->s_blocks_count < 3 * meg) + if (fs_blocks_count < 3 * meg) size_type = "floppy"; - else if (fs_param->s_blocks_count < 512 * meg) + else if (fs_blocks_count < 512 * meg) size_type = "small"; - else + else if (fs_blocks_count < 4 * 1024 * 1024 * meg) size_type = "default"; + else if (fs_blocks_count < 16 * 1024 * 1024 * meg) + size_type = "big"; + else + size_type = "huge"; if (!usage_types) usage_types = size_type; @@ -1132,6 +1096,18 @@ static int get_int_from_profile(char **fs_types, const char *opt, int def_val) return ret; } +static double get_double_from_profile(char **fs_types, const char *opt, + double def_val) +{ + double ret; + char **cpp; + + profile_get_double(profile, "defaults", opt, 0, def_val, &ret); + for (cpp = fs_types; *cpp; cpp++) + profile_get_double(profile, "fs_types", *cpp, opt, ret, &ret); + return ret; +} + static int get_bool_from_profile(char **fs_types, const char *opt, int def_val) { int ret; @@ -1201,22 +1177,23 @@ out: static void PRS(int argc, char *argv[]) { int b, c; - int size; + int cluster_size = 0; char *tmp, **cpp; int blocksize = 0; int inode_ratio = 0; int inode_size = 0; unsigned long flex_bg_size = 0; - double reserved_ratio = 5.0; + double reserved_ratio = -1.0; int lsector_size = 0, psector_size = 0; int show_version_only = 0; unsigned long long num_inodes = 0; /* unsigned long long to catch too-large input */ errcode_t retval; char * oldpath = getenv("PATH"); char * extended_opts = 0; - const char * fs_type = 0; - const char * usage_types = 0; - blk_t dev_size; + char * fs_type = 0; + char * usage_types = 0; + blk64_t dev_size; + blk64_t fs_blocks_count = 0; #ifdef __linux__ struct utsname ut; #endif @@ -1230,6 +1207,10 @@ static void PRS(int argc, char *argv[]) if (oldpath) pathlen += strlen(oldpath); newpath = malloc(pathlen); + if (!newpath) { + fprintf(stderr, _("Couldn't allocate memory for new PATH.\n")); + exit(1); + } strcpy(newpath, PATH_SET); /* Update our PATH to include /sbin */ @@ -1260,8 +1241,17 @@ static void PRS(int argc, char *argv[]) profile_set_syntax_err_cb(syntax_err_report); retval = profile_init(config_fn, &profile); if (retval == ENOENT) { - profile_init(default_files, &profile); - profile_set_default(profile, mke2fs_default_profile); + retval = profile_init(default_files, &profile); + if (retval) + goto profile_error; + retval = profile_set_default(profile, mke2fs_default_profile); + if (retval) + goto profile_error; + } else if (retval) { +profile_error: + fprintf(stderr, _("Couldn't init profile successfully" + " (error: %ld).\n"), retval); + exit(1); } setbuf(stdout, NULL); @@ -1291,7 +1281,7 @@ static void PRS(int argc, char *argv[]) } while ((c = getopt (argc, argv, - "b:cf:g:G:i:jl:m:no:qr:s:t:vE:FI:J:KL:M:N:O:R:ST:U:V")) != EOF) { + "b:cg:i:jl:m:no:qr:s:t:vC:E:FG:I:J:KL:M:N:O:R:ST:U:V")) != EOF) { switch (c) { case 'b': blocksize = strtol(optarg, &tmp, 0); @@ -1314,17 +1304,15 @@ static void PRS(int argc, char *argv[]) case 'c': /* Check for bad blocks */ cflag++; break; - case 'f': - size = strtoul(optarg, &tmp, 0); - if (size < EXT2_MIN_BLOCK_SIZE || - size > EXT2_MAX_BLOCK_SIZE || *tmp) { + case 'C': + cluster_size = strtoul(optarg, &tmp, 0); + if (cluster_size < EXT2_MIN_CLUSTER_SIZE || + cluster_size > EXT2_MAX_CLUSTER_SIZE || *tmp) { com_err(program_name, 0, _("invalid fragment size - %s"), optarg); exit(1); } - fprintf(stderr, _("Warning: fragments not supported. " - "Ignoring -f option\n")); break; case 'g': fs_param.s_blocks_per_group = strtoul(optarg, &tmp, 0); @@ -1458,10 +1446,10 @@ static void PRS(int argc, char *argv[]) super_only = 1; break; case 't': - fs_type = optarg; + fs_type = strdup(optarg); break; case 'T': - usage_types = optarg; + usage_types = strdup(optarg); break; case 'U': fs_uuid = optarg; @@ -1538,11 +1526,11 @@ static void PRS(int argc, char *argv[]) blocksize, sys_page_size); } if (optind < argc) { - fs_param.s_blocks_count = parse_num_blocks(argv[optind++], - fs_param.s_log_block_size); - if (!fs_param.s_blocks_count) { + fs_blocks_count = parse_num_blocks2(argv[optind++], + fs_param.s_log_block_size); + if (!fs_blocks_count) { com_err(program_name, 0, - _("invalid blocks count '%s' on device '%s'"), + _("invalid blocks '%s' on device '%s'"), argv[optind - 1], device_name); exit(1); } @@ -1554,54 +1542,21 @@ static void PRS(int argc, char *argv[]) check_plausibility(device_name); check_mount(device_name, force, _("filesystem")); - fs_param.s_log_cluster_size = fs_param.s_log_block_size; - - if (noaction && fs_param.s_blocks_count) { - dev_size = fs_param.s_blocks_count; + /* Determine the size of the device (if possible) */ + if (noaction && fs_blocks_count) { + dev_size = fs_blocks_count; retval = 0; - } else { - retry: - retval = ext2fs_get_device_size(device_name, - EXT2_BLOCK_SIZE(&fs_param), - &dev_size); - if ((retval == EFBIG) && - (blocksize == 0) && - (fs_param.s_log_block_size == 0)) { - fs_param.s_log_block_size = 2; - blocksize = 4096; - goto retry; - } - } - - if (retval == EFBIG) { - blk64_t big_dev_size; - - if (blocksize < 4096) { - fs_param.s_log_block_size = 2; - blocksize = 4096; - } + } else retval = ext2fs_get_device_size2(device_name, - EXT2_BLOCK_SIZE(&fs_param), &big_dev_size); - if (retval) - goto get_size_failure; - if (big_dev_size == (1ULL << 32)) { - dev_size = (blk_t) (big_dev_size - 1); - goto got_size; - } - fprintf(stderr, _("%s: Size of device %s too big " - "to be expressed in 32 bits\n\t" - "using a blocksize of %d.\n"), - program_name, device_name, EXT2_BLOCK_SIZE(&fs_param)); - exit(1); - } -get_size_failure: + EXT2_BLOCK_SIZE(&fs_param), + &dev_size); + if (retval && (retval != EXT2_ET_UNIMPLEMENTED)) { com_err(program_name, retval, _("while trying to determine filesystem size")); exit(1); } -got_size: - if (!fs_param.s_blocks_count) { + if (!fs_blocks_count) { if (retval == EXT2_ET_UNIMPLEMENTED) { com_err(program_name, 0, _("Couldn't determine device size; you " @@ -1621,19 +1576,32 @@ got_size: )); exit(1); } - fs_param.s_blocks_count = dev_size; + fs_blocks_count = dev_size; if (sys_page_size > EXT2_BLOCK_SIZE(&fs_param)) - fs_param.s_blocks_count &= ~((sys_page_size / - EXT2_BLOCK_SIZE(&fs_param))-1); + fs_blocks_count &= ~((blk64_t) ((sys_page_size / + EXT2_BLOCK_SIZE(&fs_param))-1)); } - - } else if (!force && (fs_param.s_blocks_count > dev_size)) { + } else if (!force && (fs_blocks_count > dev_size)) { com_err(program_name, 0, _("Filesystem larger than apparent device size.")); proceed_question(); } - fs_types = parse_fs_type(fs_type, usage_types, &fs_param, argv[0]); + if (!fs_type) + profile_get_string(profile, "devices", device_name, + "fs_type", 0, &fs_type); + if (!usage_types) + profile_get_string(profile, "devices", device_name, + "usage_types", 0, &usage_types); + + /* + * We have the file system (or device) size, so we can now + * determine the appropriate file system types so the fs can + * be appropriately configured. + */ + fs_types = parse_fs_type(fs_type, usage_types, &fs_param, + fs_blocks_count ? fs_blocks_count : dev_size, + argv[0]); if (!fs_types) { fprintf(stderr, _("Failed to parse fs types list\n")); exit(1); @@ -1661,14 +1629,43 @@ got_size: "features", "", &tmp); if (tmp && *tmp) edit_feature(tmp, &fs_param.s_feature_compat); - free(tmp); + if (tmp) + free(tmp); } tmp = get_string_from_profile(fs_types, "default_features", ""); } edit_feature(fs_features ? fs_features : tmp, &fs_param.s_feature_compat); - free(tmp); + if (tmp) + free(tmp); + + /* + * We now need to do a sanity check of fs_blocks_count for + * 32-bit vs 64-bit block number support. + */ + if ((fs_blocks_count > MAX_32_NUM) && (blocksize == 0)) { + fs_blocks_count /= 4; /* Try using a 4k blocksize */ + blocksize = 4096; + fs_param.s_log_block_size = 2; + } + if ((fs_blocks_count > MAX_32_NUM) && + !(fs_param.s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) && + get_bool_from_profile(fs_types, "auto_64-bit_support", 0)) { + fs_param.s_feature_incompat |= EXT4_FEATURE_INCOMPAT_64BIT; + fs_param.s_feature_compat &= ~EXT2_FEATURE_COMPAT_RESIZE_INODE; + } + if ((fs_blocks_count > MAX_32_NUM) && + !(fs_param.s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT)) { + fprintf(stderr, _("%s: Size of device (0x%llx blocks) %s " + "too big to be expressed\n\t" + "in 32 bits using a blocksize of %d.\n"), + program_name, fs_blocks_count, device_name, + EXT2_BLOCK_SIZE(&fs_param)); + exit(1); + } + + ext2fs_blocks_count_set(&fs_param, fs_blocks_count); if (fs_param.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { fs_types[0] = strdup("journal"); @@ -1710,6 +1707,18 @@ got_size: EXT3_FEATURE_COMPAT_HAS_JOURNAL; } + /* Get reserved_ratio from profile if not specified on cmd line. */ + if (reserved_ratio < 0.0) { + reserved_ratio = get_double_from_profile( + fs_types, "reserved_ratio", 5.0); + if (reserved_ratio > 50 || reserved_ratio < 0) { + com_err(program_name, 0, + _("invalid reserved blocks percent - %lf"), + reserved_ratio); + exit(1); + } + } + if (fs_param.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { reserved_ratio = 0; @@ -1770,7 +1779,9 @@ got_size: if ((blocksize < 0) && (use_bsize < (-blocksize))) use_bsize = -blocksize; blocksize = use_bsize; - fs_param.s_blocks_count /= blocksize / 1024; + ext2fs_blocks_count_set(&fs_param, + ext2fs_blocks_count(&fs_param) / + (blocksize / 1024)); } else { if (blocksize < lsector_size) { /* Impossible */ com_err(program_name, EINVAL, @@ -1785,16 +1796,27 @@ got_size: } } + fs_param.s_log_block_size = + int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE); + if (fs_param.s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_BIGALLOC) { + if (!cluster_size) + cluster_size = get_int_from_profile(fs_types, + "cluster_size", + blocksize*16); + fs_param.s_log_cluster_size = + int_log2(cluster_size >> EXT2_MIN_CLUSTER_LOG_SIZE); + } else + fs_param.s_log_cluster_size = fs_param.s_log_block_size; + if (inode_ratio == 0) { inode_ratio = get_int_from_profile(fs_types, "inode_ratio", 8192); if (inode_ratio < blocksize) inode_ratio = blocksize; + if (inode_ratio < EXT2_CLUSTER_SIZE(&fs_param)) + inode_ratio = EXT2_CLUSTER_SIZE(&fs_param); } - fs_param.s_log_cluster_size = fs_param.s_log_block_size = - int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE); - #ifdef HAVE_BLKID_PROBE_GET_TOPOLOGY retval = get_device_geometry(device_name, &fs_param, psector_size); if (retval < 0) { @@ -1819,6 +1841,9 @@ got_size: "lazy_itable_init", lazy_itable_init); discard = get_bool_from_profile(fs_types, "discard" , discard); + journal_flags |= get_bool_from_profile(fs_types, + "lazy_journal_init", 0) ? + EXT2_MKJOURNAL_LAZYINIT : 0; /* Get options from profile */ for (cpp = fs_types; *cpp; cpp++) { @@ -1885,13 +1910,19 @@ got_size: /* Make sure number of inodes specified will fit in 32 bits */ if (num_inodes == 0) { unsigned long long n; - n = (unsigned long long) fs_param.s_blocks_count * blocksize / inode_ratio; - if (n > ~0U) { - com_err(program_name, 0, - _("too many inodes (%llu), raise inode ratio?"), n); - exit(1); + n = ext2fs_blocks_count(&fs_param) * blocksize / inode_ratio; + if (n > MAX_32_NUM) { + if (fs_param.s_feature_incompat & + EXT4_FEATURE_INCOMPAT_64BIT) + num_inodes = MAX_32_NUM; + else { + com_err(program_name, 0, + _("too many inodes (%llu), raise" + "inode ratio?"), n); + exit(1); + } } - } else if (num_inodes > ~0U) { + } else if (num_inodes > MAX_32_NUM) { com_err(program_name, 0, _("too many inodes (%llu), specify < 2^32 inodes"), num_inodes); @@ -1901,29 +1932,31 @@ got_size: * Calculate number of inodes based on the inode ratio */ fs_param.s_inodes_count = num_inodes ? num_inodes : - ((__u64) fs_param.s_blocks_count * blocksize) - / inode_ratio; + (ext2fs_blocks_count(&fs_param) * blocksize) / inode_ratio; if ((((long long)fs_param.s_inodes_count) * (inode_size ? inode_size : EXT2_GOOD_OLD_INODE_SIZE)) >= - (((long long)fs_param.s_blocks_count) * + ((ext2fs_blocks_count(&fs_param)) * EXT2_BLOCK_SIZE(&fs_param))) { com_err(program_name, 0, _("inode_size (%u) * inodes_count " "(%u) too big for a\n\t" - "filesystem with %lu blocks, " + "filesystem with %llu blocks, " "specify higher inode_ratio (-i)\n\t" "or lower inode count (-N).\n"), inode_size ? inode_size : EXT2_GOOD_OLD_INODE_SIZE, fs_param.s_inodes_count, - (unsigned long) fs_param.s_blocks_count); + (unsigned long long) ext2fs_blocks_count(&fs_param)); exit(1); } /* * Calculate number of blocks to reserve */ - fs_param.s_r_blocks_count = (unsigned int) (reserved_ratio * - fs_param.s_blocks_count / 100.0); + ext2fs_r_blocks_count_set(&fs_param, reserved_ratio * + ext2fs_blocks_count(&fs_param) / 100.0); + + free(fs_type); + free(usage_types); } static int should_do_undo(const char *name) @@ -1954,7 +1987,7 @@ static int should_do_undo(const char *name) } io_channel_set_blksize(channel, SUPERBLOCK_OFFSET); - retval = io_channel_read_blk(channel, 1, -SUPERBLOCK_SIZE, &super); + retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super); if (retval) { retval = 0; goto err_out; @@ -1979,8 +2012,8 @@ open_err_out: static int mke2fs_setup_tdb(const char *name, io_manager *io_ptr) { - errcode_t retval = 0; - char *tdb_dir, *tdb_file; + errcode_t retval = ENOMEM; + char *tdb_dir, *tdb_file = NULL; char *device_name, *tmp_name; /* @@ -1998,103 +2031,111 @@ static int mke2fs_setup_tdb(const char *name, io_manager *io_ptr) return 0; tmp_name = strdup(name); - if (!tmp_name) { - alloc_fn_fail: - com_err(program_name, ENOMEM, - _("Couldn't allocate memory for tdb filename\n")); - return ENOMEM; - } + if (!tmp_name) + goto errout; device_name = basename(tmp_name); tdb_file = malloc(strlen(tdb_dir) + 8 + strlen(device_name) + 7 + 1); - if (!tdb_file) - goto alloc_fn_fail; + if (!tdb_file) { + free(tmp_name); + goto errout; + } sprintf(tdb_file, "%s/mke2fs-%s.e2undo", tdb_dir, device_name); + free(tmp_name); if (!access(tdb_file, F_OK)) { if (unlink(tdb_file) < 0) { retval = errno; - com_err(program_name, retval, - _("while trying to delete %s"), - tdb_file); - free(tdb_file); - return retval; + goto errout; } } set_undo_io_backing_manager(*io_ptr); *io_ptr = undo_io_manager; - set_undo_io_backup_file(tdb_file); + retval = set_undo_io_backup_file(tdb_file); + if (retval) + goto errout; printf(_("Overwriting existing filesystem; this can be undone " "using the command:\n" " e2undo %s %s\n\n"), tdb_file, name); free(tdb_file); - free(tmp_name); + return 0; + +errout: + free(tdb_file); + com_err(program_name, retval, + _("while trying to setup undo file\n")); return retval; } -#ifdef __linux__ +static int mke2fs_discard_device(ext2_filsys fs) +{ + struct ext2fs_numeric_progress_struct progress; + blk64_t blocks = ext2fs_blocks_count(fs->super); + blk64_t count = DISCARD_STEP_MB; + blk64_t cur = 0; + int retval = 0; -#ifndef BLKDISCARD -#define BLKDISCARD _IO(0x12,119) -#endif + retval = io_channel_discard(fs->io, 0, 0); + if (retval) + return retval; -#ifndef BLKDISCARDZEROES -#define BLKDISCARDZEROES _IO(0x12,124) -#endif + count *= (1024 * 1024); + count /= fs->blocksize; -/* - * Return zero if the discard succeeds, and -1 if the discard fails. - */ -static int mke2fs_discard_blocks(ext2_filsys fs) -{ - int fd; - int ret; - int blocksize; - __u64 blocks; - __uint64_t range[2]; - - blocks = fs->super->s_blocks_count; - blocksize = EXT2_BLOCK_SIZE(fs->super); - range[0] = 0; - range[1] = blocks * blocksize; - - fd = open64(fs->device_name, O_RDWR); - - if (fd > 0) { - ret = ioctl(fd, BLKDISCARD, &range); - if (verbose) { - printf(_("Calling BLKDISCARD from %llu to %llu "), - (unsigned long long) range[0], - (unsigned long long) range[1]); - if (ret) - printf(_("failed.\n")); - else - printf(_("succeeded.\n")); - } - close(fd); + ext2fs_numeric_progress_init(fs, &progress, + _("Discarding device blocks: "), + blocks); + while (cur < blocks) { + ext2fs_numeric_progress_update(fs, &progress, cur); + + if (cur + count > blocks) + count = blocks - cur; + + retval = io_channel_discard(fs->io, cur, count); + if (retval) + break; + cur += count; } - return ret; -} -static int mke2fs_discard_zeroes_data(ext2_filsys fs) -{ - int fd; - int ret; - int discard_zeroes_data = 0; + if (retval) { + ext2fs_numeric_progress_close(fs, &progress, + _("failed - ")); + if (!quiet) + printf("%s\n",error_message(retval)); + } else + ext2fs_numeric_progress_close(fs, &progress, + _("done \n")); - fd = open64(fs->device_name, O_RDWR); + return retval; +} - if (fd > 0) { - ioctl(fd, BLKDISCARDZEROES, &discard_zeroes_data); - close(fd); +static void fix_cluster_bg_counts(ext2_filsys fs) +{ + blk64_t cluster, num_clusters, tot_free; + int grp_free, num_free, group, num; + + num_clusters = EXT2FS_B2C(fs, ext2fs_blocks_count(fs->super)); + tot_free = num_free = num = group = grp_free = 0; + for (cluster = EXT2FS_B2C(fs, fs->super->s_first_data_block); + cluster < num_clusters; cluster++) { + if (!ext2fs_test_block_bitmap2(fs->block_map, + EXT2FS_C2B(fs, cluster))) { + grp_free++; + tot_free++; + } + num++; + if ((num == fs->super->s_clusters_per_group) || + (cluster == num_clusters-1)) { + ext2fs_bg_free_blocks_count_set(fs, group, grp_free); + ext2fs_group_desc_csum_set(fs, group); + num = 0; + grp_free = 0; + group++; + } } - return discard_zeroes_data; + ext2fs_free_blocks_count_set(fs->super, EXT2FS_C2B(fs, tot_free)); } -#else -#define mke2fs_discard_blocks(fs) 1 -#define mke2fs_discard_zeroes_data(fs) 0 -#endif int main (int argc, char *argv[]) { @@ -2104,6 +2145,8 @@ int main (int argc, char *argv[]) unsigned int journal_blocks; unsigned int i; int val, hash_alg; + int flags; + int old_bitmaps; io_manager io_ptr; char tdb_string[40]; char *hash_alg_str; @@ -2134,8 +2177,19 @@ int main (int argc, char *argv[]) /* * Initialize the superblock.... */ - retval = ext2fs_initialize(device_name, EXT2_FLAG_EXCLUSIVE, &fs_param, - io_ptr, &fs); + flags = EXT2_FLAG_EXCLUSIVE; + profile_get_boolean(profile, "options", "old_bitmaps", 0, 0, + &old_bitmaps); + if (!old_bitmaps) + flags |= EXT2_FLAG_64BITS; + /* + * By default, we print how many inode tables or block groups + * or whatever we've written so far. The quiet flag disables + * this, along with a lot of other output. + */ + if (!quiet) + flags |= EXT2_FLAG_PRINT_PROGRESS; + retval = ext2fs_initialize(device_name, flags, &fs_param, io_ptr, &fs); if (retval) { com_err(device_name, retval, _("while setting up superblock")); exit(1); @@ -2143,9 +2197,8 @@ int main (int argc, char *argv[]) /* Can't undo discard ... */ if (!noaction && discard && (io_ptr != undo_io_manager)) { - retval = mke2fs_discard_blocks(fs); - - if (!retval && mke2fs_discard_zeroes_data(fs)) { + retval = mke2fs_discard_device(fs); + if (!retval && io_channel_discard_zeroes_data(fs->io)) { if (verbose) printf(_("Discard succeeded and will return 0s " " - skipping inode table wipe\n")); @@ -2280,21 +2333,33 @@ int main (int argc, char *argv[]) handle_bad_blocks(fs, bb_list); fs->stride = fs_stride = fs->super->s_raid_stride; + if (!quiet) + printf(_("Allocating group tables: ")); retval = ext2fs_allocate_tables(fs); if (retval) { com_err(program_name, retval, _("while trying to allocate filesystem tables")); exit(1); } + if (!quiet) + printf(_("done \n")); + + retval = ext2fs_convert_subcluster_bitmap(fs, &fs->block_map); + if (retval) { + com_err(program_name, retval, + _("\n\twhile converting subcluster bitmap")); + exit(1); + } + if (super_only) { fs->super->s_state |= EXT2_ERROR_FS; fs->flags &= ~(EXT2_FLAG_IB_DIRTY|EXT2_FLAG_BB_DIRTY); } else { /* rsv must be a power of two (64kB is MD RAID sb alignment) */ - unsigned int rsv = 65536 / fs->blocksize; - unsigned long blocks = fs->super->s_blocks_count; - unsigned long start; - blk_t ret_blk; + blk64_t rsv = 65536 / fs->blocksize; + blk64_t blocks = ext2fs_blocks_count(fs->super); + blk64_t start; + blk64_t ret_blk; #ifdef ZAP_BOOTBLOCK zap_sector(fs, 0, 2); @@ -2309,12 +2374,12 @@ int main (int argc, char *argv[]) if (start > rsv) start -= rsv; if (start > 0) - retval = ext2fs_zero_blocks(fs, start, blocks - start, + retval = ext2fs_zero_blocks2(fs, start, blocks - start, &ret_blk, NULL); if (retval) { com_err(program_name, retval, - _("while zeroing block %u at end of filesystem"), + _("while zeroing block %llu at end of filesystem"), ret_blk); } write_inode_tables(fs, lazy_itable_init, itable_zeroed); @@ -2398,6 +2463,9 @@ int main (int argc, char *argv[]) } no_journal: + if (EXT2_HAS_RO_COMPAT_FEATURE(&fs_param, + EXT4_FEATURE_RO_COMPAT_BIGALLOC)) + fix_cluster_bg_counts(fs); if (!quiet) printf(_("Writing superblocks and " "filesystem accounting information: ")); diff --git a/misc/mke2fs.conf b/misc/mke2fs.conf index b32a7394..775e0462 100644 --- a/misc/mke2fs.conf +++ b/misc/mke2fs.conf @@ -12,6 +12,7 @@ } ext4 = { features = has_journal,extent,huge_file,flex_bg,uninit_bg,dir_nlink,extra_isize + auto_64-bit_support = 1 inode_size = 256 } ext4dev = { diff --git a/misc/mke2fs.conf.5.in b/misc/mke2fs.conf.5.in index 7f140133..b2c7a57b 100644 --- a/misc/mke2fs.conf.5.in +++ b/misc/mke2fs.conf.5.in @@ -65,7 +65,7 @@ file. They will be described in more detail in future sections of this document. .TP .I [defaults] -Contains relations which define the default parameters +Contains relations which define the default parameters used by .BR mke2fs (8). In general, these defaults may be overridden by a definition in the @@ -168,6 +168,13 @@ specify one on the command line, and the filesystem-type specific section of the configuration file does not specify a default inode size. .TP +.I reserved_ratio +This relation specifies the default percentage of filesystem blocks +reserved for the super-user, if the user does not +specify one on the command line, and the filesystem-type +specific section of the configuration file does not specify a default +reserved ratio. This value can be a floating point number. +.TP .I undo_dir This relation specifies the directory where the undo file should be stored. It can be overridden via the @@ -314,6 +321,14 @@ relations. It may be overridden by the command-line option to .BR mke2fs (8). .TP +.I auto_64-bit_support +This relation is a boolean which specifies whether +.BR mke2fs (8) +should automatically add the 64bit feature if the number of blocks for +the file system requires this feature to be enabled. The resize_inode +feature is also automatically disabled since it doesn't support 64-bit +block numbers. +.TP .I default_mntopts This relation specifies the set of mount options which should be enabled by default. These may be changed at a later time with the @@ -345,6 +360,11 @@ specify one on the command line. This relation specifies the default inode size if the user does not specify one on the command line. .TP +.I reserved_ratio +This relation specifies the default percentage of filesystem blocks +reserved for the super-user, if the user does not specify one on the command +line. +.TP .I hash_alg This relation specifies the default hash algorithm used for the new filesystems with hashed b-tree directories. Valid algorithms @@ -375,6 +395,27 @@ on a per-filesystem type basis. This relation is a boolean which specifies whether the .BR mke2fs (8) should attempt to discard device prior to filesystem creation. +.TP +.I cluster_size +This relation specifies the default cluster size if the bigalloc file +system feature is enabled. It can be overridden via the +.B \-C +command line option to +.BR mke2fs (8) +.SH THE [devices] STANZA +Each tag in the +.I [devices] +stanza names device name so that per-device defaults can be specified. +.TP +.I fs_type +This relation specifies the default parameter for the +.B \-t +option, if this option isn't specified on the command line. +.TP +.I usage_types +This relation specifies the default parameter for the +.B \-T +option, if this option isn't specified on the command line. .SH FILES .TP .I /etc/mke2fs.conf diff --git a/misc/tune2fs.8.in b/misc/tune2fs.8.in index 2f9db811..233f85a8 100644 --- a/misc/tune2fs.8.in +++ b/misc/tune2fs.8.in @@ -491,6 +491,9 @@ The following filesystem features can be set or cleared using .B dir_index Use hashed b-trees to speed up lookups in large directories. .TP +.B dir_nlink +Allow more than 65000 subdirectories per directory. +.TP .B filetype Store file type information in directory entries. .TP diff --git a/misc/tune2fs.c b/misc/tune2fs.c index 0d1b7518..5bf51870 100644 --- a/misc/tune2fs.c +++ b/misc/tune2fs.c @@ -69,7 +69,8 @@ static int I_flag; static time_t last_check_time; static int print_label; static int max_mount_count, mount_count, mount_flags; -static unsigned long interval, reserved_blocks; +static unsigned long interval; +static blk64_t reserved_blocks; static double reserved_ratio; static unsigned long resgid, resuid; static unsigned short errors; @@ -89,8 +90,8 @@ static struct list_head blk_move_list; struct blk_move { struct list_head list; - blk_t old_loc; - blk_t new_loc; + blk64_t old_loc; + blk64_t new_loc; }; @@ -198,7 +199,7 @@ static void remove_journal_device(ext2_filsys fs) } /* Get the journal superblock */ - if ((retval = io_channel_read_blk(jfs->io, 1, -1024, buf))) { + if ((retval = io_channel_read_blk64(jfs->io, 1, -1024, buf))) { com_err(program_name, retval, _("while reading journal superblock")); goto no_valid_journal; @@ -230,7 +231,7 @@ static void remove_journal_device(ext2_filsys fs) jsb->s_nr_users = htonl(nr_users); /* Write back the journal superblock */ - if ((retval = io_channel_write_blk(jfs->io, 1, -1024, buf))) { + if ((retval = io_channel_write_blk64(jfs->io, 1, -1024, buf))) { com_err(program_name, retval, "while writing journal superblock."); goto no_valid_journal; @@ -251,19 +252,21 @@ no_valid_journal: } /* Helper function for remove_journal_inode */ -static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr, - int blockcnt EXT2FS_ATTR((unused)), +static int release_blocks_proc(ext2_filsys fs, blk64_t *blocknr, + e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), + blk64_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), void *private EXT2FS_ATTR((unused))) { - blk_t block; + blk64_t block; int group; block = *blocknr; - ext2fs_unmark_block_bitmap(fs->block_map, block); - group = ext2fs_group_of_blk(fs, block); - fs->group_desc[group].bg_free_blocks_count++; + ext2fs_unmark_block_bitmap2(fs->block_map, block); + group = ext2fs_group_of_blk2(fs, block); + ext2fs_bg_free_blocks_count_set(fs, group, ext2fs_bg_free_blocks_count(fs, group) + 1); ext2fs_group_desc_csum_set(fs, group); - fs->super->s_free_blocks_count++; + ext2fs_free_blocks_count_add(fs->super, EXT2FS_CLUSTER_RATIO(fs)); return 0; } @@ -289,9 +292,9 @@ static void remove_journal_inode(ext2_filsys fs) _("while reading bitmaps")); exit(1); } - retval = ext2fs_block_iterate(fs, ino, - BLOCK_FLAG_READ_ONLY, NULL, - release_blocks_proc, NULL); + retval = ext2fs_block_iterate3(fs, ino, + BLOCK_FLAG_READ_ONLY, NULL, + release_blocks_proc, NULL); if (retval) { com_err(program_name, retval, _("while clearing journal inode")); @@ -346,7 +349,6 @@ static void update_feature_set(ext2_filsys fs, char *features) { struct ext2_super_block *sb = fs->super; struct ext2_group_desc *gd; - errcode_t retval; __u32 old_features[3]; int i, type_err; unsigned int mask_err; @@ -447,8 +449,8 @@ static void update_feature_set(ext2_filsys fs, char *features) if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { - gd = fs->group_desc; - for (i = 0; i < fs->group_desc_count; i++, gd++) { + for (i = 0; i < fs->group_desc_count; i++) { + gd = ext2fs_group_desc(fs, fs->group_desc, i); gd->bg_itable_unused = 0; gd->bg_flags = EXT2_BG_INODE_ZEROED; ext2fs_group_desc_csum_set(fs, i); @@ -458,8 +460,8 @@ static void update_feature_set(ext2_filsys fs, char *features) if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { - gd = fs->group_desc; - for (i = 0; i < fs->group_desc_count; i++, gd++) { + for (i = 0; i < fs->group_desc_count; i++) { + gd = ext2fs_group_desc(fs, fs->group_desc, i); if ((gd->bg_flags & EXT2_BG_INODE_ZEROED) == 0) { /* * XXX what we really should do is zap @@ -1022,22 +1024,22 @@ static int get_move_bitmaps(ext2_filsys fs, int new_ino_blks_per_grp, dgrp_t i; int retval; ext2_badblocks_list bb_list = 0; - blk_t j, needed_blocks = 0; - blk_t start_blk, end_blk; + blk64_t j, needed_blocks = 0; + blk64_t start_blk, end_blk; retval = ext2fs_read_bb_inode(fs, &bb_list); if (retval) return retval; for (i = 0; i < fs->group_desc_count; i++) { - start_blk = fs->group_desc[i].bg_inode_table + + start_blk = ext2fs_inode_table_loc(fs, i) + fs->inode_blocks_per_group; - end_blk = fs->group_desc[i].bg_inode_table + + end_blk = ext2fs_inode_table_loc(fs, i) + new_ino_blks_per_grp; for (j = start_blk; j < end_blk; j++) { - if (ext2fs_test_block_bitmap(fs->block_map, j)) { + if (ext2fs_test_block_bitmap2(fs->block_map, j)) { /* * IF the block is a bad block we fail */ @@ -1046,20 +1048,20 @@ static int get_move_bitmaps(ext2_filsys fs, int new_ino_blks_per_grp, return ENOSPC; } - ext2fs_mark_block_bitmap(bmap, j); + ext2fs_mark_block_bitmap2(bmap, j); } else { /* * We are going to use this block for * inode table. So mark them used. */ - ext2fs_mark_block_bitmap(fs->block_map, j); + ext2fs_mark_block_bitmap2(fs->block_map, j); } } needed_blocks += end_blk - start_blk; } ext2fs_badblocks_list_free(bb_list); - if (needed_blocks > fs->super->s_free_blocks_count) + if (needed_blocks > ext2fs_free_blocks_count(fs->super)) return ENOSPC; return 0; @@ -1069,9 +1071,9 @@ static int ext2fs_is_meta_block(ext2_filsys fs, blk_t blk) { dgrp_t group; group = ext2fs_group_of_blk(fs, blk); - if (fs->group_desc[group].bg_block_bitmap == blk) + if (ext2fs_block_bitmap_loc(fs, group) == blk) return 1; - if (fs->group_desc[group].bg_inode_bitmap == blk) + if (ext2fs_inode_bitmap_loc(fs, group) == blk) return 1; return 0; } @@ -1095,10 +1097,10 @@ static int move_block(ext2_filsys fs, ext2fs_block_bitmap bmap) { char *buf; - dgrp_t group; + dgrp_t group = 0; errcode_t retval; int meta_data = 0; - blk_t blk, new_blk, goal; + blk64_t blk, new_blk, goal; struct blk_move *bmv; retval = ext2fs_get_mem(fs->blocksize, &buf); @@ -1106,8 +1108,8 @@ static int move_block(ext2_filsys fs, ext2fs_block_bitmap bmap) return retval; for (new_blk = blk = fs->super->s_first_data_block; - blk < fs->super->s_blocks_count; blk++) { - if (!ext2fs_test_block_bitmap(bmap, blk)) + blk < ext2fs_blocks_count(fs->super); blk++) { + if (!ext2fs_test_block_bitmap2(bmap, blk)) continue; if (ext2fs_is_meta_block(fs, blk)) { @@ -1119,13 +1121,13 @@ static int move_block(ext2_filsys fs, ext2fs_block_bitmap bmap) * fail */ group = ext2fs_group_of_blk(fs, blk); - goal = ext2fs_group_first_block(fs, group); + goal = ext2fs_group_first_block2(fs, group); meta_data = 1; } else { goal = new_blk; } - retval = ext2fs_new_block(fs, goal, NULL, &new_blk); + retval = ext2fs_new_block2(fs, goal, NULL, &new_blk); if (retval) goto err_out; @@ -1136,7 +1138,7 @@ static int move_block(ext2_filsys fs, ext2fs_block_bitmap bmap) } /* Mark this block as allocated */ - ext2fs_mark_block_bitmap(fs->block_map, new_blk); + ext2fs_mark_block_bitmap2(fs->block_map, new_blk); /* Add it to block move list */ retval = ext2fs_get_mem(sizeof(struct blk_move), &bmv); @@ -1148,11 +1150,11 @@ static int move_block(ext2_filsys fs, ext2fs_block_bitmap bmap) list_add(&(bmv->list), &blk_move_list); - retval = io_channel_read_blk(fs->io, blk, 1, buf); + retval = io_channel_read_blk64(fs->io, blk, 1, buf); if (retval) goto err_out; - retval = io_channel_write_blk(fs->io, new_blk, 1, buf); + retval = io_channel_write_blk64(fs->io, new_blk, 1, buf); if (retval) goto err_out; } @@ -1162,7 +1164,7 @@ err_out: return retval; } -static blk_t translate_block(blk_t blk) +static blk64_t translate_block(blk64_t blk) { struct list_head *entry; struct blk_move *bmv; @@ -1177,17 +1179,17 @@ static blk_t translate_block(blk_t blk) } static int process_block(ext2_filsys fs EXT2FS_ATTR((unused)), - blk_t *block_nr, + blk64_t *block_nr, e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), - blk_t ref_block EXT2FS_ATTR((unused)), + blk64_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { int ret = 0; - blk_t new_blk; + blk64_t new_blk; ext2fs_block_bitmap bmap = (ext2fs_block_bitmap) priv_data; - if (!ext2fs_test_block_bitmap(bmap, *block_nr)) + if (!ext2fs_test_block_bitmap2(bmap, *block_nr)) return 0; new_blk = translate_block(*block_nr); if (new_blk) { @@ -1205,7 +1207,7 @@ static int inode_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap) { errcode_t retval = 0; ext2_ino_t ino; - blk_t blk; + blk64_t blk; char *block_buf = 0; struct ext2_inode inode; ext2_inode_scan scan = NULL; @@ -1236,13 +1238,14 @@ static int inode_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap) * Do we need to fix this ?? */ - if (inode.i_file_acl && - ext2fs_test_block_bitmap(bmap, inode.i_file_acl)) { - blk = translate_block(inode.i_file_acl); + if (ext2fs_file_acl_block(&inode) && + ext2fs_test_block_bitmap2(bmap, + ext2fs_file_acl_block(&inode))) { + blk = translate_block(ext2fs_file_acl_block(&inode)); if (!blk) continue; - inode.i_file_acl = blk; + ext2fs_file_acl_block_set(&inode, blk); /* * Write the inode to disk so that inode table @@ -1256,7 +1259,7 @@ static int inode_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap) if (!ext2fs_inode_has_valid_blocks(&inode)) continue; - retval = ext2fs_block_iterate2(fs, ino, 0, block_buf, + retval = ext2fs_block_iterate3(fs, ino, 0, block_buf, process_block, bmap); if (retval) goto err_out; @@ -1280,20 +1283,20 @@ static int group_desc_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap) blk_t blk, new_blk; for (i = 0; i < fs->group_desc_count; i++) { - blk = fs->group_desc[i].bg_block_bitmap; - if (ext2fs_test_block_bitmap(bmap, blk)) { + blk = ext2fs_block_bitmap_loc(fs, i); + if (ext2fs_test_block_bitmap2(bmap, blk)) { new_blk = translate_block(blk); if (!new_blk) continue; - fs->group_desc[i].bg_block_bitmap = new_blk; + ext2fs_block_bitmap_loc_set(fs, i, new_blk); } - blk = fs->group_desc[i].bg_inode_bitmap; - if (ext2fs_test_block_bitmap(bmap, blk)) { + blk = ext2fs_inode_bitmap_loc(fs, i); + if (ext2fs_test_block_bitmap2(bmap, blk)) { new_blk = translate_block(blk); if (!new_blk) continue; - fs->group_desc[i].bg_inode_bitmap = new_blk; + ext2fs_inode_bitmap_loc_set(fs, i, new_blk); } } return 0; @@ -1302,7 +1305,7 @@ static int group_desc_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap) static int expand_inode_table(ext2_filsys fs, unsigned long new_ino_size) { dgrp_t i; - blk_t blk; + blk64_t blk; errcode_t retval; int new_ino_blks_per_grp; unsigned int j; @@ -1333,8 +1336,8 @@ static int expand_inode_table(ext2_filsys fs, unsigned long new_ino_size) tmp_new_itable = new_itable; for (i = 0; i < fs->group_desc_count; i++) { - blk = fs->group_desc[i].bg_inode_table; - retval = io_channel_read_blk(fs->io, blk, + blk = ext2fs_inode_table_loc(fs, i); + retval = io_channel_read_blk64(fs->io, blk, fs->inode_blocks_per_group, old_itable); if (retval) goto err_out; @@ -1353,7 +1356,7 @@ static int expand_inode_table(ext2_filsys fs, unsigned long new_ino_size) old_itable = tmp_old_itable; new_itable = tmp_new_itable; - retval = io_channel_write_blk(fs->io, blk, + retval = io_channel_write_blk64(fs->io, blk, new_ino_blks_per_grp, new_itable); if (retval) goto err_out; @@ -1375,7 +1378,7 @@ err_out: static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs) { - blk_t blk; + blk64_t blk; ext2_ino_t ino; unsigned int group = 0; unsigned int count = 0; @@ -1386,21 +1389,22 @@ static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs) * First calculate the block statistics */ for (blk = fs->super->s_first_data_block; - blk < fs->super->s_blocks_count; blk++) { - if (!ext2fs_fast_test_block_bitmap(fs->block_map, blk)) { + blk < ext2fs_blocks_count(fs->super); blk++) { + if (!ext2fs_fast_test_block_bitmap2(fs->block_map, blk)) { group_free++; total_free++; } count++; if ((count == fs->super->s_blocks_per_group) || - (blk == fs->super->s_blocks_count-1)) { - fs->group_desc[group++].bg_free_blocks_count = - group_free; + (blk == ext2fs_blocks_count(fs->super)-1)) { + ext2fs_bg_free_blocks_count_set(fs, group++, + group_free); count = 0; group_free = 0; } } - fs->super->s_free_blocks_count = total_free; + total_free = EXT2FS_C2B(fs, total_free); + ext2fs_free_blocks_count_set(fs->super, total_free); /* * Next, calculate the inode statistics @@ -1412,15 +1416,15 @@ static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs) /* Protect loop from wrap-around if s_inodes_count maxed */ for (ino = 1; ino <= fs->super->s_inodes_count && ino > 0; ino++) { - if (!ext2fs_fast_test_inode_bitmap(fs->inode_map, ino)) { + if (!ext2fs_fast_test_inode_bitmap2(fs->inode_map, ino)) { group_free++; total_free++; } count++; if ((count == fs->super->s_inodes_per_group) || (ino == fs->super->s_inodes_count)) { - fs->group_desc[group++].bg_free_inodes_count = - group_free; + ext2fs_bg_free_inodes_count_set(fs, group++, + group_free); count = 0; group_free = 0; } @@ -1714,23 +1718,22 @@ retry_open: interval); } if (m_flag) { - sb->s_r_blocks_count = (unsigned int) (reserved_ratio * - sb->s_blocks_count / 100.0); + ext2fs_r_blocks_count_set(sb, reserved_ratio * + ext2fs_blocks_count(sb) / 100.0); ext2fs_mark_super_dirty(fs); - printf(_("Setting reserved blocks percentage to %g%% " - "(%u blocks)\n"), - reserved_ratio, sb->s_r_blocks_count); + printf (_("Setting reserved blocks percentage to %g%% (%llu blocks)\n"), + reserved_ratio, ext2fs_r_blocks_count(sb)); } if (r_flag) { - if (reserved_blocks > sb->s_blocks_count/2) { + if (reserved_blocks > ext2fs_blocks_count(sb)/2) { com_err(program_name, 0, - _("reserved blocks count is too big (%lu)"), + _("reserved blocks count is too big (%llu)"), reserved_blocks); exit(1); } - sb->s_r_blocks_count = reserved_blocks; + ext2fs_r_blocks_count_set(sb, reserved_blocks); ext2fs_mark_super_dirty(fs); - printf(_("Setting reserved blocks count to %lu\n"), + printf(_("Setting reserved blocks count to %llu\n"), reserved_blocks); } if (s_flag == 1) { diff --git a/misc/util.c b/misc/util.c index 28979372..51bdb60f 100644 --- a/misc/util.c +++ b/misc/util.c @@ -257,7 +257,7 @@ unsigned int figure_journal_size(int size, ext2_filsys fs) { int j_blocks; - j_blocks = ext2fs_default_journal_size(fs->super->s_blocks_count); + j_blocks = ext2fs_default_journal_size(ext2fs_blocks_count(fs->super)); if (j_blocks < 0) { fputs(_("\nFilesystem too small for a journal\n"), stderr); return 0; @@ -273,7 +273,7 @@ unsigned int figure_journal_size(int size, ext2_filsys fs) j_blocks); exit(1); } - if ((unsigned) j_blocks > fs->super->s_free_blocks_count / 2) { + if ((unsigned) j_blocks > ext2fs_free_blocks_count(fs->super) / 2) { fputs(_("\nJournal size too big for filesystem.\n"), stderr); exit(1); |