Tuesday, July 05, 2011

Copying a filesystem on Linux

In the last two days, I've had to copy a filesystem on my Linux development VM four times (to grow it).  I eventually made a shell script for it, since I kept forgetting details (like the arguments to cpio).  Since I started this blog to have a place to publish random bits of information for future Google users, I'm putting the script here.  The script is below; you pass in the old device name (e.g., sda) as the first argument, and the new one (e.g., sdc) as the second.

There's some significant assumptions in this script, and they may not be warranted for your system:
  • It assumes that you have only one filesystem, and that's on the first partition of the disk.  (It's also easy to add a swap partition using mkswap.)
  • You're expected to use fdisk to set up the new filesystem yourself.  (Linux is fs type 83; Linux swap is 82.)
  • This builds an ext3 filesystem.
  • It assumes that once you're finished, you'll be putting the new device into the old device's place; in other words, it doesn't try to replace sdc with sda in /etc files.  (If you're only using UUIDs in your fstab, this probably doesn't matter.)
  • It uses a default grub install, more or less.  If you're using LILO, or have got something funky going on, it might not carry over.  If you've customized your grub.cfg, for example, it will get overwritten.  (However, changes in grub.d are fine and will be reflected.)
  • As used here, cpio will make sparse files out of non-sparse files.  Normally, this is a good thing, but some programs (such as BitTorrent clients) will intentionally build non-sparse files.  (It's ok to turn them sparse after the torrent completes, and it's only a fragmentation hit if they get turned into sparse before the torrent is done.)
  • This uses a 64k I/O block size.  I'm doing that because that's the block size of the default QEMU backing store under qcow2.  (Remember, in my case, I'm doing this for a VM.)  I'm not sure if that helps anything or not (since the I/O goes through many layers between cpio and qcow), but it's not going to hurt anything.  However, you may want to replace the -C65536 with -B if you're using a normal disk.

This script is better to learn from than to use directly.  But if you need to copy a filesystem (to grow it, move off a failing disk, defragment it (rarely necessary on Linux), or whatever), this may give you a good starting point.
#! /bin/sh

set -xe

fdisk /dev/${out}
mke2fs -j /dev/${out}1
mount /dev/${out}1 /mnt
find / -xdev -depth -print0 | cpio -pdmV0a --sparse -C65536 /mnt
grub-install --root-directory=/mnt /dev/${out}
perl -pi.bak -e s/$(blkid -o value -s UUID /dev/${in}1)/(blkid -o value -s UUID /dev/${out}1)/g /mnt/etc/fstab /mnt/boot/grub/grub.cfg
umount /mnt

A few implementation comments:

  • This makes a lot of assumptions (listed above) for simplicity.  Feel free to improve it.
  • The -depth is so that find will print the directories after their contents, which will cause cpio to fix the modification time.  Otherwise, cpio will create the directory, change its mtime to match the original, and then add contents, thereby changing the directory's mtime to the current time.
  • The Perl can be done by hand: it's changing the old filesystem's UUID to the new filesystem's UUID in fstab and grub.cfg.  You can find out the IDs by running sudo blkid.
  • Note that the Perl line is long; be careful when copying it.  It spans from perl to grub.cfg.

Share and enjoy!

PS: If you're wanting to grow a filesystem on the same disk (i.e., you've deleted the next partition and want to use its free space), there's utilities to do that.  This script is about moving to a different disk.

(Disclaimer: I release the above code to the public domain, as if the public actually wants it.  It's for education, and should be evaluated and customized before running it in any particular environment.  I provide no warranty, expressed or implied; if it breaks, you get to keep both pieces.)

No comments: