If you can't mount the image you might still be able in some cases to "stream out" some of its data with cpio
.
Once you've ascertained whether the image is:
- An image using a supported filesystem and a partition -->
mount
- An image using a supported filesystem and more than one partition -->
mount with offset
, or use dd
to extract a partition with offset then
mount that partition only or use something like kpartx
- An image not using a supported filesystem or with no filesystem
at all --> kernel support and further investigation...
You can use the hexdump
and strings
utilities to try to analyze the header and to extract text strings from the image and gain more information about the image file and its structure.
Something captured my interest in doing so:
@(#)/usr/bin/echo.sl 1.1 4.0 10/01/90 16865 AT&T-SF
There's a line like this for every single binary in the image so you somewhat know what's in there. Also, in this case, when you take a closer look at how the installation process occurs on the original platform with installpkg
, you find out that:
The basic mechanism to transfer software from a floppy disk to the
UNIX System V /386 hard disk is cpio.
Basically, the data is extracted with cpio
to /usr/tmp/install and a series of files are included with this (an install, ascii, file, name and size file). It so happens here that this command:
cat U19.IMA | cpio -imdv
outputs malformed number errors to begin with, but then creates a /usr/bin folder with the contents of the image! The tr
I was looking for is there:
#file tr
tr: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), stripped.
Trying cpio
in the first place can't hurt!
Firstly, writing a sparse image to a disk will not result in anything but the whole of the size of that image file - holes and all - covering the disk. This is because handling of sparse files is a quality of the filesystem - and a raw device (such as the one to which you write the image) has no such thing yet. A sparse file can be stored safely and securely on a medium controlled by a filesystem which understands sparse files (such as an ext4 device) but as soon as you write it out it will envelop all that you intend it to. And so what you should do is either:
Simply store it on an fs which understands sparse files until you are prepared to write it.
Make it two layers deep...
Which is to say, write out your main image to a file, create another parent image with an fs which understands sparse files, then copy your image to the parent image, and...
When it comes time to write the image, first write your parent image, then write your main image.
Here's how to do 2:
Create a 1GB sparse file...
dd bs=1kx1k seek=1k of=img </dev/null
Write two ext4
partitions to its partition table: 1 200MB, 2 800MB...
printf '%b\n\n\n\n' n '+200M\nn\n' 'w\n\c' | fdisk img
Create two ext4
filesystems on a -P
artitioned loop device and put a copy of the second on the first...
sudo sh -c '
for p in "$(losetup --show -Pf img)p"* ### the for loop will iterate
do mkfs.ext4 "$p" ### over fdisks two partitions
mkdir -p ./mnt/"${p##*/}" ### and mkfs, then mount each
mount "$p" ./mnt/"${p##*/}" ### on dirs created for them
done; sync; cd ./mnt/*/ ### next we cp a sparse image
cp --sparse=always "$p" ./part2 ### of part2 onto part1
dd bs=1kx1k count=175 </dev/zero >./zero_fill ### fill out part1 w/ zeroes
sync; cd ..; ls -Rhls . ### sync, and list contents
umount */; losetup -d "${p%p*}" ### last umount, destroy
rm -rf loop*p[12]/ ' ### loop devs and mount dirs
mke2fs 1.42.12 (29-Aug-2014)
Discarding device blocks: done
Creating filesystem with 204800 1k blocks and 51200 inodes
Filesystem UUID: 2f8ae02f-4422-4456-9a8b-8056a40fab32
Superblock backups stored on blocks:
8193, 24577, 40961, 57345, 73729
Allocating group tables: done
Writing inode tables: done
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done
mke2fs 1.42.12 (29-Aug-2014)
Discarding device blocks: done
Creating filesystem with 210688 4k blocks and 52752 inodes
Filesystem UUID: fa14171c-f591-4067-a39a-e5d0dac1b806
Superblock backups stored on blocks:
32768, 98304, 163840
Allocating group tables: done
Writing inode tables: done
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done
175+0 records in
175+0 records out
183500800 bytes (184 MB) copied, 0.365576 s, 502 MB/s
./:
total 1.0K
1.0K drwxr-xr-x 3 root root 1.0K Jul 16 20:49 loop0p1
0 drwxr-xr-x 2 root root 40 Jul 16 20:42 loop0p2
./loop0p1:
total 176M
12K drwx------ 2 root root 12K Jul 16 20:49 lost+found
79K -rw-r----- 1 root root 823M Jul 16 20:49 part2
176M -rw-r--r-- 1 root root 175M Jul 16 20:49 zero_fill
./loop0p1/lost+found:
total 0
./loop0p2:
total 0
Now that's a lot of output - mostly from mkfs.ext4
- but notice especially the ls
bits at the bottom. ls -s
will show the actual -s
ize of a file on disk - and it is always displayed in the first column.
Now we can basically reduce our image to only the first partition...
fdisk -l img
Disk img: 1 GiB, 1073741824 bytes, 2097152 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xc455ed35
Device Boot Start End Sectors Size Id Type
img1 2048 411647 409600 200M 83 Linux
img2 411648 2097151 1685504 823M 83 Linux
There fdisk
tells us there are 411647 +1 512 byte sectors in the first partition of img
...
dd seek=411648 of=img </dev/null
That truncates the img
file to only its first partition. See?
ls -hls img
181M -rw-r--r-- 1 mikeserv mikeserv 201M Jul 16 21:37 img
...but we can still mount that partition...
sudo mount "$(sudo losetup -Pf --show img)p"*1 ./mnt
...and here are its contents...
ls -hls ./mnt
total 176M
12K drwx------ 2 root root 12K Jul 16 21:34 lost+found
79K -rw-r----- 1 root root 823M Jul 16 21:34 part2
176M -rw-r--r-- 1 root root 175M Jul 16 21:34 zero_fill
And we can append the stored image of the second partition to the first...
sudo sh -c '
dd seek=411648 if=./mnt/part2 of=img
umount ./mnt; losetup -D
mount "$(losetup -Pf --show img)p"*2 ./mnt
ls ./mnt; umount ./mnt; losetup -D'
1685504+0 records in
1685504+0 records out
862978048 bytes (863 MB) copied, 1.96805 s, 438 MB/s
lost+found
Now that has grown our img
file: it's no longer sparse...
ls -hls img
1004M -rw-r--r-- 1 mikeserv mikeserv 1.0G Jul 16 21:58 img
...but removing that is as simple the second time as it was the first, of course...
dd seek=411648 of=img </dev/null
ls -hls img
181M -rw-r--r-- 1 mikeserv mikeserv 201M Jul 16 22:01 img
Best Answer
It's possible to do with fuse, but would probably be cleaner with custom tools.
Solution
With apt-get-able tools the following kludge is possible:
Explanation
The basic idea is that fuse can be used to separate a full disk image in place into files that point to it's partitions. vdfuse does this, but is a VirtualBox tool and requires a VDI or VMDK file to work. xmount uses fuse to make a raw disk image appear as a VDI file.
Finally once the partition file is available via vdfuse, it can be mounted via an ext2/3/4 tool fuseext2.
It's ugly but it works completely in userspace.
Update
vdfuse should be able to mount a raw image without the help of xmount, but there is a bug which ignores the RAW option.
I tracked down and fixed the bug with a patch here:
https://bugs.launchpad.net/ubuntu/+source/virtualbox-ose/+bug/1019075