Alexander Gromnitsky's Blog

Disk images as archive file formats

Latest update:

As a prank, how do you create an archive in Linux that ⓐ cannot be opened in Windows (without WSL2 or Cygwin), ⓑ can be opened in MacOS of FreeBSD?

Creating an .cpio or .tar.xz won't cut it: file archivers such as 7-Zip are free & easy to install. Furthermore, sending an ext4 image, generated as follows:

$ truncate -s 10M file.img
$ mkfs.ext4 file.img
$ sudo mount -o loop file.img /somewhere
$ sudo cp something /somewhere
$ sudo umount /somewhere

doesn't help nowadays, for 7-Zip opens them too1. Although disk cloning utils like FSArchiver can produce an image file from a directory, they are exclusive to Linux.

It boils down to this: which filesystems can be read across Linux/MacOS/FreeBSD that Windows file archivers don't recognise? This rules out fat/ntfs/udf, for they are too common, or f2fs/nilfs2, for they are Linux-only.

The only viable candidate I found is XFS. Btrfs was a contender, but I'm unsure how to mount it on Mac.

Below is a script to automate the creation of prank archives. It takes any zip/tar.gz (or anything else that bsdtar is able to parse) & outputs an image file in the format specified by the output file extension:

sudo ./mkimg file.zip file.xfs

It requires sudo, for mount -o loop can't be done under a regular user.

#!/bin/sh

set -e

input=$1
output=$2
type=${2##*.}
[ -r "$input" ] && [ "$output" ] && [ "`id -u`" = 0 ] || {
    echo Usage: sudo mkimg file.zip file.ext2 1>&2
    exit 1
}
mkfs=mkfs.$type
cmd() { for c; do command -v $c >/dev/null || { echo no $c; return 1; }; done; }
cmd bsdtar "$mkfs"

cleanup() {
    set +e
    umount "$mnt" 2>/dev/null
    rm -rf "$mnt" "$log"
    [ "$ok" ] || rm -f "$output"
}

trap cleanup 0 1 2 15
usize=`bsdtar tvf "$input" | awk '{s += $5} END {print s}'`
mnt=`mktemp -d`
log=`mktemp`

case "$type" in
    msdos|*fat) size=$((1024*1024 + usize*2)); opt_tar=--no-same-owner ;;
    ext*|udf  ) size=$((1024*1024 + usize*2)) ;;
    f2fs      ) size=$((1024*1024*50 + usize*2)) ;;
    btrfs     ) size=$((114294784 + usize*2)) ;;
    nilfs2    ) size=$((134221824 + usize*2)) ;;
    xfs       ) size=$((1024*1024*300 + usize*2)) ;;
    jfs       ) size=$((1024*1024*16 + usize*2)); opt=-q ;;
    hfsplus   )
        size=$((1024*1024 + usize*2))
        [ $((size % 4096)) != 0 ] && size=$((size + (4096-(size % 4096)))) ;;
    *) echo "$type is untested" 1>&2; exit 1
esac
rm -f "$output"
truncate -s $size "$output"
$mkfs $opt "$output" > "$log" 2>&1 || { cat "$log"; exit 1; }

mount -o loop "$output" "$mnt"
bsdtar -C "$mnt" $opt_tar --chroot -xf "$input"
[ "$SUDO_UID" ] && chown "$SUDO_UID:$SUDO_GID" "$output"
ok=1

.xfs files start at a size of 300MB, even if you place a 0-length file in it, but bzip2 compresses such an image into 6270 bytes.

To mount an .xfs under a regular user, use libfsxfs.


  1. 7z -i prints all supported formats.

Tags: ойті
Authors: ag