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"