[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20230630155128.GA11419@frogsfrogsfrogs>
Date: Fri, 30 Jun 2023 08:51:28 -0700
From: "Darrick J. Wong" <djwong@...nel.org>
To: Johannes Schauer Marin Rodrigues <josch@...ter-muffin.de>
Cc: linux-ext4@...r.kernel.org
Subject: Re: [PATCH 1/1] mke2fs: the -d option can now handle tarball input
On Tue, Jun 20, 2023 at 02:16:41PM +0200, Johannes Schauer Marin Rodrigues wrote:
> If archive.h is available during compilation, enable mke2fs to read a
> tarball as input. Since libarchive.so.13 is opened with dlopen,
> libarchive is not a hard library dependency of the resulting binary.
I can't say I'm in favor of adding build dependencies to e2fsprogs,
since the point of -d taking a directory arg was to *avoid* having to
understand anything other than posix(ish) directory tree walking APIs.
> This enables the creation of filesystems containing files which would
> otherwise need superuser privileges to create (like device nodes, which
> are also not allowed in unshared user namespaces). By reading from
> standard input when the filename is a dash (-), mke2fs can be used as
> part of a shell pipeline without temporary files.
What if the argument is actually a Microsoft CAB archive (which
libarchive claims to support)? Will it actually copy the cab archive
into an ext4 image?
--D
> A round-trip from tarball to ext4 to tarball yields bit-by-bit identical
> results.
> ---
> MCONFIG.in | 1 +
> configure | 52 +++
> configure.ac | 9 +
> debugfs/Makefile.in | 2 +-
> lib/config.h.in | 3 +
> lib/ext2fs/Makefile.in | 2 +-
> misc/Makefile.in | 2 +-
> misc/create_inode.c | 621 ++++++++++++++++++++++++++++++++-
> misc/mke2fs.8.in | 10 +-
> misc/mke2fs.c | 12 +-
> tests/m_rootgnutar/expect | 188 ++++++++++
> tests/m_rootgnutar/output.sed | 5 +
> tests/m_rootgnutar/script | 88 +++++
> tests/m_rootpaxtar/expect | 87 +++++
> tests/m_rootpaxtar/mkpaxtar.pl | 69 ++++
> tests/m_rootpaxtar/output.sed | 5 +
> tests/m_rootpaxtar/script | 44 +++
> tests/m_roottar/expect | 208 +++++++++++
> tests/m_roottar/mktar.pl | 62 ++++
> tests/m_roottar/output.sed | 5 +
> tests/m_roottar/script | 57 +++
> 21 files changed, 1513 insertions(+), 19 deletions(-)
> create mode 100644 tests/m_rootgnutar/expect
> create mode 100644 tests/m_rootgnutar/output.sed
> create mode 100644 tests/m_rootgnutar/script
> create mode 100644 tests/m_rootpaxtar/expect
> create mode 100644 tests/m_rootpaxtar/mkpaxtar.pl
> create mode 100644 tests/m_rootpaxtar/output.sed
> create mode 100644 tests/m_rootpaxtar/script
> create mode 100644 tests/m_roottar/expect
> create mode 100644 tests/m_roottar/mktar.pl
> create mode 100644 tests/m_roottar/output.sed
> create mode 100644 tests/m_roottar/script
>
> diff --git a/MCONFIG.in b/MCONFIG.in
> index 82c75a28..cb3ec759 100644
> --- a/MCONFIG.in
> +++ b/MCONFIG.in
> @@ -141,6 +141,7 @@ LIBFUSE = @FUSE_LIB@
> LIBSUPPORT = $(LIBINTL) $(LIB)/libsupport@...TIC_LIB_EXT@
> LIBBLKID = @LIBBLKID@ @PRIVATE_LIBS_CMT@ $(LIBUUID)
> LIBINTL = @LIBINTL@
> +LIBARCHIVE = @ARCHIVE_LIB@
> SYSLIBS = @LIBS@ @PTHREAD_LIBS@
> DEPLIBSS = $(LIB)/libss@..._EXT@
> DEPLIBCOM_ERR = $(LIB)/libcom_err@..._EXT@
> diff --git a/configure b/configure
> index 72c39b4d..d0e3410b 100755
> --- a/configure
> +++ b/configure
> @@ -704,6 +704,7 @@ SEM_INIT_LIB
> FUSE_CMT
> FUSE_LIB
> CLOCK_GETTIME_LIB
> +ARCHIVE_LIB
> MAGIC_LIB
> SOCKET_LIB
> SIZEOF_TIME_T
> @@ -13539,6 +13540,57 @@ if test "$ac_cv_func_dlopen" = yes ; then
> MAGIC_LIB=$DLOPEN_LIB
> fi
>
> +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for archive_read_new in -larchive" >&5
> +printf %s "checking for archive_read_new in -larchive... " >&6; }
> +if test ${ac_cv_lib_archive_archive_read_new+y}
> +then :
> + printf %s "(cached) " >&6
> +else $as_nop
> + ac_check_lib_save_LIBS=$LIBS
> +LIBS="-larchive $LIBS"
> +cat confdefs.h - <<_ACEOF >conftest.$ac_ext
> +/* end confdefs.h. */
> +
> +/* Override any GCC internal prototype to avoid an error.
> + Use char because int might match the return type of a GCC
> + builtin and then its argument prototype would still apply. */
> +char archive_read_new ();
> +int
> +main (void)
> +{
> +return archive_read_new ();
> + ;
> + return 0;
> +}
> +_ACEOF
> +if ac_fn_c_try_link "$LINENO"
> +then :
> + ac_cv_lib_archive_archive_read_new=yes
> +else $as_nop
> + ac_cv_lib_archive_archive_read_new=no
> +fi
> +rm -f core conftest.err conftest.$ac_objext conftest.beam \
> + conftest$ac_exeext conftest.$ac_ext
> +LIBS=$ac_check_lib_save_LIBS
> +fi
> +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_archive_archive_read_new" >&5
> +printf "%s\n" "$ac_cv_lib_archive_archive_read_new" >&6; }
> +if test "x$ac_cv_lib_archive_archive_read_new" = xyes
> +then :
> + ARCHIVE_LIB=-larchive
> +ac_fn_c_check_header_compile "$LINENO" "archive.h" "ac_cv_header_archive_h" "$ac_includes_default"
> +if test "x$ac_cv_header_archive_h" = xyes
> +then :
> + printf "%s\n" "#define HAVE_ARCHIVE_H 1" >>confdefs.h
> +
> +fi
> +
> +fi
> +
> +if test "$ac_cv_func_dlopen" = yes ; then
> + ARCHIVE_LIB=$DLOPEN_LIB
> +fi
> +
> { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lrt" >&5
> printf %s "checking for clock_gettime in -lrt... " >&6; }
> if test ${ac_cv_lib_rt_clock_gettime+y}
> diff --git a/configure.ac b/configure.ac
> index b905e999..69fbbd27 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -1296,6 +1296,15 @@ if test "$ac_cv_func_dlopen" = yes ; then
> fi
> AC_SUBST(MAGIC_LIB)
> dnl
> +dnl libarchive
> +dnl
> +AC_CHECK_LIB(archive, archive_read_new, [ARCHIVE_LIB=-larchive
> +AC_CHECK_HEADERS([archive.h])])
> +if test "$ac_cv_func_dlopen" = yes ; then
> + ARCHIVE_LIB=$DLOPEN_LIB
> +fi
> +AC_SUBST(ARCHIVE_LIB)
> +dnl
> dnl Check to see if librt is required for clock_gettime() (glibc < 2.17)
> dnl
> AC_CHECK_LIB(rt, clock_gettime, [CLOCK_GETTIME_LIB=-lrt])
> diff --git a/debugfs/Makefile.in b/debugfs/Makefile.in
> index 67f8d0b6..8162df33 100644
> --- a/debugfs/Makefile.in
> +++ b/debugfs/Makefile.in
> @@ -36,7 +36,7 @@ SRCS= debug_cmds.c $(srcdir)/debugfs.c $(srcdir)/util.c $(srcdir)/ls.c \
> $(srcdir)/../e2fsck/recovery.c $(srcdir)/do_journal.c
>
> LIBS= $(LIBSUPPORT) $(LIBEXT2FS) $(LIBE2P) $(LIBSS) $(LIBCOM_ERR) $(LIBBLKID) \
> - $(LIBUUID) $(LIBMAGIC) $(SYSLIBS)
> + $(LIBUUID) $(LIBMAGIC) $(SYSLIBS) $(LIBARCHIVE)
> DEPLIBS= $(DEPLIBSUPPORT) $(LIBEXT2FS) $(LIBE2P) $(DEPLIBSS) $(DEPLIBCOM_ERR) \
> $(DEPLIBBLKID) $(DEPLIBUUID)
>
> diff --git a/lib/config.h.in b/lib/config.h.in
> index 076c3823..30477698 100644
> --- a/lib/config.h.in
> +++ b/lib/config.h.in
> @@ -40,6 +40,9 @@
> /* Define to 1 if you have the `add_key' function. */
> #undef HAVE_ADD_KEY
>
> +/* Define to 1 if you have the <archive.h> header file. */
> +#undef HAVE_ARCHIVE_H
> +
> /* Define to 1 if you have the <attr/xattr.h> header file. */
> #undef HAVE_ATTR_XATTR_H
>
> diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
> index 798ff609..e365793a 100644
> --- a/lib/ext2fs/Makefile.in
> +++ b/lib/ext2fs/Makefile.in
> @@ -499,7 +499,7 @@ tst_libext2fs: $(DEBUG_OBJS) \
> $(Q) $(CC) -o tst_libext2fs $(ALL_LDFLAGS) -DDEBUG $(DEBUG_OBJS) \
> $(STATIC_LIBSS) $(STATIC_LIBE2P) $(LIBSUPPORT) \
> $(STATIC_LIBEXT2FS) $(LIBBLKID) $(LIBUUID) $(LIBMAGIC) \
> - $(STATIC_LIBCOM_ERR) $(SYSLIBS) -I $(top_srcdir)/debugfs
> + $(STATIC_LIBCOM_ERR) $(SYSLIBS) $(LIBARCHIVE) -I $(top_srcdir)/debugfs
>
> tst_inline: $(srcdir)/inline.c $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
> $(E) " LD $@"
> diff --git a/misc/Makefile.in b/misc/Makefile.in
> index e5420bbd..c42d1f45 100644
> --- a/misc/Makefile.in
> +++ b/misc/Makefile.in
> @@ -281,7 +281,7 @@ mke2fs: $(MKE2FS_OBJS) $(DEPLIBS) $(LIBE2P) $(DEPLIBBLKID) $(DEPLIBUUID) \
> $(E) " LD $@"
> $(Q) $(CC) $(ALL_LDFLAGS) -o mke2fs $(MKE2FS_OBJS) $(LIBS) $(LIBBLKID) \
> $(LIBUUID) $(LIBEXT2FS) $(LIBE2P) $(LIBINTL) \
> - $(SYSLIBS) $(LIBMAGIC)
> + $(SYSLIBS) $(LIBMAGIC) $(LIBARCHIVE)
>
> mke2fs.static: $(MKE2FS_OBJS) $(STATIC_DEPLIBS) $(STATIC_LIBE2P) $(DEPSTATIC_LIBUUID) \
> $(DEPSTATIC_LIBBLKID)
> diff --git a/misc/create_inode.c b/misc/create_inode.c
> index a3a34cd9..0c9009a5 100644
> --- a/misc/create_inode.c
> +++ b/misc/create_inode.c
> @@ -39,6 +39,164 @@
> #include "create_inode.h"
> #include "support/nls-enable.h"
>
> +#ifdef HAVE_ARCHIVE_H
> +#include <locale.h>
> +#include <libgen.h>
> +#include <archive.h>
> +#include <archive_entry.h>
> +
> +static const char *(*dl_archive_entry_hardlink)(struct archive_entry *);
> +static const char *(*dl_archive_entry_pathname)(struct archive_entry *);
> +static const struct stat *(*dl_archive_entry_stat)(struct archive_entry *);
> +static const char *(*dl_archive_entry_symlink)(struct archive_entry *);
> +static int (*dl_archive_entry_xattr_count)(struct archive_entry *);
> +static int (*dl_archive_entry_xattr_next)(struct archive_entry *, const char **, const void **, size_t *);
> +static int (*dl_archive_entry_xattr_reset)(struct archive_entry *);
> +static const char *(*dl_archive_error_string)(struct archive *);
> +static int (*dl_archive_read_close)(struct archive *);
> +static la_ssize_t (*dl_archive_read_data)(struct archive *, void *, size_t);
> +static int (*dl_archive_read_free)(struct archive *);
> +static struct archive *(*dl_archive_read_new)(void);
> +static int (*dl_archive_read_next_header)(struct archive *, struct archive_entry **);
> +static int (*dl_archive_read_open_filename)(struct archive *, const char *filename, size_t);
> +static int (*dl_archive_read_support_filter_all)(struct archive *);
> +static int (*dl_archive_read_support_format_all)(struct archive *);
> +
> +#ifdef HAVE_DLOPEN
> +#include <dlfcn.h>
> +
> +static void *libarchive_handle;
> +
> +static int libarchive_available(void) {
> + if (!libarchive_handle) {
> + libarchive_handle = dlopen("libarchive.so.13", RTLD_NOW);
> + if (!libarchive_handle)
> + return 0;
> +
> + dl_archive_entry_hardlink =
> + (const char *(*)(struct archive_entry *))
> + dlsym(libarchive_handle, "archive_entry_hardlink");
> + if (!dl_archive_entry_hardlink) {
> + return 0;
> + }
> + dl_archive_entry_pathname =
> + (const char *(*)(struct archive_entry *))
> + dlsym(libarchive_handle, "archive_entry_pathname");
> + if (!dl_archive_entry_pathname) {
> + return 0;
> + }
> + dl_archive_entry_stat =
> + (const struct stat *(*)(struct archive_entry *))
> + dlsym(libarchive_handle, "archive_entry_stat");
> + if (!dl_archive_entry_stat) {
> + return 0;
> + }
> + dl_archive_entry_symlink =
> + (const char *(*)(struct archive_entry *))
> + dlsym(libarchive_handle, "archive_entry_symlink");
> + if (!dl_archive_entry_symlink) {
> + return 0;
> + }
> + dl_archive_entry_xattr_count =
> + (int (*)(struct archive_entry *))
> + dlsym(libarchive_handle, "archive_entry_xattr_count");
> + if (!dl_archive_entry_xattr_count) {
> + return 0;
> + }
> + dl_archive_entry_xattr_next =
> + (int (*)(struct archive_entry *, const char **, const void **, size_t *))
> + dlsym(libarchive_handle, "archive_entry_xattr_next");
> + if (!dl_archive_entry_xattr_next) {
> + return 0;
> + }
> + dl_archive_entry_xattr_reset =
> + (int (*)(struct archive_entry *))
> + dlsym(libarchive_handle, "archive_entry_xattr_reset");
> + if (!dl_archive_entry_xattr_reset) {
> + return 0;
> + }
> + dl_archive_error_string =
> + (const char *(*)(struct archive *))
> + dlsym(libarchive_handle, "archive_error_string");
> + if (!dl_archive_error_string) {
> + return 0;
> + }
> + dl_archive_read_close =
> + (int (*)(struct archive *))
> + dlsym(libarchive_handle, "archive_read_close");
> + if (!dl_archive_read_close) {
> + return 0;
> + }
> + dl_archive_read_data =
> + (la_ssize_t (*)(struct archive *, void *, size_t))
> + dlsym(libarchive_handle, "archive_read_data");
> + if (!dl_archive_read_data) {
> + return 0;
> + }
> + dl_archive_read_free =
> + (int (*)(struct archive *))
> + dlsym(libarchive_handle, "archive_read_free");
> + if (!dl_archive_read_free) {
> + return 0;
> + }
> + dl_archive_read_new =
> + (struct archive *(*)(void))
> + dlsym(libarchive_handle, "archive_read_new");
> + if (!dl_archive_read_new) {
> + return 0;
> + }
> + dl_archive_read_next_header =
> + (int (*)(struct archive *, struct archive_entry **))
> + dlsym(libarchive_handle, "archive_read_next_header");
> + if (!dl_archive_read_next_header) {
> + return 0;
> + }
> + dl_archive_read_open_filename =
> + (int (*)(struct archive *, const char *filename, size_t))
> + dlsym(libarchive_handle, "archive_read_open_filename");
> + if (!dl_archive_read_open_filename) {
> + return 0;
> + }
> + dl_archive_read_support_filter_all =
> + (int (*)(struct archive *))
> + dlsym(libarchive_handle, "archive_read_support_filter_all");
> + if (!dl_archive_read_support_filter_all) {
> + return 0;
> + }
> + dl_archive_read_support_format_all =
> + (int (*)(struct archive *))
> + dlsym(libarchive_handle, "archive_read_support_format_all");
> + if (!dl_archive_read_support_format_all) {
> + return 0;
> + }
> + }
> +
> + return 1;
> +}
> +#elif
> +static int libarchive_available(void) {
> + dl_archive_entry_hardlink = archive_entry_hardlink;
> + dl_archive_entry_pathname = archive_entry_pathname;
> + dl_archive_entry_stat = archive_entry_stat;
> + dl_archive_entry_symlink = archive_entry_symlink;
> + dl_archive_entry_xattr_count = archive_entry_xattr_count;
> + dl_archive_entry_xattr_next = archive_entry_xattr_next;
> + dl_archive_entry_xattr_reset = archive_entry_xattr_reset;
> + dl_archive_error_string = archive_error_string;
> + dl_archive_read_close = archive_read_close;
> + dl_archive_read_data = archive_read_data;
> + dl_archive_read_free = archive_read_free;
> + dl_archive_read_new = archive_read_new;
> + dl_archive_read_next_header = archive_read_next_header;
> + dl_archive_read_open_filename = archive_read_open_filename;
> + dl_archive_read_support_filter_all = archive_read_support_filter_all;
> + dl_archive_read_support_format_all = archive_read_support_format_all;
> +
> + return 1;
> +}
> +#endif
> +#endif
> +
> /* 64KiB is the minimum blksize to best minimize system call overhead. */
> #define COPY_FILE_BUFLEN 65536
>
> @@ -109,7 +267,7 @@ static errcode_t add_link(ext2_filsys fs, ext2_ino_t parent_ino,
>
> /* Set the uid, gid, mode and time for the inode */
> static errcode_t set_inode_extra(ext2_filsys fs, ext2_ino_t ino,
> - struct stat *st)
> + const struct stat *st)
> {
> errcode_t retval;
> struct ext2_inode inode;
> @@ -1043,8 +1201,429 @@ out:
> return retval;
> }
>
> +#ifdef HAVE_ARCHIVE_H
> +static errcode_t __find_path(ext2_filsys fs, ext2_ino_t root, const char *name,
> + ext2_ino_t *inode) {
> + errcode_t retval;
> + ext2_ino_t tmpino;
> + char *p, *n, *n2 = strdup(name);
> + if (n2 == NULL) {
> + retval = errno;
> + goto out;
> + }
> + n = n2;
> + *inode = root;
> + // any number of leading slashes
> + while(*n == '/') {
> + n++;
> + }
> + while(*n) {
> + // replace the next slash by a NULL, if any
> + if((p = strchr(n, '/')))
> + (*p) = 0;
> + // find the inode of the next component
> + retval = ext2fs_lookup(fs, *inode, n, strlen(n), 0, &tmpino);
> + if (retval) {
> + goto out;
> + }
> + *inode = tmpino;
> + // continue the search at the character after the slash
> + if(p)
> + n = p + 1;
> + else
> + break;
> + }
> +
> +out:
> + free(n2);
> + return retval;
> +}
> +
> +/* Rounds qty upto a multiple of siz. siz should be a power of 2 */
> +static inline unsigned int __rndup(unsigned int qty, unsigned int siz) {
> + return (qty + (siz - 1)) & ~(siz - 1);
> +}
> +
> +static errcode_t copy_file_chunk_tar(ext2_filsys fs, struct archive *archive, ext2_file_t e2_file,
> + off_t start, off_t end, char *buf,
> + char *zerobuf)
> +{
> + off_t off, bpos;
> + ssize_t got, blen;
> + unsigned int written;
> + char *ptr;
> + errcode_t err = 0;
> +
> + for (off = start; off < end; off += COPY_FILE_BUFLEN) {
> + got = dl_archive_read_data(archive, buf, COPY_FILE_BUFLEN);
> + if (got < 0) {
> + err = errno;
> + goto fail;
> + }
> + for (bpos = 0, ptr = buf; bpos < got; bpos += fs->blocksize) {
> + blen = fs->blocksize;
> + if (blen > got - bpos)
> + blen = got - bpos;
> + if (memcmp(ptr, zerobuf, blen) == 0) {
> + ptr += blen;
> + continue;
> + }
> + err = ext2fs_file_llseek(e2_file, off + bpos,
> + EXT2_SEEK_SET, NULL);
> + if (err)
> + goto fail;
> + while (blen > 0) {
> + err = ext2fs_file_write(e2_file, ptr, blen,
> + &written);
> + if (err)
> + goto fail;
> + if (written == 0) {
> + err = EIO;
> + goto fail;
> + }
> + blen -= written;
> + ptr += written;
> + }
> + }
> + }
> +fail:
> + return err;
> +}
> +static errcode_t copy_file_tar(ext2_filsys fs, struct archive *archive, const struct stat *statbuf,
> + ext2_ino_t ino)
> +{
> + ext2_file_t e2_file;
> + char *buf = NULL, *zerobuf = NULL;
> + errcode_t err, close_err;
> +
> + err = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &e2_file);
> + if (err)
> + return err;
> +
> + err = ext2fs_get_mem(COPY_FILE_BUFLEN, &buf);
> + if (err)
> + goto out;
> +
> + err = ext2fs_get_memzero(fs->blocksize, &zerobuf);
> + if (err)
> + goto out;
> +
> + err = copy_file_chunk_tar(fs, archive, e2_file, 0, statbuf->st_size, buf,
> + zerobuf);
> +out:
> + ext2fs_free_mem(&zerobuf);
> + ext2fs_free_mem(&buf);
> + close_err = ext2fs_file_close(e2_file);
> + if (err == 0)
> + err = close_err;
> + return err;
> +}
> +
> +
> +errcode_t do_write_internal_tar(ext2_filsys fs, ext2_ino_t cwd, struct archive *archive,
> + const char *dest, const struct stat *statbuf)
> +{
> + ext2_ino_t newfile;
> + errcode_t retval;
> + struct ext2_inode inode;
> + char *cp;
> + retval = ext2fs_new_inode(fs, cwd, 010755, 0, &newfile);
> + if (retval)
> + goto out;
> +#ifdef DEBUGFS
> + printf("Allocated inode: %u\n", newfile);
> +#endif
> + retval = ext2fs_link(fs, cwd, dest, newfile, EXT2_FT_REG_FILE);
> + if (retval == EXT2_ET_DIR_NO_SPACE) {
> + retval = ext2fs_expand_dir(fs, cwd);
> + if (retval)
> + goto out;
> + retval = ext2fs_link(fs, cwd, dest, newfile,
> + EXT2_FT_REG_FILE);
> + }
> + if (retval)
> + goto out;
> + if (ext2fs_test_inode_bitmap2(fs->inode_map, newfile))
> + com_err(__func__, 0, "Warning: inode already set");
> + ext2fs_inode_alloc_stats2(fs, newfile, +1, 0);
> + memset(&inode, 0, sizeof(inode));
> + inode.i_mode = (statbuf->st_mode & ~S_IFMT) | LINUX_S_IFREG;
> + inode.i_atime = inode.i_ctime = inode.i_mtime =
> + fs->now ? fs->now : time(0);
> + inode.i_links_count = 1;
> + retval = ext2fs_inode_size_set(fs, &inode, statbuf->st_size);
> + if (retval)
> + goto out;
> + if (ext2fs_has_feature_inline_data(fs->super)) {
> + inode.i_flags |= EXT4_INLINE_DATA_FL;
> + } else if (ext2fs_has_feature_extents(fs->super)) {
> + ext2_extent_handle_t handle;
> +
> + inode.i_flags &= ~EXT4_EXTENTS_FL;
> + retval = ext2fs_extent_open2(fs, newfile, &inode, &handle);
> + if (retval)
> + goto out;
> + ext2fs_extent_free(handle);
> + }
> +
> + retval = ext2fs_write_new_inode(fs, newfile, &inode);
> + if (retval)
> + goto out;
> + if (inode.i_flags & EXT4_INLINE_DATA_FL) {
> + retval = ext2fs_inline_data_init(fs, newfile);
> + if (retval)
> + goto out;
> + }
> + if (LINUX_S_ISREG(inode.i_mode)) {
> + retval = copy_file_tar(fs, archive, statbuf, newfile);
> + if (retval)
> + goto out;
> + }
> +out:
> + return retval;
> +}
> +
> +static errcode_t set_inode_xattr_tar(ext2_filsys fs, ext2_ino_t ino,
> + struct archive_entry *entry)
> +{
> + errcode_t retval, close_retval;
> + struct ext2_xattr_handle *handle;
> + ssize_t size;
> + const char *name;
> + const void *value;
> + size_t value_size;
> +
> + if (no_copy_xattrs)
> + return 0;
> +
> + size = dl_archive_entry_xattr_count(entry);
> + if (size == 0) {
> + return 0;
> + }
> +
> + retval = ext2fs_xattrs_open(fs, ino, &handle);
> + if (retval) {
> + if (retval == EXT2_ET_MISSING_EA_FEATURE)
> + return 0;
> + com_err(__func__, retval, _("while opening inode %u"), ino);
> + return retval;
> + }
> +
> + retval = ext2fs_xattrs_read(handle);
> + if (retval) {
> + com_err(__func__, retval,
> + _("while reading xattrs for inode %u"), ino);
> + goto out;
> + }
> +
> + dl_archive_entry_xattr_reset(entry);
> + while (dl_archive_entry_xattr_next(entry, &name, &value, &value_size) == ARCHIVE_OK) {
> + if (strcmp(name, "security.capability") != 0) {
> + continue;
> + }
> +
> + retval = ext2fs_xattr_set(handle, name, value, value_size);
> + if (retval) {
> + com_err(__func__, retval,
> + _("while writing attribute \"%s\" to inode %u"),
> + name, ino);
> + break;
> + }
> +
> + }
> + out:
> + close_retval = ext2fs_xattrs_close(&handle);
> + if (close_retval) {
> + com_err(__func__, retval, _("while closing inode %u"), ino);
> + retval = retval ? retval : close_retval;
> + }
> + return retval;
> +}
> +
> +static errcode_t __populate_fs_from_tar(ext2_filsys fs, ext2_ino_t root_ino,
> + const char *source_tar, ext2_ino_t root,
> + struct hdlinks_s *hdlinks,
> + struct file_info *target,
> + struct fs_ops_callbacks *fs_callbacks)
> +{
> + char *path2, *path3, *dir, *name, *ln_target;
> + unsigned int uid, gid, mode;
> + unsigned long ctime, mtime;
> + struct archive *a;
> + struct archive_entry *entry;
> + errcode_t retval = 0;
> + locale_t archive_locale;
> + locale_t old_locale;
> + ext2_ino_t dirinode, tmpino;
> + const struct stat *st;
> +
> + if (!libarchive_available()) {
> + com_err(__func__, 0, _("you need libarchive to be able to process tarballs"));
> + return 1;
> + }
> +
> + archive_locale = newlocale(LC_CTYPE_MASK, "", (locale_t)0);
> + old_locale = uselocale(archive_locale);
> + a = dl_archive_read_new();
> + if (a == NULL) {
> + retval = 1;
> + com_err(__func__, retval, _("while creating archive reader"));
> + goto out;
> + }
> + if (dl_archive_read_support_filter_all(a) != ARCHIVE_OK) {
> + retval = 1;
> + com_err(__func__, retval, _("while enabling decompression"));
> + goto out;
> + }
> + if (dl_archive_read_support_format_all(a) != ARCHIVE_OK) {
> + retval = 1;
> + com_err(__func__, retval, _("while enabling reader formats"));
> + goto out;
> + }
> +
> + if ((retval = dl_archive_read_open_filename(a, source_tar, 4096))) {
> + com_err(__func__, retval, _("while opening \"%s\""), dl_archive_error_string(a));
> + goto out;
> + }
> +
> + for (;;) {
> + retval = dl_archive_read_next_header(a, &entry);
> + if (retval == ARCHIVE_EOF) {
> + retval = 0;
> + break;
> + }
> + if (retval != ARCHIVE_OK) {
> + com_err(__func__, retval, _("cannot read archive header: \"%s\""),
> + dl_archive_error_string(a));
> + goto out;
> + }
> + path2 = strdup(dl_archive_entry_pathname(entry));
> + path3 = strdup(dl_archive_entry_pathname(entry));
> + name = basename(path2);
> + dir = dirname(path3);
> + if(retval = __find_path(fs, root_ino, dir, &dirinode)) {
> + com_err(__func__, retval,
> + _("cannot find directory \"%s\" to create \"%s\""), dir, name);
> + goto out;
> + }
> + st = dl_archive_entry_stat(entry);
> + retval = ext2fs_namei(fs, root, dirinode, name, &tmpino);
> + if (!retval) {
> + // repeated element in tar
> + // FIXME: if regular file, update content
> + } else {
> + // no special casing for sockets and symlinks on windows is needed
> + // because even on windows we can have tarballs containing those files
> + switch(st->st_mode & S_IFMT)
> + {
> + case S_IFCHR:
> + case S_IFBLK:
> + case S_IFIFO:
> + case S_IFSOCK:
> + retval = do_mknod_internal(fs, dirinode, name,
> + st->st_mode, st->st_rdev);
> + if (retval) {
> + com_err(__func__, retval,
> + _("while creating special file "
> + "\"%s\""), name);
> + goto out;
> + }
> + break;
> + case S_IFLNK:
> + ln_target = calloc(1, __rndup(strlen(dl_archive_entry_symlink(entry)), 1024));
> + strcpy(ln_target, dl_archive_entry_symlink(entry));
> + retval = do_symlink_internal(fs, dirinode, name,
> + ln_target, root);
> + free(ln_target);
> + if (retval) {
> + com_err(__func__, retval,
> + _("while writing symlink\"%s\""),
> + name);
> + goto out;
> + }
> + break;
> + case S_IFREG:
> + retval = do_write_internal_tar(fs, dirinode, a, name, st);
> + if (retval) {
> + com_err(__func__, retval,
> + _("while writing file \"%s\""), name);
> + goto out;
> + }
> + break;
> + case S_IFDIR:
> + retval = do_mkdir_internal(fs, dirinode, name, root);
> + if (retval) {
> + com_err(__func__, retval,
> + _("while making dir \"%s\""), name);
> + goto out;
> + }
> + break;
> + default:
> + if (dl_archive_entry_hardlink(entry) != NULL) {
> + if(retval = __find_path(fs, root_ino, dl_archive_entry_hardlink(entry), &tmpino)) {
> + com_err(__func__, retval,
> + _("cannot find hardlink destination \"%s\" to create \"%s\""), dl_archive_entry_hardlink(entry), name);
> + goto out;
> + }
> + retval = add_link(fs, dirinode,
> + tmpino,
> + name);
> + if (retval) {
> + com_err(__func__, retval,
> + "while linking %s", name);
> + goto out;
> + }
> + } else {
> + com_err(__func__, 0,
> + _("ignoring entry \"%s\""), dl_archive_entry_pathname(entry));
> + }
> + }
> +
> + retval = ext2fs_namei(fs, root, dirinode, name, &tmpino);
> + if (retval) {
> + com_err(name, retval, _("while looking up \"%s\""),
> + name);
> + goto out;
> + }
> + }
> +
> + retval = set_inode_extra(fs, tmpino, st);
> + if (retval) {
> + com_err(__func__, retval,
> + _("while setting inode for \"%s\""), name);
> + goto out;
> + }
> +
> + retval = set_inode_xattr_tar(fs, tmpino, entry);
> + if (retval) {
> + com_err(__func__, retval,
> + _("while setting xattrs for \"%s\""), name);
> + goto out;
> + }
> +
> + if (fs_callbacks && fs_callbacks->end_create_new_inode) {
> + retval = fs_callbacks->end_create_new_inode(fs,
> + target->path, name, dirinode, root,
> + st->st_mode & S_IFMT);
> + if (retval)
> + goto out;
> + }
> +
> + free(path2);
> + free(path3);
> + }
> +
> +out:
> + dl_archive_read_close(a);
> + dl_archive_read_free(a);
> + uselocale(old_locale);
> + freelocale(archive_locale);
> + return retval;
> +}
> +#endif
> +
> errcode_t populate_fs2(ext2_filsys fs, ext2_ino_t parent_ino,
> - const char *source_dir, ext2_ino_t root,
> + const char *source, ext2_ino_t root,
> struct fs_ops_callbacks *fs_callbacks)
> {
> struct file_info file_info;
> @@ -1069,14 +1648,44 @@ errcode_t populate_fs2(ext2_filsys fs, ext2_ino_t parent_ino,
> file_info.path_max_len = 255;
> file_info.path = calloc(file_info.path_max_len, 1);
>
> - retval = set_inode_xattr(fs, root, source_dir);
> + /* interpret input as tarball either if it's "-" (stdin) or if it's
> + * a regular file (or a symlink pointing to a regular file) */
> + if (strcmp(source, "-") == 0) {
> +#ifdef HAVE_ARCHIVE_H
> + retval = __populate_fs_from_tar(fs, parent_ino, NULL, root, &hdlinks,
> + &file_info, fs_callbacks);
> +#else
> + com_err(__func__, 0, _("you need to compile e2fsprogs with libarchive to be able to process tarballs"));
> + retval = 1;
> +#endif
> + goto out;
> + } else {
> + struct stat st;
> + if (stat(source, &st)) {
> + retval = errno;
> + com_err(__func__, retval, _("while calling stat"));
> + return retval;
> + }
> + if (S_ISREG(st.st_mode)) {
> +#ifdef HAVE_ARCHIVE_H
> + retval = __populate_fs_from_tar(fs, parent_ino, source, root, &hdlinks,
> + &file_info, fs_callbacks);
> +#else
> + com_err(__func__, 0, _("you need to compile e2fsprogs with libarchive to be able to process tarballs"));
> + retval = 1;
> +#endif
> + goto out;
> + }
> + }
> +
> + retval = set_inode_xattr(fs, root, source);
> if (retval) {
> com_err(__func__, retval,
> _("while copying xattrs on root directory"));
> goto out;
> }
>
> - retval = __populate_fs(fs, parent_ino, source_dir, root, &hdlinks,
> + retval = __populate_fs(fs, parent_ino, source, root, &hdlinks,
> &file_info, fs_callbacks);
>
> out:
> @@ -1086,7 +1695,7 @@ out:
> }
>
> errcode_t populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
> - const char *source_dir, ext2_ino_t root)
> + const char *source, ext2_ino_t root)
> {
> - return populate_fs2(fs, parent_ino, source_dir, root, NULL);
> + return populate_fs2(fs, parent_ino, source, root, NULL);
> }
> diff --git a/misc/mke2fs.8.in b/misc/mke2fs.8.in
> index 30f97bb5..744e9b11 100644
> --- a/misc/mke2fs.8.in
> +++ b/misc/mke2fs.8.in
> @@ -23,7 +23,7 @@ mke2fs \- create an ext2/ext3/ext4 file system
> ]
> [
> .B \-d
> -.I root-directory
> +.I root-directory|tarball
> ]
> [
> .B \-D
> @@ -239,9 +239,11 @@ enabled. (See the
> man page for more details about bigalloc.) The default cluster size if
> bigalloc is enabled is 16 times the block size.
> .TP
> -.BI \-d " root-directory"
> -Copy the contents of the given directory into the root directory of the
> -file system.
> +.BI \-d " root-directory|tarball"
> +Copy the contents of the given directory or tarball into the root directory of the
> +file system. Tarball input is only available if mke2fs was compiled with
> +libarchive support enabled and if the libarchive shared library is available
> +at run-time. The special value "-" will read a tarball from standard input.
> .TP
> .B \-D
> Use direct I/O when writing to the disk. This avoids mke2fs dirtying a
> diff --git a/misc/mke2fs.c b/misc/mke2fs.c
> index 4a9c1b09..4ffa9748 100644
> --- a/misc/mke2fs.c
> +++ b/misc/mke2fs.c
> @@ -117,7 +117,7 @@ static char *mount_dir;
> char *journal_device;
> static int sync_kludge; /* Set using the MKE2FS_SYNC env. option */
> char **fs_types;
> -const char *src_root_dir; /* Copy files from the specified directory */
> +const char *src_root; /* Copy files from the specified directory or tarball */
> static char *undo_file;
>
> static int android_sparse_file; /* -E android_sparse */
> @@ -134,7 +134,7 @@ static void usage(void)
> "[-C cluster-size]\n\t[-i bytes-per-inode] [-I inode-size] "
> "[-J journal-options]\n"
> "\t[-G flex-group-size] [-N number-of-inodes] "
> - "[-d root-directory]\n"
> + "[-d root-directory|tarball]\n"
> "\t[-m reserved-blocks-percentage] [-o creator-os]\n"
> "\t[-g blocks-per-group] [-L volume-label] "
> "[-M last-mounted-directory]\n\t[-O feature[,...]] "
> @@ -1712,7 +1712,7 @@ profile_error:
> }
> break;
> case 'd':
> - src_root_dir = optarg;
> + src_root = optarg;
> break;
> case 'D':
> direct_io = 1;
> @@ -3551,12 +3551,12 @@ no_journal:
> retval = mk_hugefiles(fs, device_name);
> if (retval)
> com_err(program_name, retval, "while creating huge files");
> - /* Copy files from the specified directory */
> - if (src_root_dir) {
> + /* Copy files from the specified directory or tarball */
> + if (src_root) {
> if (!quiet)
> printf("%s", _("Copying files into the device: "));
>
> - retval = populate_fs(fs, EXT2_ROOT_INO, src_root_dir,
> + retval = populate_fs(fs, EXT2_ROOT_INO, src_root,
> EXT2_ROOT_INO);
> if (retval) {
> com_err(program_name, retval, "%s",
> diff --git a/tests/m_rootgnutar/expect b/tests/m_rootgnutar/expect
> new file mode 100644
> index 00000000..336e159e
> --- /dev/null
> +++ b/tests/m_rootgnutar/expect
> @@ -0,0 +1,188 @@
> +Filesystem volume name: <none>
> +Last mounted on: <not available>
> +Filesystem magic number: 0xEF53
> +Filesystem revision #: 1 (dynamic)
> +Filesystem features: has_journal ext_attr resize_inode dir_index filetype extent 64bit flex_bg sparse_super huge_file dir_nlink extra_isize metadata_csum
> +Default mount options: (none)
> +Filesystem state: clean
> +Errors behavior: Continue
> +Filesystem OS type: Linux
> +Inode count: 1024
> +Block count: 16384
> +Reserved block count: 819
> +Overhead clusters: 1543
> +Free blocks: 14791
> +Free inodes: 1005
> +First block: 1
> +Block size: 1024
> +Fragment size: 1024
> +Group descriptor size: 64
> +Reserved GDT blocks: 127
> +Blocks per group: 8192
> +Fragments per group: 8192
> +Inodes per group: 512
> +Inode blocks per group: 128
> +Flex block group size: 16
> +Mount count: 0
> +Check interval: 15552000 (6 months)
> +Reserved blocks uid: 0
> +Reserved blocks gid: 0
> +First inode: 11
> +Inode size: 256
> +Required extra isize: 32
> +Desired extra isize: 32
> +Journal inode: 8
> +Default directory hash: half_md4
> +Journal backup: inode blocks
> +Checksum type: crc32c
> +Journal features: (none)
> +Total journal size: 1024k
> +Total journal blocks: 1024
> +Max transaction length: 1024
> +Fast commit length: 0
> +Journal sequence: 0x00000001
> +Journal start: 0
> +
> +
> +Group 0: (Blocks 1-8192)
> + Primary superblock at 1, Group descriptors at 2-2
> + Reserved GDT blocks at 3-129
> + Block bitmap at 130 (+129)
> + Inode bitmap at 132 (+131)
> + Inode table at 134-261 (+133)
> + 7753 free blocks, 493 free inodes, 5 directories, 493 unused inodes
> + Free blocks: 440-8192
> + Free inodes: 20-512
> +Group 1: (Blocks 8193-16383) [INODE_UNINIT]
> + Backup superblock at 8193, Group descriptors at 8194-8194
> + Reserved GDT blocks at 8195-8321
> + Block bitmap at 131 (bg #0 + 130)
> + Inode bitmap at 133 (bg #0 + 132)
> + Inode table at 262-389 (bg #0 + 261)
> + 7038 free blocks, 512 free inodes, 0 directories, 512 unused inodes
> + Free blocks: 9346-16383
> + Free inodes: 513-1024
> +debugfs: stat /test/emptyfile
> +Inode: III Type: regular
> +Size: 0
> +Fragment: Address: 0 Number: 0 Size: 0
> +debugfs: stat /test/bigfile
> +Inode: III Type: regular
> +Size: 32768
> +Fragment: Address: 0 Number: 0 Size: 0
> +debugfs: stat /test/zerofile
> +Inode: III Type: regular
> +Size: 1025
> +Fragment: Address: 0 Number: 0 Size: 0
> +debugfs: stat /test/silly_bs_link
> +Inode: III Type: symlink
> +Size: 14
> +Fragment: Address: 0 Number: 0 Size: 0
> +debugfs: stat /test/emptydir
> +Inode: III Type: directory
> +Size: 1024
> +Fragment: Address: 0 Number: 0 Size: 0
> +debugfs: stat /test/dir
> +Inode: III Type: directory
> +Size: 1024
> +Fragment: Address: 0 Number: 0 Size: 0
> +debugfs: stat /test/dir/file
> +Inode: III Type: regular
> +Size: 8
> +Fragment: Address: 0 Number: 0 Size: 0
> +debugfs: ex /test/emptyfile
> +Level Entries Logical Physical Length Flags
> +debugfs: ex /test/bigfile
> +Level Entries Logical Physical Length Flags
> +X 0/0 1/1 0-31 AAA-BBB 32
> +debugfs: ex /test/zerofile
> +Level Entries Logical Physical Length Flags
> +debugfs: ex /test/silly_bs_link
> +/test/silly_bs_link: does not uses extent block maps
> +debugfs: ex /test/emptydir
> +Level Entries Logical Physical Length Flags
> +X 0/0 1/1 0-0 AAA-BBB 1
> +debugfs: ex /test/dir
> +Level Entries Logical Physical Length Flags
> +X 0/0 1/1 0-0 AAA-BBB 1
> +debugfs: ex /test/dir/file
> +Level Entries Logical Physical Length Flags
> +X 0/0 1/1 0-0 AAA-BBB 1
> +Filesystem volume name: <none>
> +Last mounted on: <not available>
> +Filesystem magic number: 0xEF53
> +Filesystem revision #: 1 (dynamic)
> +Filesystem features: has_journal ext_attr resize_inode dir_index filetype extent 64bit flex_bg sparse_super huge_file dir_nlink extra_isize metadata_csum
> +Default mount options: (none)
> +Filesystem state: clean
> +Errors behavior: Continue
> +Filesystem OS type: Linux
> +Inode count: 1024
> +Block count: 16384
> +Reserved block count: 819
> +Overhead clusters: 1543
> +Free blocks: 14791
> +Free inodes: 1005
> +First block: 1
> +Block size: 1024
> +Fragment size: 1024
> +Group descriptor size: 64
> +Reserved GDT blocks: 127
> +Blocks per group: 8192
> +Fragments per group: 8192
> +Inodes per group: 512
> +Inode blocks per group: 128
> +Flex block group size: 16
> +Mount count: 0
> +Check interval: 15552000 (6 months)
> +Reserved blocks uid: 0
> +Reserved blocks gid: 0
> +First inode: 11
> +Inode size: 256
> +Required extra isize: 32
> +Desired extra isize: 32
> +Journal inode: 8
> +Default directory hash: half_md4
> +Journal backup: inode blocks
> +Checksum type: crc32c
> +Journal features: (none)
> +Total journal size: 1024k
> +Total journal blocks: 1024
> +Max transaction length: 1024
> +Fast commit length: 0
> +Journal sequence: 0x00000001
> +Journal start: 0
> +
> +
> +Group 0: (Blocks 1-8192)
> + Primary superblock at 1, Group descriptors at 2-2
> + Reserved GDT blocks at 3-129
> + Block bitmap at 130 (+129)
> + Inode bitmap at 132 (+131)
> + Inode table at 134-261 (+133)
> + 7753 free blocks, 493 free inodes, 5 directories, 493 unused inodes
> + Free blocks: 440-8192
> + Free inodes: 20-512
> +Group 1: (Blocks 8193-16383) [INODE_UNINIT]
> + Backup superblock at 8193, Group descriptors at 8194-8194
> + Reserved GDT blocks at 8195-8321
> + Block bitmap at 131 (bg #0 + 130)
> + Inode bitmap at 133 (bg #0 + 132)
> + Inode table at 262-389 (bg #0 + 261)
> + 7038 free blocks, 512 free inodes, 0 directories, 512 unused inodes
> + Free blocks: 9346-16383
> + Free inodes: 513-1024
> +test/
> +test/bigfile
> +test/dir/
> +test/dir/file
> +test/emptydir/
> +test/emptyfile
> +test/silly_bs_link
> +test/zerofile
> +Pass 1: Checking inodes, blocks, and sizes
> +Pass 2: Checking directory structure
> +Pass 3: Checking directory connectivity
> +Pass 4: Checking reference counts
> +Pass 5: Checking group summary information
> +test.img: 19/1024 files (0.0% non-contiguous), 1593/16384 blocks
> diff --git a/tests/m_rootgnutar/output.sed b/tests/m_rootgnutar/output.sed
> new file mode 100644
> index 00000000..2e769678
> --- /dev/null
> +++ b/tests/m_rootgnutar/output.sed
> @@ -0,0 +1,5 @@
> +s/^[[:space:]]*\([0-9]*\)\/[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*\)\/[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*\)[[:space:]]*-[[:space:]]*\([0-9]*\)[[:space:]]*[0-9]*[[:space:]]*-[[:space:]]*[0-9]*[[:space:]]*\([0-9]*\)/X \1\/\2 \3\/\4 \5-\6 AAA-BBB \7/g
> +s/^[[:space:]]*\([0-9]*\)\/[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*\)\/[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*\)[[:space:]]*-[[:space:]]*\([0-9]*\)[[:space:]]*[0-9]*[[:space:]]*\([0-9]*\)/Y \1\/\2 \3\/\4 \5-\6 AAA \7/g
> +s/Mode:.*$//g
> +s/User:.*Size:/Size:/g
> +s/^Inode: [0-9]*/Inode: III/g
> diff --git a/tests/m_rootgnutar/script b/tests/m_rootgnutar/script
> new file mode 100644
> index 00000000..949d9b5f
> --- /dev/null
> +++ b/tests/m_rootgnutar/script
> @@ -0,0 +1,88 @@
> +# vim: filetype=sh
> +
> +test_description="create fs image from GNU tarball"
> +if ! test -x $DEBUGFS_EXE; then
> + echo "$test_name: $test_description: skipped (no debugfs)"
> + return 0
> +fi
> +if [ "$(grep -c 'define HAVE_ARCHIVE_H' ../lib/config.h)" -eq 0 ]; then
> + echo "$test_name: skipped (no libarchive)"
> + exit 0
> +fi
> +
> +MKFS_TAR=$TMPFILE.tar
> +MKFS_DIR=$TMPFILE.dir
> +OUT=$test_name.log
> +EXP=$test_dir/expect
> +
> +# we put everything in a subdir because we cannot rdump the root as that would
> +# require permissions to changing ownership of /lost+found
> +rm -rf $MKFS_DIR
> +mkdir -p $MKFS_DIR/test
> +touch $MKFS_DIR/test/emptyfile
> +dd if=/dev/zero bs=1024 count=32 2> /dev/null | tr '\0' 'a' > $MKFS_DIR/test/bigfile
> +dd if=/dev/zero of=$MKFS_DIR/test/zerofile bs=1 count=1 seek=1024 2> /dev/null
> +ln -s /silly_bs_link $MKFS_DIR/test/silly_bs_link
> +mkdir $MKFS_DIR/test/emptydir
> +mkdir $MKFS_DIR/test/dir
> +echo "Test me" > $MKFS_DIR/test/dir/file
> +
> +tar --sort=name -C $MKFS_DIR --format=gnu -cf $MKFS_TAR test
> +rm -r $MKFS_DIR
> +
> +$MKE2FS -q -F -o Linux -T ext4 -O metadata_csum,64bit -E lazy_itable_init=1 -b 1024 -d $MKFS_TAR $TMPFILE 16384 > $OUT 2>&1
> +
> +$DUMPE2FS $TMPFILE >> $OUT 2>&1
> +cat > $TMPFILE.cmd << ENDL
> +stat /test/emptyfile
> +stat /test/bigfile
> +stat /test/zerofile
> +stat /test/silly_bs_link
> +stat /test/emptydir
> +stat /test/dir
> +stat /test/dir/file
> +ENDL
> +$DEBUGFS -f $TMPFILE.cmd $TMPFILE 2>&1 | egrep "(stat|Size:|Type:)" >> $OUT
> +
> +cat > $TMPFILE.cmd << ENDL
> +ex /test/emptyfile
> +ex /test/bigfile
> +ex /test/zerofile
> +ex /test/silly_bs_link
> +ex /test/emptydir
> +ex /test/dir
> +ex /test/dir/file
> +ENDL
> +$DEBUGFS -f $TMPFILE.cmd $TMPFILE >> $OUT 2>&1
> +
> +$DUMPE2FS $TMPFILE >> $OUT 2>&1
> +
> +$DEBUGFS -R "dump /test/dir/file $TMPFILE.testme" $TMPFILE >> $OUT 2>&1
> +
> +# extract the files and directories from the image and tar them again to make
> +# sure that a tarball from the image contents is bit-by-bit identical to the
> +# tarball the image was created from -- essentially this checks whether a
> +# roundtrip from tar to ext4 to tar remains identical
> +mkdir "$MKFS_DIR"
> +$DEBUGFS -R "rdump /test $MKFS_DIR" $TMPFILE >> $OUT 2>&1
> +tar --sort=name -C $MKFS_DIR --format=gnu -cvf $TMPFILE.new.tar test >> $OUT 2>&1
> +
> +$FSCK -f -n $TMPFILE >> $OUT 2>&1
> +
> +sed -f $cmd_dir/filter.sed -f $test_dir/output.sed -e "s;$TMPFILE;test.img;" < $OUT > $OUT.tmp
> +mv $OUT.tmp $OUT
> +
> +# Do the verification
> +cmp -s $OUT $EXP && echo "Test me" | cmp -s - $TMPFILE.testme && cmp $MKFS_TAR $TMPFILE.new.tar
> +status=$?
> +
> +if [ "$status" = 0 ] ; then
> + echo "$test_name: $test_description: ok"
> + touch $test_name.ok
> +else
> + echo "$test_name: $test_description: failed"
> + diff $DIFF_OPTS $EXP $OUT > $test_name.failed
> +fi
> +
> +rm -rf $MKFS_TAR $MKFS_DIR $TMPFILE.cmd $TMPFILE.test_rel.c $TMPFILE.new.tar
> +unset MKFS_TAR MKFS_DIR OUT EXP
> diff --git a/tests/m_rootpaxtar/expect b/tests/m_rootpaxtar/expect
> new file mode 100644
> index 00000000..54a2d4b6
> --- /dev/null
> +++ b/tests/m_rootpaxtar/expect
> @@ -0,0 +1,87 @@
> +Filesystem volume name: <none>
> +Last mounted on: <not available>
> +Filesystem magic number: 0xEF53
> +Filesystem revision #: 1 (dynamic)
> +Filesystem features: has_journal ext_attr resize_inode dir_index filetype extent 64bit flex_bg sparse_super huge_file dir_nlink extra_isize metadata_csum
> +Default mount options: (none)
> +Filesystem state: clean
> +Errors behavior: Continue
> +Filesystem OS type: Linux
> +Inode count: 1024
> +Block count: 16384
> +Reserved block count: 819
> +Overhead clusters: 1543
> +Free blocks: 14827
> +Free inodes: 1012
> +First block: 1
> +Block size: 1024
> +Fragment size: 1024
> +Group descriptor size: 64
> +Reserved GDT blocks: 127
> +Blocks per group: 8192
> +Fragments per group: 8192
> +Inodes per group: 512
> +Inode blocks per group: 128
> +Flex block group size: 16
> +Mount count: 0
> +Check interval: 15552000 (6 months)
> +Reserved blocks uid: 0
> +Reserved blocks gid: 0
> +First inode: 11
> +Inode size: 256
> +Required extra isize: 32
> +Desired extra isize: 32
> +Journal inode: 8
> +Default directory hash: half_md4
> +Journal backup: inode blocks
> +Checksum type: crc32c
> +Journal features: (none)
> +Total journal size: 1024k
> +Total journal blocks: 1024
> +Max transaction length: 1024
> +Fast commit length: 0
> +Journal sequence: 0x00000001
> +Journal start: 0
> +
> +
> +Group 0: (Blocks 1-8192)
> + Primary superblock at 1, Group descriptors at 2-2
> + Reserved GDT blocks at 3-129
> + Block bitmap at 130 (+129)
> + Inode bitmap at 132 (+131)
> + Inode table at 134-261 (+133)
> + 7789 free blocks, 500 free inodes, 2 directories, 500 unused inodes
> + Free blocks: 404-8192
> + Free inodes: 13-512
> +Group 1: (Blocks 8193-16383) [INODE_UNINIT]
> + Backup superblock at 8193, Group descriptors at 8194-8194
> + Reserved GDT blocks at 8195-8321
> + Block bitmap at 131 (bg #0 + 130)
> + Inode bitmap at 133 (bg #0 + 132)
> + Inode table at 262-389 (bg #0 + 261)
> + 7038 free blocks, 512 free inodes, 0 directories, 512 unused inodes
> + Free blocks: 9346-16383
> + Free inodes: 513-1024
> +debugfs: stat /file
> +Inode: III Type: regular
> +Generation: 0 Version: 0x00000000:00000000
> +Size: 0
> +File ACL: 0
> +Links: 1 Blockcount: 0
> +Fragment: Address: 0 Number: 0 Size: 0
> + ctime: 0x00000000:00000000 -- Thu Jan 1 00:00:00 1970
> + atime: 0x00000000:00000000 -- Thu Jan 1 00:00:00 1970
> + mtime: 0x00000000:00000000 -- Thu Jan 1 00:00:00 1970
> +Size of extra inode fields: 32
> +Extended attributes:
> + security.capability (20) = 01 00 00 02 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> +EXTENTS:
> +debugfs: ea_list /file
> +Extended attributes:
> + security.capability (20) = 01 00 00 02 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> +Pass 1: Checking inodes, blocks, and sizes
> +Pass 2: Checking directory structure
> +Pass 3: Checking directory connectivity
> +Pass 4: Checking reference counts
> +Pass 5: Checking group summary information
> +test.img: 12/1024 files (0.0% non-contiguous), 1557/16384 blocks
> diff --git a/tests/m_rootpaxtar/mkpaxtar.pl b/tests/m_rootpaxtar/mkpaxtar.pl
> new file mode 100644
> index 00000000..9152828b
> --- /dev/null
> +++ b/tests/m_rootpaxtar/mkpaxtar.pl
> @@ -0,0 +1,69 @@
> +#!/usr/bin/env perl
> +
> +use strict;
> +use warnings;
> +
> +my @entries = (
> + # filename mode type content
> + ['./PaxHeaders/file', oct(644), 'x', "57 SCHILY.xattr.security.capability=\x01\0\0\x02\0\x20\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a"],
> + ['file', oct(644), 0, ''],
> +);
> +
> +my $num_entries = 0;
> +
> +foreach my $file (@entries) {
> + my ( $fname, $mode, $type, $content) = @{$file};
> + my $entry = pack(
> + 'a100 a8 a8 a8 a12 a12 A8 a1 a100 a6 a2 a32 a32 a8 a8 a155 x12',
> + $fname,
> + sprintf( '%07o', $mode ),
> + sprintf( '%07o', 0 ), # uid
> + sprintf( '%07o', 0 ), # gid
> + sprintf( '%011o', length $content ), # size
> + sprintf( '%011o', 0 ), # mtime
> + '', # checksum
> + $type,
> + '', # linkname
> + "ustar", # magic
> + "00", # version
> + '', # username
> + '', # groupname
> + '', # dev major
> + '', # dev minor
> + '', # prefix
> + );
> +
> + # compute and insert checksum
> + substr( $entry, 148, 7 ) =
> + sprintf( "%06o\0", unpack( "%16C*", $entry ) );
> + print $entry;
> + $num_entries += 1;
> +
> + if (length $content) {
> + print(pack 'a512', $content);
> + $num_entries += 1;
> + }
> +}
> +
> +# https://www.gnu.org/software/tar/manual/html_node/Standard.html
> +#
> +# Physically, an archive consists of a series of file entries terminated by an
> +# end-of-archive entry, which consists of two 512 blocks of zero bytes. At the
> +# end of the archive file there are two 512-byte blocks filled with binary
> +# zeros as an end-of-file marker.
> +
> +print(pack 'a512', '');
> +print(pack 'a512', '');
> +$num_entries += 2;
> +
> +# https://www.gnu.org/software/tar/manual/html_section/tar_76.html
> +#
> +# Some devices requires that all write operations be a multiple of a certain
> +# size, and so, tar pads the archive out to the next record boundary.
> +#
> +# The default blocking factor is 20. With a block size of 512 bytes, we get a
> +# record size of 10240.
> +
> +for (my $i = $num_entries; $i < 20; $i++) {
> + print(pack 'a512', '');
> +}
> diff --git a/tests/m_rootpaxtar/output.sed b/tests/m_rootpaxtar/output.sed
> new file mode 100644
> index 00000000..2e769678
> --- /dev/null
> +++ b/tests/m_rootpaxtar/output.sed
> @@ -0,0 +1,5 @@
> +s/^[[:space:]]*\([0-9]*\)\/[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*\)\/[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*\)[[:space:]]*-[[:space:]]*\([0-9]*\)[[:space:]]*[0-9]*[[:space:]]*-[[:space:]]*[0-9]*[[:space:]]*\([0-9]*\)/X \1\/\2 \3\/\4 \5-\6 AAA-BBB \7/g
> +s/^[[:space:]]*\([0-9]*\)\/[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*\)\/[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*\)[[:space:]]*-[[:space:]]*\([0-9]*\)[[:space:]]*[0-9]*[[:space:]]*\([0-9]*\)/Y \1\/\2 \3\/\4 \5-\6 AAA \7/g
> +s/Mode:.*$//g
> +s/User:.*Size:/Size:/g
> +s/^Inode: [0-9]*/Inode: III/g
> diff --git a/tests/m_rootpaxtar/script b/tests/m_rootpaxtar/script
> new file mode 100644
> index 00000000..41dc7c38
> --- /dev/null
> +++ b/tests/m_rootpaxtar/script
> @@ -0,0 +1,44 @@
> +# vim: filetype=sh
> +
> +test_description="create fs image from pax tarball with xattrs"
> +if ! test -x $DEBUGFS_EXE; then
> + echo "$test_name: $test_description: skipped (no debugfs)"
> + return 0
> +fi
> +if [ "$(grep -c 'define HAVE_ARCHIVE_H' ../lib/config.h)" -eq 0 ]; then
> + echo "$test_name: skipped (no libarchive)"
> + exit 0
> +fi
> +
> +OUT=$test_name.log
> +EXP=$test_dir/expect
> +
> +perl $test_dir/mkpaxtar.pl \
> + | $MKE2FS -q -F -o Linux -T ext4 -O metadata_csum,64bit -E lazy_itable_init=1 -b 1024 -d - $TMPFILE 16384 > $OUT 2>&1
> +
> +$DUMPE2FS $TMPFILE >> $OUT 2>&1
> +cat > $TMPFILE.cmd << ENDL
> +stat /file
> +ea_list /file
> +ENDL
> +$DEBUGFS -f $TMPFILE.cmd $TMPFILE 2>&1 | egrep -v '^(crtime|Inode checksum):' >> $OUT
> +
> +$FSCK -f -n $TMPFILE >> $OUT 2>&1
> +
> +sed -f $cmd_dir/filter.sed -f $test_dir/output.sed -e "s;$TMPFILE;test.img;" < $OUT > $OUT.tmp
> +mv $OUT.tmp $OUT
> +
> +# Do the verification
> +cmp -s $OUT $EXP
> +status=$?
> +
> +if [ "$status" = 0 ] ; then
> + echo "$test_name: $test_description: ok"
> + touch $test_name.ok
> +else
> + echo "$test_name: $test_description: failed"
> + diff $DIFF_OPTS $EXP $OUT > $test_name.failed
> +fi
> +
> +rm -rf $TMPFILE.cmd
> +unset OUT EXP
> diff --git a/tests/m_roottar/expect b/tests/m_roottar/expect
> new file mode 100644
> index 00000000..78e86c55
> --- /dev/null
> +++ b/tests/m_roottar/expect
> @@ -0,0 +1,208 @@
> +Filesystem volume name: <none>
> +Last mounted on: <not available>
> +Filesystem magic number: 0xEF53
> +Filesystem revision #: 1 (dynamic)
> +Filesystem features: has_journal ext_attr resize_inode dir_index filetype extent 64bit flex_bg sparse_super huge_file dir_nlink extra_isize metadata_csum
> +Default mount options: (none)
> +Filesystem state: clean
> +Errors behavior: Continue
> +Filesystem OS type: Linux
> +Inode count: 1024
> +Block count: 16384
> +Reserved block count: 819
> +Overhead clusters: 1543
> +Free blocks: 14824
> +Free inodes: 998
> +First block: 1
> +Block size: 1024
> +Fragment size: 1024
> +Group descriptor size: 64
> +Reserved GDT blocks: 127
> +Blocks per group: 8192
> +Fragments per group: 8192
> +Inodes per group: 512
> +Inode blocks per group: 128
> +Flex block group size: 16
> +Mount count: 0
> +Check interval: 15552000 (6 months)
> +Reserved blocks uid: 0
> +Reserved blocks gid: 0
> +First inode: 11
> +Inode size: 256
> +Required extra isize: 32
> +Desired extra isize: 32
> +Journal inode: 8
> +Default directory hash: half_md4
> +Journal backup: inode blocks
> +Checksum type: crc32c
> +Journal features: (none)
> +Total journal size: 1024k
> +Total journal blocks: 1024
> +Max transaction length: 1024
> +Fast commit length: 0
> +Journal sequence: 0x00000001
> +Journal start: 0
> +
> +
> +Group 0: (Blocks 1-8192)
> + Primary superblock at 1, Group descriptors at 2-2
> + Reserved GDT blocks at 3-129
> + Block bitmap at 130 (+129)
> + Inode bitmap at 132 (+131)
> + Inode table at 134-261 (+133)
> + 7786 free blocks, 486 free inodes, 5 directories, 486 unused inodes
> + Free blocks: 407-8192
> + Free inodes: 27-512
> +Group 1: (Blocks 8193-16383) [INODE_UNINIT]
> + Backup superblock at 8193, Group descriptors at 8194-8194
> + Reserved GDT blocks at 8195-8321
> + Block bitmap at 131 (bg #0 + 130)
> + Inode bitmap at 133 (bg #0 + 132)
> + Inode table at 262-389 (bg #0 + 261)
> + 7038 free blocks, 512 free inodes, 0 directories, 512 unused inodes
> + Free blocks: 9346-16383
> + Free inodes: 513-1024
> +debugfs: stat /dev/
> +Inode: III Type: directory
> +Generation: 0 Version: 0x00000000:00000000
> +Size: 1024
> +File ACL: 0
> +Links: 4 Blockcount: 2
> +Fragment: Address: 0 Number: 0 Size: 0
> +Size of extra inode fields: 32
> +EXTENTS:
> +(0):404
> +debugfs: stat /dev/console
> +Inode: III Type: character special
> +Generation: 0 Version: 0x00000000:00000000
> +Size: 0
> +File ACL: 0
> +Links: 1 Blockcount: 0
> +Fragment: Address: 0 Number: 0 Size: 0
> +Size of extra inode fields: 32
> +Device major/minor number: 05:01 (hex 05:01)
> +debugfs: stat /dev/fd
> +Inode: III Type: symlink
> +Generation: 0 Version: 0x00000000:00000000
> +Size: 13
> +File ACL: 0
> +Links: 1 Blockcount: 0
> +Fragment: Address: 0 Number: 0 Size: 0
> +Size of extra inode fields: 32
> +Fast link dest: "/proc/self/fd"
> +debugfs: stat /dev/full
> +Inode: III Type: character special
> +Generation: 0 Version: 0x00000000:00000000
> +Size: 0
> +File ACL: 0
> +Links: 1 Blockcount: 0
> +Fragment: Address: 0 Number: 0 Size: 0
> +Size of extra inode fields: 32
> +Device major/minor number: 01:07 (hex 01:07)
> +debugfs: stat /dev/null
> +Inode: III Type: character special
> +Generation: 0 Version: 0x00000000:00000000
> +Size: 0
> +File ACL: 0
> +Links: 1 Blockcount: 0
> +Fragment: Address: 0 Number: 0 Size: 0
> +Size of extra inode fields: 32
> +Device major/minor number: 01:03 (hex 01:03)
> +debugfs: stat /dev/ptmx
> +Inode: III Type: character special
> +Generation: 0 Version: 0x00000000:00000000
> +Size: 0
> +File ACL: 0
> +Links: 1 Blockcount: 0
> +Fragment: Address: 0 Number: 0 Size: 0
> +Size of extra inode fields: 32
> +Device major/minor number: 05:02 (hex 05:02)
> +debugfs: stat /dev/pts/
> +Inode: III Type: directory
> +Generation: 0 Version: 0x00000000:00000000
> +Size: 1024
> +File ACL: 0
> +Links: 2 Blockcount: 2
> +Fragment: Address: 0 Number: 0 Size: 0
> +Size of extra inode fields: 32
> +EXTENTS:
> +(0):405
> +debugfs: stat /dev/random
> +Inode: III Type: character special
> +Generation: 0 Version: 0x00000000:00000000
> +Size: 0
> +File ACL: 0
> +Links: 1 Blockcount: 0
> +Fragment: Address: 0 Number: 0 Size: 0
> +Size of extra inode fields: 32
> +Device major/minor number: 01:08 (hex 01:08)
> +debugfs: stat /dev/shm/
> +Inode: III Type: directory
> +Generation: 0 Version: 0x00000000:00000000
> +Size: 1024
> +File ACL: 0
> +Links: 2 Blockcount: 2
> +Fragment: Address: 0 Number: 0 Size: 0
> +Size of extra inode fields: 32
> +EXTENTS:
> +(0):406
> +debugfs: stat /dev/stderr
> +Inode: III Type: symlink
> +Generation: 0 Version: 0x00000000:00000000
> +Size: 15
> +File ACL: 0
> +Links: 1 Blockcount: 0
> +Fragment: Address: 0 Number: 0 Size: 0
> +Size of extra inode fields: 32
> +Fast link dest: "/proc/self/fd/2"
> +debugfs: stat /dev/stdin
> +Inode: III Type: symlink
> +Generation: 0 Version: 0x00000000:00000000
> +Size: 15
> +File ACL: 0
> +Links: 1 Blockcount: 0
> +Fragment: Address: 0 Number: 0 Size: 0
> +Size of extra inode fields: 32
> +Fast link dest: "/proc/self/fd/0"
> +debugfs: stat /dev/stdout
> +Inode: III Type: symlink
> +Generation: 0 Version: 0x00000000:00000000
> +Size: 15
> +File ACL: 0
> +Links: 1 Blockcount: 0
> +Fragment: Address: 0 Number: 0 Size: 0
> +Size of extra inode fields: 32
> +Fast link dest: "/proc/self/fd/1"
> +debugfs: stat /dev/tty
> +Inode: III Type: character special
> +Generation: 0 Version: 0x00000000:00000000
> +Size: 0
> +File ACL: 0
> +Links: 1 Blockcount: 0
> +Fragment: Address: 0 Number: 0 Size: 0
> +Size of extra inode fields: 32
> +Device major/minor number: 05:00 (hex 05:00)
> +debugfs: stat /dev/urandom
> +Inode: III Type: character special
> +Generation: 0 Version: 0x00000000:00000000
> +Size: 0
> +File ACL: 0
> +Links: 1 Blockcount: 0
> +Fragment: Address: 0 Number: 0 Size: 0
> +Size of extra inode fields: 32
> +Device major/minor number: 01:09 (hex 01:09)
> +debugfs: stat /dev/zero
> +Inode: III Type: character special
> +Generation: 0 Version: 0x00000000:00000000
> +Size: 0
> +File ACL: 0
> +Links: 1 Blockcount: 0
> +Fragment: Address: 0 Number: 0 Size: 0
> +Size of extra inode fields: 32
> +Device major/minor number: 01:05 (hex 01:05)
> +Pass 1: Checking inodes, blocks, and sizes
> +Pass 2: Checking directory structure
> +Pass 3: Checking directory connectivity
> +Pass 4: Checking reference counts
> +Pass 5: Checking group summary information
> +test.img: 26/1024 files (0.0% non-contiguous), 1560/16384 blocks
> diff --git a/tests/m_roottar/mktar.pl b/tests/m_roottar/mktar.pl
> new file mode 100644
> index 00000000..3a555afe
> --- /dev/null
> +++ b/tests/m_roottar/mktar.pl
> @@ -0,0 +1,62 @@
> +#!/usr/bin/env perl
> +
> +use strict;
> +use warnings;
> +
> +# type codes:
> +# 0 -> normal file
> +# 1 -> hardlink
> +# 2 -> symlink
> +# 3 -> character special
> +# 4 -> block special
> +# 5 -> directory
> +my @devfiles = (
> + # filename mode type link target major minor
> + ["", oct(755), 5, '', undef, undef],
> + ["console", oct(666), 3, '', 5, 1],
> + ["fd", oct(777), 2, '/proc/self/fd', undef, undef],
> + ["full", oct(666), 3, '', 1, 7],
> + ["null", oct(666), 3, '', 1, 3],
> + ["ptmx", oct(666), 3, '', 5, 2],
> + ["pts/", oct(755), 5, '', undef, undef],
> + ["random", oct(666), 3, '', 1, 8],
> + ["shm/", oct(755), 5, '', undef, undef],
> + ["stderr", oct(777), 2, '/proc/self/fd/2', undef, undef],
> + ["stdin", oct(777), 2, '/proc/self/fd/0', undef, undef],
> + ["stdout", oct(777), 2, '/proc/self/fd/1', undef, undef],
> + ["tty", oct(666), 3, '', 5, 0],
> + ["urandom", oct(666), 3, '', 1, 9],
> + ["zero", oct(666), 3, '', 1, 5],
> +);
> +
> +my $mtime = time;
> +if (exists $ENV{SOURCE_DATE_EPOCH}) {
> + $mtime = $ENV{SOURCE_DATE_EPOCH} + 0;
> +}
> +
> +foreach my $file (@devfiles) {
> + my ( $fname, $mode, $type, $linkname, $devmajor, $devminor ) = @{$file};
> + my $entry = pack(
> + 'a100 a8 a8 a8 a12 a12 A8 a1 a100 a8 a32 a32 a8 a8 a155 x12',
> + "./dev/$fname",
> + sprintf( '%07o', $mode ),
> + sprintf( '%07o', 0 ), # uid
> + sprintf( '%07o', 0 ), # gid
> + sprintf( '%011o', 0 ), # size
> + sprintf( '%011o', $mtime ),
> + '', # checksum
> + $type,
> + $linkname,
> + "ustar ",
> + '', # username
> + '', # groupname
> + defined($devmajor) ? sprintf( '%07o', $devmajor ) : '',
> + defined($devminor) ? sprintf( '%07o', $devminor ) : '',
> + '', # prefix
> + );
> +
> + # compute and insert checksum
> + substr( $entry, 148, 7 ) =
> + sprintf( "%06o\0", unpack( "%16C*", $entry ) );
> + print $entry;
> +}
> diff --git a/tests/m_roottar/output.sed b/tests/m_roottar/output.sed
> new file mode 100644
> index 00000000..2e769678
> --- /dev/null
> +++ b/tests/m_roottar/output.sed
> @@ -0,0 +1,5 @@
> +s/^[[:space:]]*\([0-9]*\)\/[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*\)\/[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*\)[[:space:]]*-[[:space:]]*\([0-9]*\)[[:space:]]*[0-9]*[[:space:]]*-[[:space:]]*[0-9]*[[:space:]]*\([0-9]*\)/X \1\/\2 \3\/\4 \5-\6 AAA-BBB \7/g
> +s/^[[:space:]]*\([0-9]*\)\/[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*\)\/[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*\)[[:space:]]*-[[:space:]]*\([0-9]*\)[[:space:]]*[0-9]*[[:space:]]*\([0-9]*\)/Y \1\/\2 \3\/\4 \5-\6 AAA \7/g
> +s/Mode:.*$//g
> +s/User:.*Size:/Size:/g
> +s/^Inode: [0-9]*/Inode: III/g
> diff --git a/tests/m_roottar/script b/tests/m_roottar/script
> new file mode 100644
> index 00000000..134b9a2e
> --- /dev/null
> +++ b/tests/m_roottar/script
> @@ -0,0 +1,57 @@
> +# vim: filetype=sh
> +
> +test_description="create fs image from tarball"
> +if ! test -x $DEBUGFS_EXE; then
> + echo "$test_name: $test_description: skipped (no debugfs)"
> + return 0
> +fi
> +if [ "$(grep -c 'define HAVE_ARCHIVE_H' ../lib/config.h)" -eq 0 ]; then
> + echo "$test_name: skipped (no libarchive)"
> + exit 0
> +fi
> +
> +OUT=$test_name.log
> +EXP=$test_dir/expect
> +
> +perl $test_dir/mktar.pl \
> + | $MKE2FS -q -F -o Linux -T ext4 -O metadata_csum,64bit -E lazy_itable_init=1 -b 1024 -d - $TMPFILE 16384 > $OUT 2>&1
> +
> +$DUMPE2FS $TMPFILE >> $OUT 2>&1
> +cat > $TMPFILE.cmd << ENDL
> +stat /dev/
> +stat /dev/console
> +stat /dev/fd
> +stat /dev/full
> +stat /dev/null
> +stat /dev/ptmx
> +stat /dev/pts/
> +stat /dev/random
> +stat /dev/shm/
> +stat /dev/stderr
> +stat /dev/stdin
> +stat /dev/stdout
> +stat /dev/tty
> +stat /dev/urandom
> +stat /dev/zero
> +ENDL
> +$DEBUGFS -f $TMPFILE.cmd $TMPFILE 2>&1 | egrep -v "(time|checksum):" >> $OUT
> +
> +$FSCK -f -n $TMPFILE >> $OUT 2>&1
> +
> +sed -f $cmd_dir/filter.sed -f $test_dir/output.sed -e "s;$TMPFILE;test.img;" < $OUT > $OUT.tmp
> +mv $OUT.tmp $OUT
> +
> +# Do the verification
> +cmp -s $OUT $EXP
> +status=$?
> +
> +if [ "$status" = 0 ] ; then
> + echo "$test_name: $test_description: ok"
> + touch $test_name.ok
> +else
> + echo "$test_name: $test_description: failed"
> + diff $DIFF_OPTS $EXP $OUT > $test_name.failed
> +fi
> +
> +rm -rf $TMPFILE.cmd
> +unset OUT EXP
> --
> 2.40.0
>
Powered by blists - more mailing lists