是否可以在原始映像文件中重新排列并重新创建单个磁盘分区?

是否可以在原始映像文件中重新排列并重新创建单个磁盘分区?

主机:Ubuntu 20.04.4 LTS

当前状态:原始图像文件有 2 个分区,p1:启动和 p2:rootfs。

Disk original.img.bak: 3.25 GiB, 3487742976 bytes, 6811998 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: 0xddbefb06

Device            Boot  Start     End Sectors  Size Id Type
original.img.bak1 *      2048  526335  524288  256M  c W95 FAT32 (LBA)
original.img.bak2      526336 6811963 6285628    3G 83 Linux

目标:创建第三个持久分区,并将其定位为第二个分区(p2),并将 rootfs 分区位置更改为第三个分区(p3)。

它被添加到脚本中,因此例如不能使用任何 GUI 工具,也没有任何特殊的 stdin 要求。

生成的映像将是:p1 boot、p2 persistence、p3 rootfs

我心中是如何思考这个过程的(“算法”):

Write p2 (rootfs) into a file with dd
Remove p2 from the partition table (remove partition)
Unmount
Truncate the image file unallocated space left from the removed rootfs partition
Allocate space for new partition (persistence) + extra empty space => append to the image file as zeroes
Mount
Create the new partition (now p2)
Create the filesystem
Check the filesystem
Unmount
Allocate space for the original rootfs partition + extra empty space
Mount
Create a new partition for the rootfs
Write the rootfs image file over the partition with dd
=> TODO somehow fix the partition table to think where the correct start and end sectors are
Check the filesystem is ok

我为此过程编写的脚本:

imgFile='original.img'
imgFileBak="${imgFile}.bak"

rootImgFile='rootfs.img'

zeroDev='/dev/zero'

partNumBoot=1
partNumRoot=2
partNumPersist=3

partNumBootNew=1
partNumRootNew=3
partNumPersistNew=2

rm -vf ${imgFile}
cp -v ${imgFileBak} ${imgFile}

dev=$(losetup -f)
losetup -P "${dev}" ${imgFile}

sync
sleep 1

dd status=progress conv=sparse if="${dev}p${partNumRoot}" of=${rootImgFile}

sync
sleep 1

sfdisk -s -l ${imgFile}
parted -s -a opt "${dev}" rm ${partNumRoot}
sfdisk -s -l ${imgFile}

sync
sleep 1

partEnd=$(sfdisk -s -l -o End "${dev}" | tail -1)
partEnd=$(( partEnd + 1 ))

losetup -d "${dev}"
losetup -D

sync
sleep 1

stat ${imgFile}
truncate --size $(( partEnd * 512 )) ${imgFile}
stat ${imgFile}

sync
sleep 1

dd status=progress if=${zeroDev} bs=1MB count=256 >> ${imgFile}
dd status=progress if=${zeroDev} bs=1b count=64 >> ${imgFile}

sync
sleep 1

dev=$(losetup -f)
losetup -P "${dev}" ${imgFile}

sync
sleep 1

sfdisk -s -l ${imgFile}
parted -s -a opt "${dev}" mkpart primary ext4 $(( partEnd ))s 100%
sfdisk -s -l ${imgFile}

sync
sleep 1

mkfs.ext4 "${dev}p${partNumPersistNew}"

sync
sleep 1

e2fsck -v -y -f "${dev}p${partNumPersistNew}"

sync
sleep 1

losetup -d "${dev}"
losetup -D

sync
sleep 1

dd status=progress if=${rootImgFile} >> ${imgFile}
dd status=progress if=${zeroDev} bs=1b count=64 >> ${imgFile}

sync
sleep 1

dev=$(losetup -f)
losetup -P "${dev}" ${imgFile}

sync
sleep 1

partEnd=$(sfdisk -s -l -o End "${dev}" | tail -1)
partEnd=$(( partEnd + 1 ))

sfdisk -s -l ${imgFile}
parted -s -a opt "${dev}" mkpart primary ext4 $(( partEnd ))s 100%
sfdisk -s -l ${imgFile}

sync
sleep 1

losetup -d "${dev}"
losetup -D

sync
sleep 1

dev=$(losetup -f)
losetup -P "${dev}" ${imgFile}

sync
sleep 1

dd status=progress if=${rootImgFile} of="${dev}p${partNumRootNew}"

sync
sleep 1

losetup -d "${dev}"
losetup -D

sync
sleep 1

dev=$(losetup -f)
losetup -P "${dev}" ${imgFile}

sync
sleep 1

sfdisk -s -l "${dev}"

e2fsck -v -y -f "${dev}p${partNumPersistNew}"
e2fsck -v -y -f "${dev}p${partNumRootNew}"

结果:

...

(S)fdisk 输出:

sfdisk -s -l ${imgFile}
+ sfdisk -s -l original.img

Disk original.img: 3.5 GiB, 3743791104 bytes, 7312092 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: 0xddbefb06

Device        Boot   Start     End Sectors  Size Id Type
original.img1 *       2048  526335  524288  256M  c W95 FAT32 (LBA)
original.img2       526336 1026047  499712  244M 83 Linux
original.img3      1026048 7311359 6285312    3G 83 Linux

检查分区2:

e2fsck -v -y -f "${dev}p${partNumPersistNew}"
+ e2fsck -v -y -f /dev/loop5p2
e2fsck 1.45.5 (07-Jan-2020)
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

          11 inodes used (0.02%, out of 62464)
           0 non-contiguous files (0.0%)
           0 non-contiguous directories (0.0%)
             # of inodes with ind/dind/tind blocks: 0/0/0
             Extent depth histogram: 3
        6122 blocks used (9.80%, out of 62464)
           0 bad blocks
           1 large file

           0 regular files
           2 directories
           0 character device files
           0 block device files
           0 fifos
           0 links
           0 symbolic links (0 fast symbolic links)
           0 sockets
------------
           2 files

检查分区3:

e2fsck -v -y -f "${dev}p${partNumRootNew}"
+ e2fsck -v -y -f /dev/loop5p3

e2fsck 1.45.5 (07-Jan-2020)
The filesystem size (according to the superblock) is 785703 blocks
The physical size of the device is 501864 blocks
Either the superblock or the partition table is likely to be corrupt!
Abort? yes

因此,看起来该分区无法被重新识别,因为创建该“虚拟”分区规范是为了将真实分区克隆到其上。

这种操作是否可以通过一些小的调整来实现,是否应该以完全不同的方式来实现,或者是否可以正确地实现?

答案1

这个想法确实有效。我找到了解决问题的方法。

唯一的问题是我使用选项 conv=sparse 克隆分区,它使用孔代替空块,从而搞砸了分区克隆映像文件。

为了解决这个问题,我只需要更改命令来制作一个完美的克隆,更改:

dd status=progress conv=sparse if="${dev}p${partNumRoot}" of=${rootImgFile}

简单来说就是:

dd status=progress if="${dev}p${partNumRoot}" of=${rootImgFile}

相关内容