diff --git a/.vs/ext2fs.vcxproj b/.vs/ext2fs.vcxproj index 8a51025c..24402fba 100644 --- a/.vs/ext2fs.vcxproj +++ b/.vs/ext2fs.vcxproj @@ -56,6 +56,7 @@ + @@ -71,8 +72,8 @@ - + @@ -103,6 +104,9 @@ + + + diff --git a/.vs/ext2fs.vcxproj.filters b/.vs/ext2fs.vcxproj.filters index 72367e3b..964dce94 100644 --- a/.vs/ext2fs.vcxproj.filters +++ b/.vs/ext2fs.vcxproj.filters @@ -27,9 +27,6 @@ Source Files - - Source Files - Source Files @@ -153,6 +150,12 @@ Source Files + + Source Files + + + Source Files + @@ -212,5 +215,14 @@ Header Files + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/src/drive.c b/src/drive.c index 103750ae..b2a91fb6 100644 --- a/src/drive.c +++ b/src/drive.c @@ -229,19 +229,20 @@ out: } /* - * Return an NT path to access the physical drive, or NULL on error. + * Return the path to access a partition on a specific disk, or NULL on error. * The string is allocated and must be freed (to ensure concurrent access) */ -char* GetNtPhysicalName(DWORD DriveIndex) +char* GetPartitionName(DWORD DriveIndex, DWORD PartitionNumber) { BOOL success = FALSE; - char physical_name[32]; + char partition_name[32]; CheckDriveIndex(DriveIndex); - static_sprintf(physical_name, "\\??\\PHYSICALDRIVE%lu", DriveIndex); + + static_sprintf(partition_name, "\\Device\\Harddisk%lu\\Partition%lu", DriveIndex, PartitionNumber); success = TRUE; out: - return (success) ? safe_strdup(physical_name) : NULL; + return (success) ? safe_strdup(partition_name) : NULL; } /* diff --git a/src/drive.h b/src/drive.h index 335aa91a..47a81f43 100644 --- a/src/drive.h +++ b/src/drive.h @@ -258,7 +258,7 @@ extern RUFUS_DRIVE_INFO SelectedDrive; BOOL SetAutoMount(BOOL enable); BOOL GetAutoMount(BOOL* enabled); char* GetPhysicalName(DWORD DriveIndex); -char* GetNtPhysicalName(DWORD DriveIndex); +char* GetPartitionName(DWORD DriveIndex, DWORD PartitionNumber); BOOL DeletePartitions(DWORD DriveIndex); HANDLE GetPhysicalHandle(DWORD DriveIndex, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWriteShare); char* GetLogicalName(DWORD DriveIndex, BOOL bKeepTrailingBackslash, BOOL bSilent); diff --git a/src/ext2fs/Makefile.am b/src/ext2fs/Makefile.am index a46f1a3e..598ed750 100644 --- a/src/ext2fs/Makefile.am +++ b/src/ext2fs/Makefile.am @@ -2,9 +2,9 @@ noinst_LIBRARIES = libext2fs.a libext2fs_a_SOURCES = alloc.c alloc_sb.c alloc_stats.c alloc_tables.c badblocks.c bb_inode.c \ bitmaps.c bitops.c blkmap64_ba.c blkmap64_rb.c blknum.c block.c bmap.c closefs.c crc16.c \ - crc32c.c csum.c dirblock.c dir_iterate.c extent.c ext_attr.c extent.c fileio.c freefs.c \ - gen_bitmap.c gen_bitmap64.c get_num_dirs.c hashmap.c i_block.c ind_block.c initialize.c inline.c \ - inline_data.c inode.c io_manager.c link.c lookup.c missing.c mkdir.c mmp.c newdir.c nt_io.c \ - punch.c rbtree.c read_bb.c rw_bitmaps.c sha512.c symlink.c valid_blk.c + crc32c.c csum.c dirblock.c dir_iterate.c extent.c ext_attr.c extent.c fallocate.c fileio.c \ + freefs.c gen_bitmap.c gen_bitmap64.c get_num_dirs.c hashmap.c i_block.c ind_block.c initialize.c \ + inline.c inline_data.c inode.c io_manager.c link.c lookup.c mkdir.c mkjournal.c mmp.c newdir.c \ + nt_io.c punch.c rbtree.c read_bb.c rw_bitmaps.c sha512.c symlink.c valid_blk.c libext2fs_a_CFLAGS = $(AM_CFLAGS) -DEXT2_FLAT_INCLUDES=0 -DHAVE_CONFIG_H -I. -I.. -Wno-undef -Wno-strict-aliasing -Wno-shadow diff --git a/src/ext2fs/Makefile.in b/src/ext2fs/Makefile.in index bb507c5b..61f224c2 100644 --- a/src/ext2fs/Makefile.in +++ b/src/ext2fs/Makefile.in @@ -107,8 +107,8 @@ am_libext2fs_a_OBJECTS = libext2fs_a-alloc.$(OBJEXT) \ libext2fs_a-dirblock.$(OBJEXT) \ libext2fs_a-dir_iterate.$(OBJEXT) libext2fs_a-extent.$(OBJEXT) \ libext2fs_a-ext_attr.$(OBJEXT) libext2fs_a-extent.$(OBJEXT) \ - libext2fs_a-fileio.$(OBJEXT) libext2fs_a-freefs.$(OBJEXT) \ - libext2fs_a-gen_bitmap.$(OBJEXT) \ + libext2fs_a-fallocate.$(OBJEXT) libext2fs_a-fileio.$(OBJEXT) \ + libext2fs_a-freefs.$(OBJEXT) libext2fs_a-gen_bitmap.$(OBJEXT) \ libext2fs_a-gen_bitmap64.$(OBJEXT) \ libext2fs_a-get_num_dirs.$(OBJEXT) \ libext2fs_a-hashmap.$(OBJEXT) libext2fs_a-i_block.$(OBJEXT) \ @@ -116,8 +116,8 @@ am_libext2fs_a_OBJECTS = libext2fs_a-alloc.$(OBJEXT) \ libext2fs_a-initialize.$(OBJEXT) libext2fs_a-inline.$(OBJEXT) \ libext2fs_a-inline_data.$(OBJEXT) libext2fs_a-inode.$(OBJEXT) \ libext2fs_a-io_manager.$(OBJEXT) libext2fs_a-link.$(OBJEXT) \ - libext2fs_a-lookup.$(OBJEXT) libext2fs_a-missing.$(OBJEXT) \ - libext2fs_a-mkdir.$(OBJEXT) libext2fs_a-mmp.$(OBJEXT) \ + libext2fs_a-lookup.$(OBJEXT) libext2fs_a-mkdir.$(OBJEXT) \ + libext2fs_a-mkjournal.$(OBJEXT) libext2fs_a-mmp.$(OBJEXT) \ libext2fs_a-newdir.$(OBJEXT) libext2fs_a-nt_io.$(OBJEXT) \ libext2fs_a-punch.$(OBJEXT) libext2fs_a-rbtree.$(OBJEXT) \ libext2fs_a-read_bb.$(OBJEXT) libext2fs_a-rw_bitmaps.$(OBJEXT) \ @@ -272,10 +272,10 @@ top_srcdir = @top_srcdir@ noinst_LIBRARIES = libext2fs.a libext2fs_a_SOURCES = alloc.c alloc_sb.c alloc_stats.c alloc_tables.c badblocks.c bb_inode.c \ bitmaps.c bitops.c blkmap64_ba.c blkmap64_rb.c blknum.c block.c bmap.c closefs.c crc16.c \ - crc32c.c csum.c dirblock.c dir_iterate.c extent.c ext_attr.c extent.c fileio.c freefs.c \ - gen_bitmap.c gen_bitmap64.c get_num_dirs.c hashmap.c i_block.c ind_block.c initialize.c inline.c \ - inline_data.c inode.c io_manager.c link.c lookup.c missing.c mkdir.c mmp.c newdir.c nt_io.c \ - punch.c rbtree.c read_bb.c rw_bitmaps.c sha512.c symlink.c valid_blk.c + crc32c.c csum.c dirblock.c dir_iterate.c extent.c ext_attr.c extent.c fallocate.c fileio.c \ + freefs.c gen_bitmap.c gen_bitmap64.c get_num_dirs.c hashmap.c i_block.c ind_block.c initialize.c \ + inline.c inline_data.c inode.c io_manager.c link.c lookup.c mkdir.c mkjournal.c mmp.c newdir.c \ + nt_io.c punch.c rbtree.c read_bb.c rw_bitmaps.c sha512.c symlink.c valid_blk.c libext2fs_a_CFLAGS = $(AM_CFLAGS) -DEXT2_FLAT_INCLUDES=0 -DHAVE_CONFIG_H -I. -I.. -Wno-undef -Wno-strict-aliasing -Wno-shadow all: all-am @@ -459,6 +459,12 @@ libext2fs_a-ext_attr.o: ext_attr.c libext2fs_a-ext_attr.obj: ext_attr.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libext2fs_a_CFLAGS) $(CFLAGS) -c -o libext2fs_a-ext_attr.obj `if test -f 'ext_attr.c'; then $(CYGPATH_W) 'ext_attr.c'; else $(CYGPATH_W) '$(srcdir)/ext_attr.c'; fi` +libext2fs_a-fallocate.o: fallocate.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libext2fs_a_CFLAGS) $(CFLAGS) -c -o libext2fs_a-fallocate.o `test -f 'fallocate.c' || echo '$(srcdir)/'`fallocate.c + +libext2fs_a-fallocate.obj: fallocate.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libext2fs_a_CFLAGS) $(CFLAGS) -c -o libext2fs_a-fallocate.obj `if test -f 'fallocate.c'; then $(CYGPATH_W) 'fallocate.c'; else $(CYGPATH_W) '$(srcdir)/fallocate.c'; fi` + libext2fs_a-fileio.o: fileio.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libext2fs_a_CFLAGS) $(CFLAGS) -c -o libext2fs_a-fileio.o `test -f 'fileio.c' || echo '$(srcdir)/'`fileio.c @@ -549,18 +555,18 @@ libext2fs_a-lookup.o: lookup.c libext2fs_a-lookup.obj: lookup.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libext2fs_a_CFLAGS) $(CFLAGS) -c -o libext2fs_a-lookup.obj `if test -f 'lookup.c'; then $(CYGPATH_W) 'lookup.c'; else $(CYGPATH_W) '$(srcdir)/lookup.c'; fi` -libext2fs_a-missing.o: missing.c - $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libext2fs_a_CFLAGS) $(CFLAGS) -c -o libext2fs_a-missing.o `test -f 'missing.c' || echo '$(srcdir)/'`missing.c - -libext2fs_a-missing.obj: missing.c - $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libext2fs_a_CFLAGS) $(CFLAGS) -c -o libext2fs_a-missing.obj `if test -f 'missing.c'; then $(CYGPATH_W) 'missing.c'; else $(CYGPATH_W) '$(srcdir)/missing.c'; fi` - libext2fs_a-mkdir.o: mkdir.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libext2fs_a_CFLAGS) $(CFLAGS) -c -o libext2fs_a-mkdir.o `test -f 'mkdir.c' || echo '$(srcdir)/'`mkdir.c libext2fs_a-mkdir.obj: mkdir.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libext2fs_a_CFLAGS) $(CFLAGS) -c -o libext2fs_a-mkdir.obj `if test -f 'mkdir.c'; then $(CYGPATH_W) 'mkdir.c'; else $(CYGPATH_W) '$(srcdir)/mkdir.c'; fi` +libext2fs_a-mkjournal.o: mkjournal.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libext2fs_a_CFLAGS) $(CFLAGS) -c -o libext2fs_a-mkjournal.o `test -f 'mkjournal.c' || echo '$(srcdir)/'`mkjournal.c + +libext2fs_a-mkjournal.obj: mkjournal.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libext2fs_a_CFLAGS) $(CFLAGS) -c -o libext2fs_a-mkjournal.obj `if test -f 'mkjournal.c'; then $(CYGPATH_W) 'mkjournal.c'; else $(CYGPATH_W) '$(srcdir)/mkjournal.c'; fi` + libext2fs_a-mmp.o: mmp.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libext2fs_a_CFLAGS) $(CFLAGS) -c -o libext2fs_a-mmp.o `test -f 'mmp.c' || echo '$(srcdir)/'`mmp.c diff --git a/src/ext2fs/config.h b/src/ext2fs/config.h index 25c484a7..5ddaf9f5 100644 --- a/src/ext2fs/config.h +++ b/src/ext2fs/config.h @@ -177,6 +177,9 @@ /* Define if you have the 'wint_t' type. */ #define HAVE_WINT_T 1 +/* Define if you have 'winsock.h'. */ +#define HAVE_WINSOCK_H 1 + /* Define to 1 if O_NOATIME works. */ #define HAVE_WORKING_O_NOATIME 0 diff --git a/src/ext2fs/ext2_fs.h b/src/ext2fs/ext2_fs.h index 6aa497f8..e9b2994e 100644 --- a/src/ext2fs/ext2_fs.h +++ b/src/ext2fs/ext2_fs.h @@ -785,9 +785,11 @@ struct ext2_super_block { */ #define EXT2_OS_LINUX 0 #define EXT2_OS_HURD 1 -#define EXT2_OS_WINDOWS 2 +#define EXT2_OBSO_OS_MASIX 2 #define EXT2_OS_FREEBSD 3 #define EXT2_OS_LITES 4 +#define EXT2_OS_WINDOWS 5 +#define EXT2_OS_MACOS 6 /* * Revision levels diff --git a/src/ext2fs/fallocate.c b/src/ext2fs/fallocate.c new file mode 100644 index 00000000..67389ce7 --- /dev/null +++ b/src/ext2fs/fallocate.c @@ -0,0 +1,857 @@ +/* + * fallocate.c -- Allocate large chunks of file. + * + * Copyright (C) 2014 Oracle. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include "config.h" + +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#undef DEBUG + +#ifdef DEBUG +# define dbg_printf(f, a...) do {printf(f, ## a); fflush(stdout); } while (0) +#else +# define dbg_printf(f, ...) +#endif + +/* + * Extent-based fallocate code. + * + * Find runs of unmapped logical blocks by starting at start and walking the + * extents until we reach the end of the range we want. + * + * For each run of unmapped blocks, try to find the extents on either side of + * the range. If there's a left extent that can grow by at least a cluster and + * there are lblocks between start and the next lcluster after start, see if + * there's an implied cluster allocation; if so, zero the blocks (if the left + * extent is initialized) and adjust the extent. Ditto for the blocks between + * the end of the last full lcluster and end, if there's a right extent. + * + * Try to attach as much as we can to the left extent, then try to attach as + * much as we can to the right extent. For the remainder, try to allocate the + * whole range; map in whatever we get; and repeat until we're done. + * + * To attach to a left extent, figure out the maximum amount we can add to the + * extent and try to allocate that much, and append if successful. To attach + * to a right extent, figure out the max we can add to the extent, try to + * allocate that much, and prepend if successful. + * + * We need an alloc_range function that tells us how much we can allocate given + * a maximum length and one of a suggested start, a fixed start, or a fixed end + * point. + * + * Every time we modify the extent tree we also need to update the block stats. + * + * At the end, update i_blocks and i_size appropriately. + */ + +static void dbg_print_extent(const char *desc EXT2FS_ATTR((unused)), + const struct ext2fs_extent *extent EXT2FS_ATTR((unused))) +{ +#ifdef DEBUG + if (desc) + printf("%s: ", desc); + printf("extent: lblk %llu--%llu, len %u, pblk %llu, flags: ", + extent->e_lblk, extent->e_lblk + extent->e_len - 1, + extent->e_len, extent->e_pblk); + if (extent->e_flags & EXT2_EXTENT_FLAGS_LEAF) + fputs("LEAF ", stdout); + if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT) + fputs("UNINIT ", stdout); + if (extent->e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) + fputs("2ND_VISIT ", stdout); + if (!extent->e_flags) + fputs("(none)", stdout); + fputc('\n', stdout); + fflush(stdout); +#endif +} + +static errcode_t claim_range(ext2_filsys fs, struct ext2_inode *inode, + blk64_t blk, blk64_t len) +{ + blk64_t clusters; + + clusters = (len + EXT2FS_CLUSTER_RATIO(fs) - 1) / + EXT2FS_CLUSTER_RATIO(fs); + ext2fs_block_alloc_stats_range(fs, blk, + clusters * EXT2FS_CLUSTER_RATIO(fs), +1); + return ext2fs_iblk_add_blocks(fs, inode, clusters); +} + +static errcode_t ext_falloc_helper(ext2_filsys fs, + int flags, + ext2_ino_t ino, + struct ext2_inode *inode, + ext2_extent_handle_t handle, + struct ext2fs_extent *left_ext, + struct ext2fs_extent *right_ext, + blk64_t range_start, blk64_t range_len, + blk64_t alloc_goal) +{ + struct ext2fs_extent newex, ex; + int op; + blk64_t fillable, pblk, plen, x, y; + blk64_t eof_blk = 0, cluster_fill = 0; + errcode_t err; + blk_t max_extent_len, max_uninit_len, max_init_len; + +#ifdef DEBUG + printf("%s: ", __func__); + if (left_ext) + printf("left_ext=%llu--%llu, ", left_ext->e_lblk, + left_ext->e_lblk + left_ext->e_len - 1); + if (right_ext) + printf("right_ext=%llu--%llu, ", right_ext->e_lblk, + right_ext->e_lblk + right_ext->e_len - 1); + printf("start=%llu len=%llu, goal=%llu\n", range_start, range_len, + alloc_goal); + fflush(stdout); +#endif + /* Can't create initialized extents past EOF? */ + if (!(flags & EXT2_FALLOCATE_INIT_BEYOND_EOF)) + eof_blk = EXT2_I_SIZE(inode) / fs->blocksize; + + /* The allocation goal must be as far into a cluster as range_start. */ + alloc_goal = (alloc_goal & ~EXT2FS_CLUSTER_MASK(fs)) | + (range_start & EXT2FS_CLUSTER_MASK(fs)); + + max_uninit_len = EXT_UNINIT_MAX_LEN & ~EXT2FS_CLUSTER_MASK(fs); + max_init_len = EXT_INIT_MAX_LEN & ~EXT2FS_CLUSTER_MASK(fs); + + /* We must lengthen the left extent to the end of the cluster */ + if (left_ext && EXT2FS_CLUSTER_RATIO(fs) > 1) { + /* How many more blocks can be attached to left_ext? */ + if (left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) + fillable = max_uninit_len - left_ext->e_len; + else + fillable = max_init_len - left_ext->e_len; + + if (fillable > range_len) + fillable = range_len; + if (fillable == 0) + goto expand_right; + + /* + * If range_start isn't on a cluster boundary, try an + * implied cluster allocation for left_ext. + */ + cluster_fill = EXT2FS_CLUSTER_RATIO(fs) - + (range_start & EXT2FS_CLUSTER_MASK(fs)); + cluster_fill &= EXT2FS_CLUSTER_MASK(fs); + if (cluster_fill == 0) + goto expand_right; + + if (cluster_fill > fillable) + cluster_fill = fillable; + + /* Don't expand an initialized left_ext beyond EOF */ + if (!(flags & EXT2_FALLOCATE_INIT_BEYOND_EOF)) { + x = left_ext->e_lblk + left_ext->e_len - 1; + dbg_printf("%s: lend=%llu newlend=%llu eofblk=%llu\n", + __func__, x, x + cluster_fill, eof_blk); + if (eof_blk >= x && eof_blk <= x + cluster_fill) + cluster_fill = eof_blk - x; + if (cluster_fill == 0) + goto expand_right; + } + + err = ext2fs_extent_goto(handle, left_ext->e_lblk); + if (err) + goto expand_right; + left_ext->e_len += cluster_fill; + range_start += cluster_fill; + range_len -= cluster_fill; + alloc_goal += cluster_fill; + + dbg_print_extent("ext_falloc clus left+", left_ext); + err = ext2fs_extent_replace(handle, 0, left_ext); + if (err) + goto out; + err = ext2fs_extent_fix_parents(handle); + if (err) + goto out; + + /* Zero blocks */ + if (!(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)) { + err = ext2fs_zero_blocks2(fs, left_ext->e_pblk + + left_ext->e_len - + cluster_fill, cluster_fill, + NULL, NULL); + if (err) + goto out; + } + } + +expand_right: + /* We must lengthen the right extent to the beginning of the cluster */ + if (right_ext && EXT2FS_CLUSTER_RATIO(fs) > 1) { + /* How much can we attach to right_ext? */ + if (right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) + fillable = max_uninit_len - right_ext->e_len; + else + fillable = max_init_len - right_ext->e_len; + + if (fillable > range_len) + fillable = range_len; + if (fillable == 0) + goto try_merge; + + /* + * If range_end isn't on a cluster boundary, try an implied + * cluster allocation for right_ext. + */ + cluster_fill = right_ext->e_lblk & EXT2FS_CLUSTER_MASK(fs); + if (cluster_fill == 0) + goto try_merge; + + err = ext2fs_extent_goto(handle, right_ext->e_lblk); + if (err) + goto out; + + if (cluster_fill > fillable) + cluster_fill = fillable; + right_ext->e_lblk -= cluster_fill; + right_ext->e_pblk -= cluster_fill; + right_ext->e_len += cluster_fill; + range_len -= cluster_fill; + + dbg_print_extent("ext_falloc clus right+", right_ext); + err = ext2fs_extent_replace(handle, 0, right_ext); + if (err) + goto out; + err = ext2fs_extent_fix_parents(handle); + if (err) + goto out; + + /* Zero blocks if necessary */ + if (!(right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)) { + err = ext2fs_zero_blocks2(fs, right_ext->e_pblk, + cluster_fill, NULL, NULL); + if (err) + goto out; + } + } + +try_merge: + /* Merge both extents together, perhaps? */ + if (left_ext && right_ext) { + /* Are the two extents mergeable? */ + if ((left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) != + (right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)) + goto try_left; + + /* User requires init/uninit but extent is uninit/init. */ + if (((flags & EXT2_FALLOCATE_FORCE_INIT) && + (left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)) || + ((flags & EXT2_FALLOCATE_FORCE_UNINIT) && + !(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT))) + goto try_left; + + /* + * Skip initialized extent unless user wants to zero blocks + * or requires init extent. + */ + if (!(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) && + (!(flags & EXT2_FALLOCATE_ZERO_BLOCKS) || + !(flags & EXT2_FALLOCATE_FORCE_INIT))) + goto try_left; + + /* Will it even fit? */ + x = left_ext->e_len + range_len + right_ext->e_len; + if (x > (left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT ? + max_uninit_len : max_init_len)) + goto try_left; + + err = ext2fs_extent_goto(handle, left_ext->e_lblk); + if (err) + goto try_left; + + /* Allocate blocks */ + y = left_ext->e_pblk + left_ext->e_len; + err = ext2fs_new_range(fs, EXT2_NEWRANGE_FIXED_GOAL | + EXT2_NEWRANGE_MIN_LENGTH, y, + right_ext->e_pblk - y + 1, NULL, + &pblk, &plen); + if (err) + goto try_left; + if (pblk + plen != right_ext->e_pblk) + goto try_left; + err = claim_range(fs, inode, pblk, plen); + if (err) + goto out; + + /* Modify extents */ + left_ext->e_len = x; + dbg_print_extent("ext_falloc merge", left_ext); + err = ext2fs_extent_replace(handle, 0, left_ext); + if (err) + goto out; + err = ext2fs_extent_fix_parents(handle); + if (err) + goto out; + err = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF, &newex); + if (err) + goto out; + err = ext2fs_extent_delete(handle, 0); + if (err) + goto out; + err = ext2fs_extent_fix_parents(handle); + if (err) + goto out; + *right_ext = *left_ext; + + /* Zero blocks */ + if (!(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) && + (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) { + err = ext2fs_zero_blocks2(fs, range_start, range_len, + NULL, NULL); + if (err) + goto out; + } + + return 0; + } + +try_left: + /* Extend the left extent */ + if (left_ext) { + /* How many more blocks can be attached to left_ext? */ + if (left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) + fillable = max_uninit_len - left_ext->e_len; + else if (flags & EXT2_FALLOCATE_ZERO_BLOCKS) + fillable = max_init_len - left_ext->e_len; + else + fillable = 0; + + /* User requires init/uninit but extent is uninit/init. */ + if (((flags & EXT2_FALLOCATE_FORCE_INIT) && + (left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)) || + ((flags & EXT2_FALLOCATE_FORCE_UNINIT) && + !(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT))) + goto try_right; + + if (fillable > range_len) + fillable = range_len; + + /* Don't expand an initialized left_ext beyond EOF */ + x = left_ext->e_lblk + left_ext->e_len - 1; + if (!(flags & EXT2_FALLOCATE_INIT_BEYOND_EOF)) { + dbg_printf("%s: lend=%llu newlend=%llu eofblk=%llu\n", + __func__, x, x + fillable, eof_blk); + if (eof_blk >= x && eof_blk <= x + fillable) + fillable = eof_blk - x; + } + + if (fillable == 0) + goto try_right; + + /* Test if the right edge of the range is already mapped? */ + if (EXT2FS_CLUSTER_RATIO(fs) > 1) { + err = ext2fs_map_cluster_block(fs, ino, inode, + x + fillable, &pblk); + if (err) + goto out; + if (pblk) + fillable -= 1 + ((x + fillable) + & EXT2FS_CLUSTER_MASK(fs)); + if (fillable == 0) + goto try_right; + } + + /* Allocate range of blocks */ + x = left_ext->e_pblk + left_ext->e_len; + err = ext2fs_new_range(fs, EXT2_NEWRANGE_FIXED_GOAL | + EXT2_NEWRANGE_MIN_LENGTH, + x, fillable, NULL, &pblk, &plen); + if (err) + goto try_right; + err = claim_range(fs, inode, pblk, plen); + if (err) + goto out; + + /* Modify left_ext */ + err = ext2fs_extent_goto(handle, left_ext->e_lblk); + if (err) + goto out; + range_start += plen; + range_len -= plen; + left_ext->e_len += plen; + dbg_print_extent("ext_falloc left+", left_ext); + err = ext2fs_extent_replace(handle, 0, left_ext); + if (err) + goto out; + err = ext2fs_extent_fix_parents(handle); + if (err) + goto out; + + /* Zero blocks if necessary */ + if (!(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) && + (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) { + err = ext2fs_zero_blocks2(fs, pblk, plen, NULL, NULL); + if (err) + goto out; + } + } + +try_right: + /* Extend the right extent */ + if (right_ext) { + /* How much can we attach to right_ext? */ + if (right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) + fillable = max_uninit_len - right_ext->e_len; + else if (flags & EXT2_FALLOCATE_ZERO_BLOCKS) + fillable = max_init_len - right_ext->e_len; + else + fillable = 0; + + /* User requires init/uninit but extent is uninit/init. */ + if (((flags & EXT2_FALLOCATE_FORCE_INIT) && + (right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)) || + ((flags & EXT2_FALLOCATE_FORCE_UNINIT) && + !(right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT))) + goto try_anywhere; + + if (fillable > range_len) + fillable = range_len; + if (fillable == 0) + goto try_anywhere; + + /* Test if the left edge of the range is already mapped? */ + if (EXT2FS_CLUSTER_RATIO(fs) > 1) { + err = ext2fs_map_cluster_block(fs, ino, inode, + right_ext->e_lblk - fillable, &pblk); + if (err) + goto out; + if (pblk) + fillable -= EXT2FS_CLUSTER_RATIO(fs) - + ((right_ext->e_lblk - fillable) + & EXT2FS_CLUSTER_MASK(fs)); + if (fillable == 0) + goto try_anywhere; + } + + /* + * FIXME: It would be nice if we could handle allocating a + * variable range from a fixed end point instead of just + * skipping to the general allocator if the whole range is + * unavailable. + */ + err = ext2fs_new_range(fs, EXT2_NEWRANGE_FIXED_GOAL | + EXT2_NEWRANGE_MIN_LENGTH, + right_ext->e_pblk - fillable, + fillable, NULL, &pblk, &plen); + if (err) + goto try_anywhere; + err = claim_range(fs, inode, + pblk & ~EXT2FS_CLUSTER_MASK(fs), + plen + (pblk & EXT2FS_CLUSTER_MASK(fs))); + if (err) + goto out; + + /* Modify right_ext */ + err = ext2fs_extent_goto(handle, right_ext->e_lblk); + if (err) + goto out; + range_len -= plen; + right_ext->e_lblk -= plen; + right_ext->e_pblk -= plen; + right_ext->e_len += plen; + dbg_print_extent("ext_falloc right+", right_ext); + err = ext2fs_extent_replace(handle, 0, right_ext); + if (err) + goto out; + err = ext2fs_extent_fix_parents(handle); + if (err) + goto out; + + /* Zero blocks if necessary */ + if (!(right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) && + (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) { + err = ext2fs_zero_blocks2(fs, pblk, + plen + cluster_fill, NULL, NULL); + if (err) + goto out; + } + } + +try_anywhere: + /* Try implied cluster alloc on the left and right ends */ + if (range_len > 0 && (range_start & EXT2FS_CLUSTER_MASK(fs))) { + cluster_fill = EXT2FS_CLUSTER_RATIO(fs) - + (range_start & EXT2FS_CLUSTER_MASK(fs)); + cluster_fill &= EXT2FS_CLUSTER_MASK(fs); + if (cluster_fill > range_len) + cluster_fill = range_len; + newex.e_lblk = range_start; + err = ext2fs_map_cluster_block(fs, ino, inode, newex.e_lblk, + &pblk); + if (err) + goto out; + if (pblk == 0) + goto try_right_implied; + newex.e_pblk = pblk; + newex.e_len = cluster_fill; + newex.e_flags = (flags & EXT2_FALLOCATE_FORCE_INIT ? 0 : + EXT2_EXTENT_FLAGS_UNINIT); + dbg_print_extent("ext_falloc iclus left+", &newex); + ext2fs_extent_goto(handle, newex.e_lblk); + err = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, + &ex); + if (err == EXT2_ET_NO_CURRENT_NODE) + ex.e_lblk = 0; + else if (err) + goto out; + + if (ex.e_lblk > newex.e_lblk) + op = 0; /* insert before */ + else + op = EXT2_EXTENT_INSERT_AFTER; + dbg_printf("%s: inserting %s lblk %llu newex=%llu\n", + __func__, op ? "after" : "before", ex.e_lblk, + newex.e_lblk); + err = ext2fs_extent_insert(handle, op, &newex); + if (err) + goto out; + err = ext2fs_extent_fix_parents(handle); + if (err) + goto out; + + if (!(newex.e_flags & EXT2_EXTENT_FLAGS_UNINIT) && + (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) { + err = ext2fs_zero_blocks2(fs, newex.e_pblk, + newex.e_len, NULL, NULL); + if (err) + goto out; + } + + range_start += cluster_fill; + range_len -= cluster_fill; + } + +try_right_implied: + y = range_start + range_len; + if (range_len > 0 && (y & EXT2FS_CLUSTER_MASK(fs))) { + cluster_fill = y & EXT2FS_CLUSTER_MASK(fs); + if (cluster_fill > range_len) + cluster_fill = range_len; + newex.e_lblk = y & ~EXT2FS_CLUSTER_MASK(fs); + err = ext2fs_map_cluster_block(fs, ino, inode, newex.e_lblk, + &pblk); + if (err) + goto out; + if (pblk == 0) + goto no_implied; + newex.e_pblk = pblk; + newex.e_len = cluster_fill; + newex.e_flags = (flags & EXT2_FALLOCATE_FORCE_INIT ? 0 : + EXT2_EXTENT_FLAGS_UNINIT); + dbg_print_extent("ext_falloc iclus right+", &newex); + ext2fs_extent_goto(handle, newex.e_lblk); + err = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, + &ex); + if (err == EXT2_ET_NO_CURRENT_NODE) + ex.e_lblk = 0; + else if (err) + goto out; + + if (ex.e_lblk > newex.e_lblk) + op = 0; /* insert before */ + else + op = EXT2_EXTENT_INSERT_AFTER; + dbg_printf("%s: inserting %s lblk %llu newex=%llu\n", + __func__, op ? "after" : "before", ex.e_lblk, + newex.e_lblk); + err = ext2fs_extent_insert(handle, op, &newex); + if (err) + goto out; + err = ext2fs_extent_fix_parents(handle); + if (err) + goto out; + + if (!(newex.e_flags & EXT2_EXTENT_FLAGS_UNINIT) && + (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) { + err = ext2fs_zero_blocks2(fs, newex.e_pblk, + newex.e_len, NULL, NULL); + if (err) + goto out; + } + + range_len -= cluster_fill; + } + +no_implied: + if (range_len == 0) + return 0; + + newex.e_lblk = range_start; + if (flags & EXT2_FALLOCATE_FORCE_INIT) { + max_extent_len = max_init_len; + newex.e_flags = 0; + } else { + max_extent_len = max_uninit_len; + newex.e_flags = EXT2_EXTENT_FLAGS_UNINIT; + } + pblk = alloc_goal; + y = range_len; + for (x = 0; x < y;) { + cluster_fill = newex.e_lblk & EXT2FS_CLUSTER_MASK(fs); + fillable = min(range_len + cluster_fill, max_extent_len); + err = ext2fs_new_range(fs, 0, pblk & ~EXT2FS_CLUSTER_MASK(fs), + fillable, + NULL, &pblk, &plen); + if (err) + goto out; + err = claim_range(fs, inode, pblk, plen); + if (err) + goto out; + + /* Create extent */ + newex.e_pblk = pblk + cluster_fill; + newex.e_len = plen - cluster_fill; + dbg_print_extent("ext_falloc create", &newex); + ext2fs_extent_goto(handle, newex.e_lblk); + err = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, + &ex); + if (err == EXT2_ET_NO_CURRENT_NODE) + ex.e_lblk = 0; + else if (err) + goto out; + + if (ex.e_lblk > newex.e_lblk) + op = 0; /* insert before */ + else + op = EXT2_EXTENT_INSERT_AFTER; + dbg_printf("%s: inserting %s lblk %llu newex=%llu\n", + __func__, op ? "after" : "before", ex.e_lblk, + newex.e_lblk); + err = ext2fs_extent_insert(handle, op, &newex); + if (err) + goto out; + err = ext2fs_extent_fix_parents(handle); + if (err) + goto out; + + if (!(newex.e_flags & EXT2_EXTENT_FLAGS_UNINIT) && + (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) { + err = ext2fs_zero_blocks2(fs, pblk, plen, NULL, NULL); + if (err) + goto out; + } + + /* Update variables at end of loop */ + x += plen - cluster_fill; + range_len -= plen - cluster_fill; + newex.e_lblk += plen - cluster_fill; + pblk += plen - cluster_fill; + if (pblk >= ext2fs_blocks_count(fs->super)) + pblk = fs->super->s_first_data_block; + } + +out: + return err; +} + +static errcode_t extent_fallocate(ext2_filsys fs, int flags, ext2_ino_t ino, + struct ext2_inode *inode, blk64_t goal, + blk64_t start, blk64_t len) +{ + ext2_extent_handle_t handle; + struct ext2fs_extent left_extent, right_extent; + struct ext2fs_extent *left_adjacent, *right_adjacent; + errcode_t err; + blk64_t range_start, range_end = 0, end, next; + blk64_t count, goal_distance; + + end = start + len - 1; + err = ext2fs_extent_open2(fs, ino, inode, &handle); + if (err) + return err; + + /* + * Find the extent closest to the start of the alloc range. We don't + * check the return value because _goto() sets the current node to the + * next-lowest extent if 'start' is in a hole; or the next-highest + * extent if there aren't any lower ones; or doesn't set a current node + * if there was a real error reading the extent tree. In that case, + * _get() will error out. + */ +start_again: + ext2fs_extent_goto(handle, start); + err = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &left_extent); + if (err == EXT2_ET_NO_CURRENT_NODE) { + blk64_t max_blocks = ext2fs_blocks_count(fs->super); + + if (goal == ~0ULL) + goal = ext2fs_find_inode_goal(fs, ino, inode, start); + err = ext2fs_find_first_zero_block_bitmap2(fs->block_map, + goal, max_blocks - 1, &goal); + goal += start; + err = ext_falloc_helper(fs, flags, ino, inode, handle, NULL, + NULL, start, len, goal); + goto errout; + } else if (err) + goto errout; + + dbg_print_extent("ext_falloc initial", &left_extent); + next = left_extent.e_lblk + left_extent.e_len; + if (left_extent.e_lblk > start) { + /* The nearest extent we found was beyond start??? */ + goal = left_extent.e_pblk - (left_extent.e_lblk - start); + err = ext_falloc_helper(fs, flags, ino, inode, handle, NULL, + &left_extent, start, + left_extent.e_lblk - start, goal); + if (err) + goto errout; + + goto start_again; + } else if (next >= start) { + range_start = next; + left_adjacent = &left_extent; + } else { + range_start = start; + left_adjacent = NULL; + } + goal = left_extent.e_pblk + (range_start - left_extent.e_lblk); + + do { + err = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF, + &right_extent); + dbg_printf("%s: ino=%d get next =%d\n", __func__, ino, + (int)err); + dbg_print_extent("ext_falloc next", &right_extent); + /* Stop if we've seen this extent before */ + if (!err && right_extent.e_lblk <= left_extent.e_lblk) + err = EXT2_ET_EXTENT_NO_NEXT; + + if (err && err != EXT2_ET_EXTENT_NO_NEXT) + goto errout; + if (err == EXT2_ET_EXTENT_NO_NEXT || + right_extent.e_lblk > end + 1) { + range_end = end; + right_adjacent = NULL; + } else { + /* Handle right_extent.e_lblk <= end */ + range_end = right_extent.e_lblk - 1; + right_adjacent = &right_extent; + } + goal_distance = range_start - next; + if (err != EXT2_ET_EXTENT_NO_NEXT && + goal_distance > (range_end - right_extent.e_lblk)) + goal = right_extent.e_pblk - + (right_extent.e_lblk - range_start); + + dbg_printf("%s: ino=%d rstart=%llu rend=%llu\n", __func__, ino, + range_start, range_end); + err = 0; + if (range_start <= range_end) { + count = range_end - range_start + 1; + err = ext_falloc_helper(fs, flags, ino, inode, handle, + left_adjacent, right_adjacent, + range_start, count, goal); + if (err) + goto errout; + } + + if (range_end == end) + break; + + err = ext2fs_extent_goto(handle, right_extent.e_lblk); + if (err) + goto errout; + next = right_extent.e_lblk + right_extent.e_len; + left_extent = right_extent; + left_adjacent = &left_extent; + range_start = next; + goal = left_extent.e_pblk + (range_start - left_extent.e_lblk); + } while (range_end < end); + +errout: + ext2fs_extent_free(handle); + return err; +} + +/* + * Map physical blocks to a range of logical blocks within a file. The range + * of logical blocks are (start, start + len). If there are already extents, + * the mappings will try to extend the mappings; otherwise, it will try to map + * start as if logical block 0 points to goal. If goal is ~0ULL, then the goal + * is calculated based on the inode group. + * + * Flags: + * - EXT2_FALLOCATE_ZERO_BLOCKS: Zero the blocks that are allocated. + * - EXT2_FALLOCATE_FORCE_INIT: Create only initialized extents. + * - EXT2_FALLOCATE_FORCE_UNINIT: Create only uninitialized extents. + * - EXT2_FALLOCATE_INIT_BEYOND_EOF: Create extents beyond EOF. + * + * If neither FORCE_INIT nor FORCE_UNINIT are specified, this function will + * try to expand any extents it finds, zeroing blocks as necessary. + */ +errcode_t ext2fs_fallocate(ext2_filsys fs, int flags, ext2_ino_t ino, + struct ext2_inode *inode, blk64_t goal, + blk64_t start, blk64_t len) +{ + struct ext2_inode inode_buf; + blk64_t blk, x; + errcode_t err; + + if (((flags & EXT2_FALLOCATE_FORCE_INIT) && + (flags & EXT2_FALLOCATE_FORCE_UNINIT)) || + (flags & ~EXT2_FALLOCATE_ALL_FLAGS)) + return EXT2_ET_INVALID_ARGUMENT; + + if (len > ext2fs_blocks_count(fs->super)) + return EXT2_ET_BLOCK_ALLOC_FAIL; + else if (len == 0) + return 0; + + /* Read inode structure if necessary */ + if (!inode) { + err = ext2fs_read_inode(fs, ino, &inode_buf); + if (err) + return err; + inode = &inode_buf; + } + dbg_printf("%s: ino=%d start=%llu len=%llu goal=%llu\n", __func__, ino, + start, len, goal); + + if (inode->i_flags & EXT4_EXTENTS_FL) { + err = extent_fallocate(fs, flags, ino, inode, goal, start, len); + goto out; + } + + /* XXX: Allocate a bunch of blocks the slow way */ + for (blk = start; blk < start + len; blk++) { + err = ext2fs_bmap2(fs, ino, inode, NULL, 0, blk, 0, &x); + if (err) + return err; + if (x) + continue; + + err = ext2fs_bmap2(fs, ino, inode, NULL, + BMAP_ALLOC | BMAP_UNINIT | BMAP_ZERO, blk, + 0, &x); + if (err) + return err; + } + +out: + if (inode == &inode_buf) + ext2fs_write_inode(fs, ino, inode); + return err; +} diff --git a/src/ext2fs/jfs_compat.h b/src/ext2fs/jfs_compat.h new file mode 100644 index 00000000..8df9acb3 --- /dev/null +++ b/src/ext2fs/jfs_compat.h @@ -0,0 +1,104 @@ + +#ifndef _JFS_COMPAT_H +#define _JFS_COMPAT_H + +#include "kernel-list.h" +#include +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_WINSOCK_H +// Heck if we're gonna use WinSock just for htonl and friends +#include +#define htonl _byteswap_ulong +#define ntohl _byteswap_ulong +#define htons _byteswap_ushort +#define ntohs _byteswap_ushort +#else +#include +#endif + +#define printk printf +#define KERN_ERR "" +#define KERN_DEBUG "" + +#define READ 0 +#define WRITE 1 + +#define cpu_to_be32(n) htonl(n) +#define be32_to_cpu(n) ntohl(n) +#define cpu_to_be16(n) htons(n) +#define be16_to_cpu(n) ntohs(n) + +typedef unsigned int tid_t; +typedef struct journal_s journal_t; +typedef struct kdev_s *kdev_t; + +struct buffer_head; +struct inode; + +#define GFP_KERNEL 0 +#define JFS_TAG_SIZE32 JBD_TAG_SIZE32 +#define JFS_BARRIER 0 +typedef __u64 u64; +#define JFS_CRC32_CHKSUM JBD2_CRC32_CHKSUM +#define JFS_CRC32_CHKSUM_SIZE JBD2_CRC32_CHKSUM_SIZE +#define put_bh(x) brelse(x) +#define be64_to_cpu(x) ext2fs_be64_to_cpu(x) + +static inline __u32 jbd2_chksum(journal_t *j EXT2FS_ATTR((unused)), + __u32 crc, const void *address, + unsigned int length) +{ + return ext2fs_crc32c_le(crc, address, length); +} +#define crc32_be(x, y, z) ext2fs_crc32_be((x), (y), (z)) +#define spin_lock_init(x) +#define spin_lock(x) +#define spin_unlock(x) +#define yield() +#define SLAB_HWCACHE_ALIGN 0 +#define SLAB_TEMPORARY 0 +#define KMEM_CACHE(__struct, __flags) kmem_cache_create(#__struct,\ + sizeof(struct __struct), __alignof__(struct __struct),\ + (__flags), NULL) + +#define blkdev_issue_flush(kdev, a, b) sync_blockdev(kdev) +#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0)) + +struct journal_s +{ + unsigned long j_flags; + int j_errno; + struct buffer_head * j_sb_buffer; + struct journal_superblock_s *j_superblock; + int j_format_version; + unsigned long j_head; + unsigned long j_tail; + unsigned long j_free; + unsigned long j_first, j_last; + kdev_t j_dev; + kdev_t j_fs_dev; + int j_blocksize; + unsigned int j_blk_offset; + unsigned int j_maxlen; + struct inode * j_inode; + tid_t j_tail_sequence; + tid_t j_transaction_sequence; + __u8 j_uuid[16]; + struct jbd2_revoke_table_s *j_revoke; + struct jbd2_revoke_table_s *j_revoke_table[2]; + tid_t j_failed_commit; + __u32 j_csum_seed; +}; + +#define is_journal_abort(x) 0 + +#define BUFFER_TRACE(bh, info) do {} while (0) + +/* Need this so we can compile with configure --enable-gcc-wall */ +#ifdef NO_INLINE_FUNCS +#define inline +#endif + +#endif /* _JFS_COMPAT_H */ diff --git a/src/ext2fs/kernel-jbd.h b/src/ext2fs/kernel-jbd.h new file mode 100644 index 00000000..19c78b21 --- /dev/null +++ b/src/ext2fs/kernel-jbd.h @@ -0,0 +1,461 @@ +/* + * linux/include/linux/jbd.h + * + * Written by Stephen C. Tweedie + * + * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved + * + * This file is part of the Linux kernel and is made available under + * the terms of the GNU General Public License, version 2, or at your + * option, any later version, incorporated herein by reference. + * + * Definitions for transaction data structures for the buffer cache + * filesystem journaling support. + */ + +#ifndef _LINUX_JBD_H +#define _LINUX_JBD_H + +#include "jfs_compat.h" +#define JFS_DEBUG +#define jfs_debug jbd_debug + +#if !defined(__GNUC__) && !defined(_MSC_VER) +#define __FUNCTION__ "" +#endif + +#define journal_oom_retry 1 + +#ifdef __STDC__ +#ifdef CONFIG_JBD_DEBUG +/* + * Define JBD_EXPENSIVE_CHECKING to enable more expensive internal + * consistency checks. By default we don't do this unless + * CONFIG_JBD_DEBUG is on. + */ +#define JBD_EXPENSIVE_CHECKING +extern int journal_enable_debug; + +#define jbd_debug(n, f, a...) \ + do { \ + if ((n) <= journal_enable_debug) { \ + printk (KERN_DEBUG "(%s, %d): %s: ", \ + __FILE__, __LINE__, __FUNCTION__); \ + printk (f, ## a); \ + } \ + } while (0) +#else +#ifdef __GNUC__ +#if defined(__KERNEL__) || !defined(CONFIG_JBD_DEBUG) +#define jbd_debug(f, a...) /**/ +#else +extern int journal_enable_debug; +#define jbd_debug(n, f, a...) \ + do { \ + if ((n) <= journal_enable_debug) { \ + printf("(%s, %d): %s: ", \ + __FILE__, __LINE__, __func__); \ + printf(f, ## a); \ + } \ + } while (0) +#endif /*__KERNEL__ */ +#else +#define jbd_debug(f, ...) /**/ +#endif +#endif +#else +#define jbd_debug(x) /* AIX doesn't do STDC */ +#endif + +extern void * __jbd_kmalloc (char *where, size_t size, int flags, int retry); +#define jbd_kmalloc(size, flags) \ + __jbd_kmalloc(__FUNCTION__, (size), (flags), journal_oom_retry) +#define jbd_rep_kmalloc(size, flags) \ + __jbd_kmalloc(__FUNCTION__, (size), (flags), 1) + +#define JFS_MIN_JOURNAL_BLOCKS 1024 + +/* + * Internal structures used by the logging mechanism: + */ + +#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */ + +/* + * On-disk structures + */ + +/* + * Descriptor block types: + */ + +#define JFS_DESCRIPTOR_BLOCK 1 +#define JFS_COMMIT_BLOCK 2 +#define JFS_SUPERBLOCK_V1 3 +#define JFS_SUPERBLOCK_V2 4 +#define JFS_REVOKE_BLOCK 5 + +/* + * Standard header for all descriptor blocks: + */ +typedef struct journal_header_s +{ + __u32 h_magic; + __u32 h_blocktype; + __u32 h_sequence; +} journal_header_t; + +/* + * Checksum types. + */ +#define JBD2_CRC32_CHKSUM 1 +#define JBD2_MD5_CHKSUM 2 +#define JBD2_SHA1_CHKSUM 3 +#define JBD2_CRC32C_CHKSUM 4 + +#define JBD2_CRC32_CHKSUM_SIZE 4 + +#define JBD2_CHECKSUM_BYTES (32 / sizeof(__u32)) +/* + * Commit block header for storing transactional checksums: + * + * NOTE: If FEATURE_COMPAT_CHECKSUM (checksum v1) is set, the h_chksum* + * fields are used to store a checksum of the descriptor and data blocks. + * + * If FEATURE_INCOMPAT_CSUM_V2 (checksum v2) is set, then the h_chksum + * field is used to store crc32c(uuid+commit_block). Each journal metadata + * block gets its own checksum, and data block checksums are stored in + * journal_block_tag (in the descriptor). The other h_chksum* fields are + * not used. + * + * If FEATURE_INCOMPAT_CSUM_V3 is set, the descriptor block uses + * journal_block_tag3_t to store a full 32-bit checksum. Everything else + * is the same as v2. + * + * Checksum v1, v2, and v3 are mutually exclusive features. + */ +struct commit_header { + __u32 h_magic; + __u32 h_blocktype; + __u32 h_sequence; + unsigned char h_chksum_type; + unsigned char h_chksum_size; + unsigned char h_padding[2]; + __u32 h_chksum[JBD2_CHECKSUM_BYTES]; + __u64 h_commit_sec; + __u32 h_commit_nsec; +}; + +/* + * The block tag: used to describe a single buffer in the journal + */ +typedef struct journal_block_tag3_s +{ + __u32 t_blocknr; /* The on-disk block number */ + __u32 t_flags; /* See below */ + __u32 t_blocknr_high; /* most-significant high 32bits. */ + __u32 t_checksum; /* crc32c(uuid+seq+block) */ +} journal_block_tag3_t; + +typedef struct journal_block_tag_s +{ + __u32 t_blocknr; /* The on-disk block number */ + __u16 t_checksum; /* truncated crc32c(uuid+seq+block) */ + __u16 t_flags; /* See below */ + __u32 t_blocknr_high; /* most-significant high 32bits. */ +} journal_block_tag_t; + +/* Tail of descriptor block, for checksumming */ +struct journal_block_tail { + __be32 t_checksum; +}; + +/* + * The revoke descriptor: used on disk to describe a series of blocks to + * be revoked from the log + */ +typedef struct journal_revoke_header_s +{ + journal_header_t r_header; + int r_count; /* Count of bytes used in the block */ +} journal_revoke_header_t; + +/* Tail of revoke block, for checksumming */ +struct journal_revoke_tail { + __be32 r_checksum; +}; + +/* Definitions for the journal tag flags word: */ +#define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */ +#define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */ +#define JFS_FLAG_DELETED 4 /* block deleted by this transaction */ +#define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */ + + +#define UUID_SIZE 16 +#define JFS_USERS_MAX 48 +#define JFS_USERS_SIZE (UUID_SIZE * JFS_USERS_MAX) +/* + * The journal superblock. All fields are in big-endian byte order. + */ +typedef struct journal_superblock_s +{ +/* 0x0000 */ + journal_header_t s_header; + +/* 0x000C */ + /* Static information describing the journal */ + __u32 s_blocksize; /* journal device blocksize */ + __u32 s_maxlen; /* total blocks in journal file */ + __u32 s_first; /* first block of log information */ + +/* 0x0018 */ + /* Dynamic information describing the current state of the log */ + __u32 s_sequence; /* first commit ID expected in log */ + __u32 s_start; /* blocknr of start of log */ + +/* 0x0020 */ + /* Error value, as set by journal_abort(). */ + __s32 s_errno; + +/* 0x0024 */ + /* Remaining fields are only valid in a version-2 superblock */ + __u32 s_feature_compat; /* compatible feature set */ + __u32 s_feature_incompat; /* incompatible feature set */ + __u32 s_feature_ro_compat; /* readonly-compatible feature set */ +/* 0x0030 */ + __u8 s_uuid[16]; /* 128-bit uuid for journal */ + +/* 0x0040 */ + __u32 s_nr_users; /* Nr of filesystems sharing log */ + + __u32 s_dynsuper; /* Blocknr of dynamic superblock copy*/ + +/* 0x0048 */ + __u32 s_max_transaction; /* Limit of journal blocks per trans.*/ + __u32 s_max_trans_data; /* Limit of data blocks per trans. */ + +/* 0x0050 */ + __u8 s_checksum_type; /* checksum type */ + __u8 s_padding2[3]; + __u32 s_padding[42]; + __u32 s_checksum; /* crc32c(superblock) */ + +/* 0x0100 */ + __u8 s_users[JFS_USERS_SIZE]; /* ids of all fs'es sharing the log */ + +/* 0x0400 */ +} journal_superblock_t; + +#define JFS_HAS_COMPAT_FEATURE(j,mask) \ + ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_compat & ext2fs_cpu_to_be32((mask)))) +#define JFS_HAS_RO_COMPAT_FEATURE(j,mask) \ + ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_ro_compat & ext2fs_cpu_to_be32((mask)))) +#define JFS_HAS_INCOMPAT_FEATURE(j,mask) \ + ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_incompat & ext2fs_cpu_to_be32((mask)))) + +#define JFS_FEATURE_COMPAT_CHECKSUM 0x00000001 + +#define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001 +#define JFS_FEATURE_INCOMPAT_64BIT 0x00000002 +#define JFS_FEATURE_INCOMPAT_ASYNC_COMMIT 0x00000004 +#define JFS_FEATURE_INCOMPAT_CSUM_V2 0x00000008 +#define JFS_FEATURE_INCOMPAT_CSUM_V3 0x00000010 + +/* Features known to this kernel version: */ +#define JFS_KNOWN_COMPAT_FEATURES 0 +#define JFS_KNOWN_ROCOMPAT_FEATURES 0 +#define JFS_KNOWN_INCOMPAT_FEATURES (JFS_FEATURE_INCOMPAT_REVOKE|\ + JFS_FEATURE_INCOMPAT_ASYNC_COMMIT|\ + JFS_FEATURE_INCOMPAT_64BIT|\ + JFS_FEATURE_INCOMPAT_CSUM_V2|\ + JFS_FEATURE_INCOMPAT_CSUM_V3) + +#ifdef NO_INLINE_FUNCS +extern size_t journal_tag_bytes(journal_t *journal); +extern int journal_has_csum_v2or3(journal_t *journal); +extern int tid_gt(tid_t x, tid_t y) EXT2FS_ATTR((unused)); +extern int tid_geq(tid_t x, tid_t y) EXT2FS_ATTR((unused)); +#endif + +#if (defined(E2FSCK_INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS)) +#ifdef E2FSCK_INCLUDE_INLINE_FUNCS +#if (__STDC_VERSION__ >= 199901L) +#define _INLINE_ extern inline +#else +#define _INLINE_ inline +#endif +#else /* !E2FSCK_INCLUDE_INLINE FUNCS */ +#if (__STDC_VERSION__ >= 199901L) +#define _INLINE_ inline +#else /* not C99 */ +#ifdef __GNUC__ +#define _INLINE_ extern __inline__ +#else /* For Watcom C */ +#define _INLINE_ extern inline +#endif /* __GNUC__ */ +#endif /* __STDC_VERSION__ >= 199901L */ +#endif /* INCLUDE_INLINE_FUNCS */ + +/* journal feature predicate functions */ +#define JFS_FEATURE_COMPAT_FUNCS(name, flagname) \ +_INLINE_ int jfs_has_feature_##name(journal_t *j); \ +_INLINE_ int jfs_has_feature_##name(journal_t *j) \ +{ \ + return ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_compat & \ + ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_##flagname)) != 0); \ +} \ +_INLINE_ void jfs_set_feature_##name(journal_t *j); \ +_INLINE_ void jfs_set_feature_##name(journal_t *j) \ +{ \ + (j)->j_superblock->s_feature_compat |= \ + ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_##flagname); \ +} \ +_INLINE_ void jfs_clear_feature_##name(journal_t *j); \ +_INLINE_ void jfs_clear_feature_##name(journal_t *j) \ +{ \ + (j)->j_superblock->s_feature_compat &= \ + ~ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_##flagname); \ +} + +#define JFS_FEATURE_RO_COMPAT_FUNCS(name, flagname) \ +_INLINE_ int jfs_has_feature_##name(journal_t *j); \ +_INLINE_ int jfs_has_feature_##name(journal_t *j) \ +{ \ + return ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_ro_compat & \ + ext2fs_cpu_to_be32(JFS_FEATURE_RO_COMPAT_##flagname)) != 0); \ +} \ +_INLINE_ void jfs_set_feature_##name(journal_t *j); \ +_INLINE_ void jfs_set_feature_##name(journal_t *j) \ +{ \ + (j)->j_superblock->s_feature_ro_compat |= \ + ext2fs_cpu_to_be32(JFS_FEATURE_RO_COMPAT_##flagname); \ +} \ +_INLINE_ void jfs_clear_feature_##name(journal_t *j); \ +_INLINE_ void jfs_clear_feature_##name(journal_t *j) \ +{ \ + (j)->j_superblock->s_feature_ro_compat &= \ + ~ext2fs_cpu_to_be32(JFS_FEATURE_RO_COMPAT_##flagname); \ +} + +#define JFS_FEATURE_INCOMPAT_FUNCS(name, flagname) \ +_INLINE_ int jfs_has_feature_##name(journal_t *j); \ +_INLINE_ int jfs_has_feature_##name(journal_t *j) \ +{ \ + return ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_incompat & \ + ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_##flagname)) != 0); \ +} \ +_INLINE_ void jfs_set_feature_##name(journal_t *j); \ +_INLINE_ void jfs_set_feature_##name(journal_t *j) \ +{ \ + (j)->j_superblock->s_feature_incompat |= \ + ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_##flagname); \ +} \ +_INLINE_ void jfs_clear_feature_##name(journal_t *j); \ +_INLINE_ void jfs_clear_feature_##name(journal_t *j) \ +{ \ + (j)->j_superblock->s_feature_incompat &= \ + ~ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_##flagname); \ +} + +#else +#define JFS_FEATURE_COMPAT_FUNCS(name, flagname) \ +extern int jfs_has_feature_##name(journal_t *j); \ +extern void jfs_set_feature_##name(journal_t *j); \ +extern void jfs_clear_feature_##name(journal_t *j); + +#define JFS_FEATURE_RO_COMPAT_FUNCS(name, flagname) \ +extern int jfs_has_feature_##name(journal_t *j); \ +extern void jfs_set_feature_##name(journal_t *j); \ +extern void jfs_clear_feature_##name(journal_t *j); + +#define JFS_FEATURE_INCOMPAT_FUNCS(name, flagname) \ +extern int jfs_has_feature_##name(journal_t *j); \ +extern void jfs_set_feature_##name(journal_t *j); \ +extern void jfs_clear_feature_##name(journal_t *j); + +#endif /* (defined(E2FSCK_INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS)) */ + +JFS_FEATURE_COMPAT_FUNCS(checksum, CHECKSUM) + +JFS_FEATURE_INCOMPAT_FUNCS(revoke, REVOKE) +JFS_FEATURE_INCOMPAT_FUNCS(64bit, 64BIT) +JFS_FEATURE_INCOMPAT_FUNCS(async_commit, ASYNC_COMMIT) +JFS_FEATURE_INCOMPAT_FUNCS(csum2, CSUM_V2) +JFS_FEATURE_INCOMPAT_FUNCS(csum3, CSUM_V3) + +#if (defined(E2FSCK_INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS)) +/* + * helper functions to deal with 32 or 64bit block numbers. + */ +_INLINE_ size_t journal_tag_bytes(journal_t *journal) +{ + size_t sz; + + if (jfs_has_feature_csum3(journal)) + return sizeof(journal_block_tag3_t); + + sz = sizeof(journal_block_tag_t); + + if (jfs_has_feature_csum2(journal)) + sz += sizeof(__u16); + + if (jfs_has_feature_64bit(journal)) + return sz; + + return sz - sizeof(__u32); +} + +_INLINE_ int journal_has_csum_v2or3(journal_t *journal) +{ + if (jfs_has_feature_csum2(journal) || jfs_has_feature_csum3(journal)) + return 1; + + return 0; +} + +/* Comparison functions for transaction IDs: perform comparisons using + * modulo arithmetic so that they work over sequence number wraps. */ + +_INLINE_ int tid_gt(tid_t x, tid_t y) +{ + int difference = (x - y); + return (difference > 0); +} + +_INLINE_ int tid_geq(tid_t x, tid_t y) +{ + int difference = (x - y); + return (difference >= 0); +} +#endif /* (defined(E2FSCK_INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS)) */ + +#undef _INLINE_ + +extern int journal_blocks_per_page(struct inode *inode); + +/* + * Definitions which augment the buffer_head layer + */ + +/* journaling buffer types */ +#define BJ_None 0 /* Not journaled */ +#define BJ_SyncData 1 /* Normal data: flush before commit */ +#define BJ_AsyncData 2 /* writepage data: wait on it before commit */ +#define BJ_Metadata 3 /* Normal journaled metadata */ +#define BJ_Forget 4 /* Buffer superceded by this transaction */ +#define BJ_IO 5 /* Buffer is for temporary IO use */ +#define BJ_Shadow 6 /* Buffer contents being shadowed to the log */ +#define BJ_LogCtl 7 /* Buffer contains log descriptors */ +#define BJ_Reserved 8 /* Buffer is reserved for access by journal */ +#define BJ_Types 9 + +extern int jbd_blocks_per_page(struct inode *inode); + +#endif /* _LINUX_JBD_H */ diff --git a/src/ext2fs/kernel-list.h b/src/ext2fs/kernel-list.h new file mode 100644 index 00000000..01f4f6b9 --- /dev/null +++ b/src/ext2fs/kernel-list.h @@ -0,0 +1,109 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +#if (!defined(__GNUC__) && !defined(__WATCOMC__)) +#define __inline__ +#endif + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_add(struct list_head * new, + struct list_head * prev, + struct list_head * next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/* + * Insert a new entry after the specified head.. + */ +static __inline__ void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/* + * Insert a new entry at the tail + */ +static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_del(struct list_head * prev, + struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +static __inline__ void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static __inline__ int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/* + * Splice in "list" into "head" + */ +static __inline__ void list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#endif diff --git a/src/ext2fs/missing.c b/src/ext2fs/missing.c deleted file mode 100644 index b132d63d..00000000 --- a/src/ext2fs/missing.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * missing.c --- stuff we need from features we don't care about (journal) - * - * Copyright (C) 2000 Theodore Ts'o. - * - * %Begin-Header% - * This file may be redistributed under the terms of the GNU Library - * General Public License, version 2. - * %End-Header% - */ - -#include "config.h" -#include "ext2fs.h" - -/* - * Convenience function which zeros out _num_ blocks starting at - * _blk_. In case of an error, the details of the error is returned - * via _ret_blk_ and _ret_count_ if they are non-NULL pointers. - * Returns 0 on success, and an error code on an error. - * - * As a special case, if the first argument is NULL, then it will - * attempt to free the static zeroizing buffer. (This is to keep - * programs that check for memory leaks happy.) - */ -#define MAX_STRIDE_LENGTH (4194304 / (int) fs->blocksize) -errcode_t ext2fs_zero_blocks2(ext2_filsys fs, blk64_t blk, int num, - blk64_t *ret_blk, int *ret_count) -{ - int j, count; - static void *buf; - static int stride_length; - errcode_t retval; - - /* If fs is null, clean up the static buffer and return */ - if (!fs) { - if (buf) { - free(buf); - buf = 0; - stride_length = 0; - } - return 0; - } - - /* Deal with zeroing less than 1 block */ - if (num <= 0) - return 0; - - /* Try a zero out command, if supported */ - retval = io_channel_zeroout(fs->io, blk, num); - if (retval == 0) - return 0; - - /* Allocate the zeroizing buffer if necessary */ - if (num > stride_length && stride_length < MAX_STRIDE_LENGTH) { - void *p; - int new_stride = num; - - if (new_stride > MAX_STRIDE_LENGTH) - new_stride = MAX_STRIDE_LENGTH; - p = realloc(buf, fs->blocksize * new_stride); - if (!p) - return EXT2_ET_NO_MEMORY; - buf = p; - stride_length = new_stride; - memset(buf, 0, fs->blocksize * stride_length); - } - /* OK, do the write loop */ - j=0; - while (j < num) { - if (blk % stride_length) { - count = stride_length - (blk % stride_length); - if (count > (num - j)) - count = num - j; - } else { - count = num - j; - if (count > stride_length) - count = stride_length; - } - retval = io_channel_write_blk64(fs->io, blk, count, buf); - if (retval) { - if (ret_count) - *ret_count = count; - if (ret_blk) - *ret_blk = blk; - return retval; - } - j += count; blk += count; - } - return 0; -} - -errcode_t ext2fs_zero_blocks(ext2_filsys fs, blk_t blk, int num, - blk_t *ret_blk, int *ret_count) -{ - blk64_t ret_blk2; - errcode_t retval; - - retval = ext2fs_zero_blocks2(fs, blk, num, &ret_blk2, ret_count); - if (retval) - *ret_blk = (blk_t) ret_blk2; - return retval; -} diff --git a/src/ext2fs/mkjournal.c b/src/ext2fs/mkjournal.c new file mode 100644 index 00000000..2e2e20fd --- /dev/null +++ b/src/ext2fs/mkjournal.c @@ -0,0 +1,591 @@ +/* + * mkjournal.c --- make a journal for a filesystem + * + * Copyright (C) 2000 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include "config.h" +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_IOCTL_H +#include +#endif +#if HAVE_NETINET_IN_H +#include +#endif +#ifdef _MSC_VER +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#include "kernel-jbd.h" + +/* + * This function automatically sets up the journal superblock and + * returns it as an allocated block. + */ +errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, + __u32 num_blocks, int flags, + char **ret_jsb) +{ + errcode_t retval; + journal_superblock_t *jsb; + + if (num_blocks < JFS_MIN_JOURNAL_BLOCKS) + return EXT2_ET_JOURNAL_TOO_SMALL; + + if ((retval = ext2fs_get_mem(fs->blocksize, &jsb))) + return retval; + + memset (jsb, 0, fs->blocksize); + + jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER); + if (flags & EXT2_MKJOURNAL_V1_SUPER) + jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V1); + else + jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2); + jsb->s_blocksize = htonl(fs->blocksize); + jsb->s_maxlen = htonl(num_blocks); + jsb->s_nr_users = htonl(1); + jsb->s_first = htonl(1); + jsb->s_sequence = htonl(1); + memcpy(jsb->s_uuid, fs->super->s_uuid, sizeof(fs->super->s_uuid)); + /* + * If we're creating an external journal device, we need to + * adjust these fields. + */ + if (ext2fs_has_feature_journal_dev(fs->super)) { + jsb->s_nr_users = 0; + jsb->s_first = htonl(ext2fs_journal_sb_start(fs->blocksize) + 1); + } + + *ret_jsb = (char *) jsb; + return 0; +} + +/* + * This function writes a journal using POSIX routines. It is used + * for creating external journals and creating journals on live + * filesystems. + */ +static errcode_t write_journal_file(ext2_filsys fs, char *filename, + blk_t num_blocks, int flags) +{ + errcode_t retval; + char *buf = 0; + int fd, ret_size; + blk_t i; + + if ((retval = ext2fs_create_journal_superblock(fs, num_blocks, flags, + &buf))) + return retval; + + /* Open the device or journal file */ + if ((fd = open(filename, O_WRONLY)) < 0) { + retval = errno; + goto errfree; + } + + /* Write the superblock out */ + retval = EXT2_ET_SHORT_WRITE; + ret_size = write(fd, buf, fs->blocksize); + if (ret_size < 0) { + retval = errno; + goto errout; + } + if (ret_size != (int) fs->blocksize) + goto errout; + memset(buf, 0, fs->blocksize); + + if (flags & EXT2_MKJOURNAL_LAZYINIT) + goto success; + + for (i = 1; i < num_blocks; i++) { + ret_size = write(fd, buf, fs->blocksize); + if (ret_size < 0) { + retval = errno; + goto errout; + } + if (ret_size != (int) fs->blocksize) + goto errout; + } + +success: + retval = 0; +errout: + close(fd); +errfree: + ext2fs_free_mem(&buf); + return retval; +} + +/* + * Convenience function which zeros out _num_ blocks starting at + * _blk_. In case of an error, the details of the error is returned + * via _ret_blk_ and _ret_count_ if they are non-NULL pointers. + * Returns 0 on success, and an error code on an error. + * + * As a special case, if the first argument is NULL, then it will + * attempt to free the static zeroizing buffer. (This is to keep + * programs that check for memory leaks happy.) + */ +#define MAX_STRIDE_LENGTH (4194304 / (int) fs->blocksize) +errcode_t ext2fs_zero_blocks2(ext2_filsys fs, blk64_t blk, int num, + blk64_t *ret_blk, int *ret_count) +{ + int j, count; + static void *buf; + static int stride_length; + errcode_t retval; + + /* If fs is null, clean up the static buffer and return */ + if (!fs) { + if (buf) { + free(buf); + buf = 0; + stride_length = 0; + } + return 0; + } + + /* Deal with zeroing less than 1 block */ + if (num <= 0) + return 0; + + /* Try a zero out command, if supported */ + retval = io_channel_zeroout(fs->io, blk, num); + if (retval == 0) + return 0; + + /* Allocate the zeroizing buffer if necessary */ + if (num > stride_length && stride_length < MAX_STRIDE_LENGTH) { + void *p; + int new_stride = num; + + if (new_stride > MAX_STRIDE_LENGTH) + new_stride = MAX_STRIDE_LENGTH; + p = realloc(buf, fs->blocksize * new_stride); + if (!p) + return EXT2_ET_NO_MEMORY; + buf = p; + stride_length = new_stride; + memset(buf, 0, fs->blocksize * stride_length); + } + /* OK, do the write loop */ + j=0; + while (j < num) { + if (blk % stride_length) { + count = stride_length - (blk % stride_length); + if (count > (num - j)) + count = num - j; + } else { + count = num - j; + if (count > stride_length) + count = stride_length; + } + retval = io_channel_write_blk64(fs->io, blk, count, buf); + if (retval) { + if (ret_count) + *ret_count = count; + if (ret_blk) + *ret_blk = blk; + return retval; + } + j += count; blk += count; + } + return 0; +} + +errcode_t ext2fs_zero_blocks(ext2_filsys fs, blk_t blk, int num, + blk_t *ret_blk, int *ret_count) +{ + blk64_t ret_blk2; + errcode_t retval; + + retval = ext2fs_zero_blocks2(fs, blk, num, &ret_blk2, ret_count); + if (retval) + *ret_blk = (blk_t) ret_blk2; + return retval; +} + +/* + * Calculate the initial goal block to be roughly at the middle of the + * filesystem. Pick a group that has the largest number of free + * blocks. + */ +static blk64_t get_midpoint_journal_block(ext2_filsys fs) +{ + dgrp_t group, start, end, i, log_flex; + + group = ext2fs_group_of_blk2(fs, (ext2fs_blocks_count(fs->super) - + fs->super->s_first_data_block) / 2); + log_flex = 1 << fs->super->s_log_groups_per_flex; + if (fs->super->s_log_groups_per_flex && (group > log_flex)) { + group = group & ~(log_flex - 1); + while ((group < fs->group_desc_count) && + ext2fs_bg_free_blocks_count(fs, group) == 0) + group++; + if (group == fs->group_desc_count) + group = 0; + start = group; + } else + start = (group > 0) ? group-1 : group; + end = ((group+1) < fs->group_desc_count) ? group+1 : group; + group = start; + for (i = start + 1; i <= end; i++) + if (ext2fs_bg_free_blocks_count(fs, i) > + ext2fs_bg_free_blocks_count(fs, group)) + group = i; + return ext2fs_group_first_block2(fs, group); +} + +/* + * This function creates a journal using direct I/O routines. + */ +static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino, + blk_t num_blocks, blk64_t goal, int flags) +{ + char *buf; + errcode_t retval; + struct ext2_inode inode; + unsigned long long inode_size; + int falloc_flags = EXT2_FALLOCATE_FORCE_INIT; + blk64_t zblk; + + if ((retval = ext2fs_create_journal_superblock(fs, num_blocks, flags, + &buf))) + return retval; + + if ((retval = ext2fs_read_bitmaps(fs))) + goto out2; + + if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) + goto out2; + + if (inode.i_blocks > 0) { + retval = EEXIST; + goto out2; + } + + if (goal == ~0ULL) + goal = get_midpoint_journal_block(fs); + + if (ext2fs_has_feature_extents(fs->super)) + inode.i_flags |= EXT4_EXTENTS_FL; + + if (!(flags & EXT2_MKJOURNAL_LAZYINIT)) + falloc_flags |= EXT2_FALLOCATE_ZERO_BLOCKS; + + inode_size = (unsigned long long)fs->blocksize * num_blocks; + inode.i_mtime = inode.i_ctime = fs->now ? fs->now : time(0); + inode.i_links_count = 1; + inode.i_mode = LINUX_S_IFREG | 0600; + retval = ext2fs_inode_size_set(fs, &inode, inode_size); + if (retval) + goto out2; + + retval = ext2fs_fallocate(fs, falloc_flags, journal_ino, + &inode, goal, 0, num_blocks); + if (retval) + goto out2; + + if ((retval = ext2fs_write_new_inode(fs, journal_ino, &inode))) + goto out2; + + retval = ext2fs_bmap2(fs, journal_ino, &inode, NULL, 0, 0, NULL, &zblk); + if (retval) + goto out2; + + retval = io_channel_write_blk64(fs->io, zblk, 1, buf); + if (retval) + goto out2; + + memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4); + fs->super->s_jnl_blocks[15] = inode.i_size_high; + fs->super->s_jnl_blocks[16] = inode.i_size; + fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS; + ext2fs_mark_super_dirty(fs); + +out2: + ext2fs_free_mem(&buf); + return retval; +} + +/* + * Find a reasonable journal file size (in blocks) given the number of blocks + * in the filesystem. For very small filesystems, it is not reasonable to + * have a journal that fills more than half of the filesystem. + * + * n.b. comments assume 4k blocks + */ +int ext2fs_default_journal_size(__u64 num_blocks) +{ + if (num_blocks < 2048) + return -1; + if (num_blocks < 32768) /* 128 MB */ + return (1024); /* 4 MB */ + if (num_blocks < 256*1024) /* 1 GB */ + return (4096); /* 16 MB */ + if (num_blocks < 512*1024) /* 2 GB */ + return (8192); /* 32 MB */ + if (num_blocks < 4096*1024) /* 16 GB */ + return (16384); /* 64 MB */ + if (num_blocks < 8192*1024) /* 32 GB */ + return (32768); /* 128 MB */ + if (num_blocks < 16384*1024) /* 64 GB */ + return (65536); /* 256 MB */ + if (num_blocks < 32768*1024) /* 128 GB */ + return (131072); /* 512 MB */ + return 262144; /* 1 GB */ +} + +int ext2fs_journal_sb_start(int blocksize) +{ + if (blocksize == EXT2_MIN_BLOCK_SIZE) + return 2; + return 1; +} + +/* + * This function adds a journal device to a filesystem + */ +errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev) +{ + struct stat st; + errcode_t retval; + char buf[SUPERBLOCK_SIZE]; + journal_superblock_t *jsb; + int start; + __u32 i, nr_users; + + /* Make sure the device exists and is a block device */ + if (stat(journal_dev->device_name, &st) < 0) + return errno; + + if (!S_ISBLK(st.st_mode)) + return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */ + + /* Get the journal superblock */ + start = ext2fs_journal_sb_start(journal_dev->blocksize); + if ((retval = io_channel_read_blk64(journal_dev->io, start, + -SUPERBLOCK_SIZE, + buf))) + return retval; + + jsb = (journal_superblock_t *) buf; + if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) || + (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) + return EXT2_ET_NO_JOURNAL_SB; + + if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize) + return EXT2_ET_UNEXPECTED_BLOCK_SIZE; + + /* Check and see if this filesystem has already been added */ + nr_users = ntohl(jsb->s_nr_users); + if (nr_users > JFS_USERS_MAX) + return EXT2_ET_CORRUPT_JOURNAL_SB; + for (i=0; i < nr_users; i++) { + if (memcmp(fs->super->s_uuid, + &jsb->s_users[i*16], 16) == 0) + break; + } + if (i >= nr_users) { + memcpy(&jsb->s_users[nr_users*16], + fs->super->s_uuid, 16); + jsb->s_nr_users = htonl(nr_users+1); + } + + /* Writeback the journal superblock */ + if ((retval = io_channel_write_blk64(journal_dev->io, start, + -SUPERBLOCK_SIZE, buf))) + return retval; + + fs->super->s_journal_inum = 0; + fs->super->s_journal_dev = st.st_rdev; + memcpy(fs->super->s_journal_uuid, jsb->s_uuid, + sizeof(fs->super->s_journal_uuid)); + memset(fs->super->s_jnl_blocks, 0, sizeof(fs->super->s_jnl_blocks)); + ext2fs_set_feature_journal(fs->super); + ext2fs_mark_super_dirty(fs); + return 0; +} + +/* + * This function adds a journal inode to a filesystem, using either + * POSIX routines if the filesystem is mounted, or using direct I/O + * functions if it is not. + */ +errcode_t ext2fs_add_journal_inode2(ext2_filsys fs, blk_t num_blocks, + blk64_t goal, int flags) +{ + errcode_t retval; + ext2_ino_t journal_ino; + struct stat st; + char jfile[1024]; + int mount_flags; + int fd = -1; + + if (flags & EXT2_MKJOURNAL_NO_MNT_CHECK) + mount_flags = 0; + else if ((retval = ext2fs_check_mount_point(fs->device_name, + &mount_flags, + jfile, sizeof(jfile)-10))) + return retval; + + if (mount_flags & EXT2_MF_MOUNTED) { +#if HAVE_EXT2_IOCTLS + int f = 0; +#endif + strcat(jfile, "/.journal"); + + /* + * If .../.journal already exists, make sure any + * immutable or append-only flags are cleared. + */ +#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) + (void) chflags (jfile, 0); +#else +#if HAVE_EXT2_IOCTLS + fd = open(jfile, O_RDONLY); + if (fd >= 0) { + retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f); + close(fd); + if (retval) + return retval; + } +#endif +#endif + + /* Create the journal file */ + if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0) + return errno; + + /* Note that we can't do lazy journal initialization for mounted + * filesystems, since the zero writing is also allocating the + * journal blocks. We could use fallocate, but not all kernels + * support that, and creating a journal on a mounted ext2 + * filesystems is extremely rare these days... Ignore it. */ + flags &= ~EXT2_MKJOURNAL_LAZYINIT; + + if ((retval = write_journal_file(fs, jfile, num_blocks, flags))) + goto errout; + + /* Get inode number of the journal file */ + if (fstat(fd, &st) < 0) { + retval = errno; + goto errout; + } + +#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) + retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE); +#else +#if HAVE_EXT2_IOCTLS + if (ioctl(fd, EXT2_IOC_GETFLAGS, &f) < 0) { + retval = errno; + goto errout; + } + f |= EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL; + retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f); +#endif +#endif + if (retval) { + retval = errno; + goto errout; + } + + if (close(fd) < 0) { + retval = errno; + fd = -1; + goto errout; + } + journal_ino = st.st_ino; + memset(fs->super->s_jnl_blocks, 0, + sizeof(fs->super->s_jnl_blocks)); + } else { + if ((mount_flags & EXT2_MF_BUSY) && + !(fs->flags & EXT2_FLAG_EXCLUSIVE)) { + retval = EBUSY; + goto errout; + } + journal_ino = EXT2_JOURNAL_INO; + if ((retval = write_journal_inode(fs, journal_ino, + num_blocks, goal, flags))) + return retval; + } + + fs->super->s_journal_inum = journal_ino; + fs->super->s_journal_dev = 0; + memset(fs->super->s_journal_uuid, 0, + sizeof(fs->super->s_journal_uuid)); + ext2fs_set_feature_journal(fs->super); + + ext2fs_mark_super_dirty(fs); + return 0; +errout: + if (fd >= 0) + close(fd); + return retval; +} + +errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t num_blocks, int flags) +{ + return ext2fs_add_journal_inode2(fs, num_blocks, ~0ULL, flags); +} + + +#ifdef DEBUG +main(int argc, char **argv) +{ + errcode_t retval; + char *device_name; + ext2_filsys fs; + + if (argc < 2) { + fprintf(stderr, "Usage: %s filesystem\n", argv[0]); + exit(1); + } + device_name = argv[1]; + + retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0, + unix_io_manager, &fs); + if (retval) { + com_err(argv[0], retval, "while opening %s", device_name); + exit(1); + } + + retval = ext2fs_add_journal_inode(fs, JFS_MIN_JOURNAL_BLOCKS, 0); + if (retval) { + com_err(argv[0], retval, "while adding journal to %s", + device_name); + exit(1); + } + retval = ext2fs_flush(fs); + if (retval) { + printf("Warning, had trouble writing out superblocks.\n"); + } + ext2fs_close_free(&fs); + exit(0); + +} +#endif diff --git a/src/ext2fs/nt_io.c b/src/ext2fs/nt_io.c index 5c2e6da5..65abe4ee 100644 --- a/src/ext2fs/nt_io.c +++ b/src/ext2fs/nt_io.c @@ -316,23 +316,17 @@ static VOID _GetDeviceSize(IN HANDLE h, OUT unsigned __int64 *FsSize) IOCTL_DISK_GET_PARTITION_INFO_EX, &pi, sizeof(pi), &pi, sizeof(pi)); if (NT_SUCCESS(Status)) { - uprintf("Size retrieved with: IOCTL_DISK_GET_PARTITION_INFO_EX"); *FsSize = pi.PartitionLength.QuadPart; } else if (Status == STATUS_INVALID_DEVICE_REQUEST) { // No partitions: Try a drive geometry request - uprintf("IOCTL_DISK_GET_PARTITION_INFO_EX failed, trying with IOCTL_DISK_GET_DRIVE_GEOMETRY_EX"); RtlZeroMemory(&gi, sizeof(gi)); Status = pfNtDeviceIoControlFile(h, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, &gi, sizeof(gi), &gi, sizeof(gi)); - if (NT_SUCCESS(Status)) { - uprintf("Size retrieved with: IOCTL_DISK_GET_DRIVE_GEOMETRY_EX"); + if (NT_SUCCESS(Status)) *FsSize = gi.DiskSize.QuadPart; - } else { - uprintf("IOCTL_DISK_GET_DRIVE_GEOMETRY_EX: [%x] %s", Status, NtStatusError(Status)); - } } else if (Status == STATUS_INVALID_PARAMETER) { // Possibly a straight image file if (GetFileSizeEx(h, &li)) @@ -445,6 +439,12 @@ errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags) return 0; } +// Not implemented +errcode_t ext2fs_check_mount_point(const char *file, int *mount_flags, char *mtpt, int mtlen) +{ + return EXT2_ET_OP_NOT_SUPPORTED; +} + // Returns the number of blocks in a partition // Note: Do *NOT* be tempted to cache the device size according to the NT path as // different removable devices (e.g. UFD) may be remounted under the same path. @@ -648,7 +648,6 @@ static errcode_t nt_read_blk(io_channel channel, unsigned long block, int count, return 0; } -// TODO: Add an nt_write_blk64() static errcode_t nt_write_blk(io_channel channel, unsigned long block, int count, const void *buf) { ULONG write_size; diff --git a/src/format.c b/src/format.c index c7d1b078..a2f0faa4 100644 --- a/src/format.c +++ b/src/format.c @@ -49,7 +49,6 @@ #include "format.h" #include "badblocks.h" #include "bled/bled.h" -#include "ext2fs/ext2fs.h" #include "../res/grub/grub_version.h" /* @@ -665,51 +664,110 @@ out: return r; } -extern io_manager nt_io_manager(void); -BOOL FormatExt2Fs(void) +BOOL FormatExt2Fs(const char* label) { - const char* path = "\\??\\C:\\tmp\\disk.img"; + // Mostly taken from mke2fs.conf + const float reserve_ratio = 0.05f; + const ext2fs_default_t ext2fs_default[5] = { + { 3*MB, 1024, 128, 3}, // "floppy" + { 512*MB, 1024, 128, 2}, // "small" + { 4*GB, 4096, 256, 2}, // "default" + { 16*GB, 4096, 256, 3}, // "big" + { 1024*TB, 4096, 256, 4} // "huge" + }; + + BOOL ret = FALSE; + char* path = NULL; int i, count; struct ext2_super_block features = { 0 }; io_manager manager = nt_io_manager(); + blk_t journal_size; blk64_t size = 0, cur; - ext2_filsys ext2fs; + ext2_filsys ext2fs = NULL; errcode_t r; + uint8_t* buf = NULL; + +#if defined(RUFUS_TEST) + // Create a 32 MB disk image file to test + uint8_t zb[1024]; HANDLE h; DWORD dwSize; - const uint8_t buf[1024] = { 0 }; - - // Create a 32 MB zeroed file to test + path = strdup("\\??\\C:\\tmp\\disk.img"); + memset(zb, 0xFF, sizeof(zb)); // Set to nonzero so we can detect init issues h = CreateFileU(path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); for (i = 0; i < 32 * 1024; i++) { - if (!WriteFile(h, buf, sizeof(buf), &dwSize, NULL) || (dwSize != sizeof(buf))) { + if (!WriteFile(h, zb, sizeof(zb), &dwSize, NULL) || (dwSize != sizeof(zb))) { uprintf("Write error: %s", WindowsErrorString()); break; } } CloseHandle(h); +#else + path = GetPartitionName((DWORD)ComboBox_GetItemData(hDeviceList, ComboBox_GetCurSel(hDeviceList)), CASPER_PARTITION_DEFAULT); +#endif + if (path == NULL) + goto out; - // TODO: We could probably remove that call and get our size from a different means - r = ext2fs_get_device_size2(path, EXT2_BLOCK_SIZE(&features), &size); - uprintf("ext2fs_get_device_size: %d", r); - // TODO: ERROR HANDLING - // Set the number of blocks and reserved blocks + PrintInfoDebug(0, MSG_222, "ext3"); + + // Figure out the volume size and block size + r = ext2fs_get_device_size2(path, KB, &size); + if ((r != 0) || (size == 0)) { + uprintf("Could not read device size: %d", r); + goto out; + } + size *= KB; + for (i = 0; i < ARRAYSIZE(ext2fs_default); i++) { + if (size < ext2fs_default[i].max_size) + break; + } + assert(i < ARRAYSIZE(ext2fs_default)); + size /= ext2fs_default[i].block_size; + for (features.s_log_block_size = 0; EXT2_BLOCK_SIZE_BITS(&features) <= EXT2_MAX_BLOCK_LOG_SIZE; features.s_log_block_size++) { + if (EXT2_BLOCK_SIZE(&features) == ext2fs_default[i].block_size) + break; + } + assert(EXT2_BLOCK_SIZE_BITS(&features) <= EXT2_MAX_BLOCK_LOG_SIZE); + + // Set the blocks, reserved blocks and inodes ext2fs_blocks_count_set(&features, size); - ext2fs_r_blocks_count_set(&features, (blk64_t)(0.05f * ext2fs_blocks_count(&features))); + ext2fs_r_blocks_count_set(&features, (blk64_t)(reserve_ratio * size)); features.s_rev_level = 1; - features.s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; - // TODO: This needs to be computed according to volume size - features.s_inodes_count = 8192; + features.s_inode_size = ext2fs_default[i].inode_size; + features.s_inodes_count = ((ext2fs_blocks_count(&features) >> ext2fs_default[i].inode_ratio) > UINT32_MAX) ? + UINT32_MAX : (uint32_t)(ext2fs_blocks_count(&features) >> ext2fs_default[i].inode_ratio); + uprintf("%d inodes, %lld blocks (block size = %d)", features.s_inodes_count, size, EXT2_BLOCK_SIZE(&features)); + uprintf("%lld blocks (%0.1f%%) reserved for the super user", ext2fs_r_blocks_count(&features), reserve_ratio * 100.0f); - // TODO: Set a volume label + // Set features for ext3 + ext2fs_set_feature_journal(&features); + ext2fs_set_feature_xattr(&features); + ext2fs_set_feature_resize_inode(&features); + ext2fs_set_feature_dir_index(&features); + ext2fs_set_feature_filetype(&features); + ext2fs_set_feature_sparse_super(&features); + ext2fs_set_feature_large_file(&features); + features.s_backup_bgs[0] = 1; + features.s_default_mount_opts = EXT2_DEFM_XATTR_USER | EXT2_DEFM_ACL; - // Initialize the superblock + // Now that we have set our base features, initialize a virtual superblock r = ext2fs_initialize(path, EXT2_FLAG_EXCLUSIVE | EXT2_FLAG_64BITS, &features, manager, &ext2fs); - uprintf("ext2fs_initialize: %d", r); - // TODO: ERROR HANDLING + if (r != 0) { + uprintf("Could not initialize ext2fs features: %d", r); + goto out; + } - // TODO: Erase superblock data - // Now that the superblock has been initialized, set it up + // Zero 16 blocks of data from the start of our volume + buf = calloc(16, ext2fs->io->block_size); + assert(buf != NULL); + r = io_channel_write_blk64(ext2fs->io, 0, 16, buf); + safe_free(buf); + if (r != 0) { + uprintf("Could not zero ext2fs superblock area: %d", r); + goto out; + } + + // Finish setting up the file system CoCreateGuid((GUID*)ext2fs->super->s_uuid); ext2fs_init_csum_seed(ext2fs); ext2fs->super->s_def_hash_version = EXT2_HASH_HALF_MD4; @@ -717,70 +775,79 @@ BOOL FormatExt2Fs(void) ext2fs->super->s_max_mnt_count = -1; ext2fs->super->s_creator_os = EXT2_OS_WINDOWS; ext2fs->super->s_errors = EXT2_ERRORS_CONTINUE; - - // TODO: ext2 + journaling = ext3, so the way to set ext3 is to add features: - // ext_attr, resize_inode, dir_index, filetype, sparse_super, has_journal, needs_recovery - ext2fs_set_feature_xattr(&features); - // ext2fs_set_feature_resize_inode(&sb); - // ext2fs_set_feature_dir_index(&sb); - ext2fs_set_feature_filetype(&features); - // ext2fs_set_feature_sparse_super(&sb); - // ext2fs_set_feature_journal(&sb); - // ext2fs_set_feature_journal_needs_recovery(&sb); - - // Optional we may want to add: - // ext2fs_set_feature_64bit(&sb); - // NB: the following is not needed as it is set by the OS automatically when creating a > 2GB file - // ext2fs_set_feature_large_file(&sb); + static_strcpy(ext2fs->super->s_volume_name, label); r = ext2fs_allocate_tables(ext2fs); - uprintf("ext2fs_allocate_tables: %d", r); - // TODO: ERROR HANDLING + if (r != 0) { + uprintf("Could not allocate ext2fs tables: %d", r); + goto out; + } r = ext2fs_convert_subcluster_bitmap(ext2fs, &ext2fs->block_map); - uprintf("ext2fs_convert_subcluster_bitmap: %d", r); - // TODO: ERROR HANDLING + if (r != 0) { + uprintf("Could set ext2fs cluster bitmap: %d", r); + goto out; + } // Wipe inode table for (i = 0; i < (int)ext2fs->group_desc_count; i++) { cur = ext2fs_inode_table_loc(ext2fs, i); count = ext2fs_div_ceil((ext2fs->super->s_inodes_per_group - ext2fs_bg_itable_unused(ext2fs, i)) - * EXT2_GOOD_OLD_INODE_SIZE, EXT2_GOOD_OLD_INODE_SIZE); + * EXT2_BLOCK_SIZE(ext2fs->super), EXT2_BLOCK_SIZE(ext2fs->super)); r = ext2fs_zero_blocks2(ext2fs, cur, count, &cur, &count); if (r != 0) { - uprintf("Could not zero inode at %llu (%d blocks): %d\n", cur, count, r); - // TODO: ERROR HANDLING - break; + uprintf("Could not zero ext2fs inode at %llu (%d blocks): %d\n", cur, count, r); + goto out; } } - // Create root dir + // Create root and lost+found dirs r = ext2fs_mkdir(ext2fs, EXT2_ROOT_INO, EXT2_ROOT_INO, 0); - uprintf("ext2fs_mkdir(root): %d", r); - // TODO: ERROR HANDLING - - // Create 'lost+found' + if (r != 0) { + uprintf("Failed to create ext2fs root dir: %d", r); + goto out; + } ext2fs->umask = 077; r = ext2fs_mkdir(ext2fs, EXT2_ROOT_INO, 0, "lost+found"); - uprintf("ext2fs_mkdir(lost+found): %d", r); - // TODO: ERROR HANDLING + if (r != 0) { + uprintf("Failed to create ext2fs 'lost+found' dir: %d", r); + goto out; + } + // Create bitmaps for (i = EXT2_ROOT_INO + 1; i < (int)EXT2_FIRST_INODE(ext2fs->super); i++) - ext2fs_inode_alloc_stats2(ext2fs, i, +1, 0); + ext2fs_inode_alloc_stats(ext2fs, i, 1); ext2fs_mark_ib_dirty(ext2fs); r = ext2fs_mark_inode_bitmap2(ext2fs->inode_map, EXT2_BAD_INO); - uprintf("ext2fs_mark_inode_bitmap2: %d", r); - // TODO: ERROR HANDLING - ext2fs_inode_alloc_stats2(ext2fs, EXT2_BAD_INO, 1, 0); + if (r != 0) { + uprintf("Could not set ext2fs inode bitmaps: %d", r); + goto out; + } + ext2fs_inode_alloc_stats(ext2fs, EXT2_BAD_INO, 1); r = ext2fs_update_bb_inode(ext2fs, NULL); - uprintf("ext2fs_update_bb_inode: %d", r); - // TODO: ERROR HANDLING + if (r != 0) { + uprintf("Could not set ext2fs inode stats: %d", r); + goto out; + } - r = ext2fs_close_free(&ext2fs); - uprintf("ext2fs_close_free: %d", r); - // TODO: ERROR HANDLING + // Create the journal + journal_size = ext2fs_default_journal_size(ext2fs_blocks_count(ext2fs->super)); + uprintf("Creating journal (%d blocks)", journal_size); + r = ext2fs_add_journal_inode(ext2fs, journal_size, EXT2_MKJOURNAL_NO_MNT_CHECK | EXT2_MKJOURNAL_LAZYINIT); - return TRUE; + // Finally we can call close() to get the file system gets created + r = ext2fs_close(ext2fs); + if (r != 0) { + uprintf("Could not create ext3 volume: %d", r); + goto out; + } + ret = TRUE; + +out: + ext2fs_free(ext2fs); + free(buf); + free(path); + return ret; } /* @@ -2123,6 +2190,7 @@ DWORD WINAPI FormatThread(void* param) // If FAT32 is requested and we have a large drive (>32 GB) use // large FAT32 format, else use MS's FormatEx. + // TODO: We'll want a single call for ext2/ext3/ext4, large FAT32 and everything (after we switch to VDS?) ret = use_large_fat32?FormatFAT32(DriveIndex):FormatDrive(DriveIndex); if (!ret) { // Error will be set by FormatDrive() in FormatStatus diff --git a/src/format.h b/src/format.h index 3c5ef25e..29d30a28 100644 --- a/src/format.h +++ b/src/format.h @@ -20,6 +20,8 @@ #include #include // for MEDIA_TYPE +#include "ext2fs/ext2fs.h" + #pragma once /* Callback command types (some errorcode were filled from HPUSBFW V2.2.3 and their @@ -154,3 +156,13 @@ typedef struct { #define die(msg, err) do { uprintf(msg); \ FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|err; \ goto out; } while(0) + +// For ext2/ext3/ext4 formatting +typedef struct { + uint64_t max_size; + uint32_t block_size; + uint32_t inode_size; + uint32_t inode_ratio; // inode to data ration (bitshift) +} ext2fs_default_t; + +extern io_manager nt_io_manager(void); diff --git a/src/rufus.c b/src/rufus.c index 80b12454..f62d5152 100755 --- a/src/rufus.c +++ b/src/rufus.c @@ -1864,7 +1864,7 @@ out: /* * Main dialog callback */ -extern BOOL FormatExt2Fs(void); +extern BOOL FormatExt2Fs(const char* label); static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static DWORD DeviceNum = 0; @@ -1897,7 +1897,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA case WM_COMMAND: #ifdef RUFUS_TEST if (LOWORD(wParam) == IDC_TEST) { - FormatExt2Fs(); + FormatExt2Fs("casper-rw"); break; } #endif diff --git a/src/rufus.h b/src/rufus.h index 7bb781a6..de175ba9 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -82,6 +82,7 @@ #define CHECK_DRIVE_TIMEOUT 2000 #define MARQUEE_TIMER_REFRESH 10 // Time between progress bar marquee refreshes, in ms #define FS_DEFAULT FS_FAT32 +#define CASPER_PARTITION_DEFAULT 2 #define SINGLE_CLUSTERSIZE_DEFAULT 0x00000100 #define BADLOCKS_PATTERN_TYPES 3 #define BADBLOCK_PATTERN_COUNT 4 diff --git a/src/rufus.rc b/src/rufus.rc index 49bc2958..a3c5ddaa 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 232, 326 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 3.6.1522" +CAPTION "Rufus 3.6.1523" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -394,8 +394,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,6,1522,0 - PRODUCTVERSION 3,6,1522,0 + FILEVERSION 3,6,1523,0 + PRODUCTVERSION 3,6,1523,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -413,13 +413,13 @@ BEGIN VALUE "Comments", "https://akeo.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "3.6.1522" + VALUE "FileVersion", "3.6.1523" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2019 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/copyleft/gpl.html" VALUE "OriginalFilename", "rufus-3.6.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "3.6.1522" + VALUE "ProductVersion", "3.6.1523" END END BLOCK "VarFileInfo"