#!/usr/bin/perl -w

# This script modifies your fstab in order to mount filesystems via their labels
# instead of their device names. Use at your own risk.

# For swap devices it is neccesary to label the device in order to recognize it
# later and that requires that swap is turned off briefly.

use strict;

open (FILE, "/etc/fstab") or die "Couldn't open /etc/fstab for reading: $!";
my @lines = <FILE>;
close FILE;

my @newlines = ();
my $swap = 0;
my @commands = ();

for my $line (@lines) {
	my @fields = split(/\s+/, $line);
	my ($dev, $type) = ($fields[0], $fields[2]);

	my $changed = 0;

	if ($type eq 'ufs' && $dev !~ m!^/dev/ufsid/!) {
		my $id = get_ufsid($dev);

		if (defined $id) {
			substr($line, 0, length $dev, "/dev/ufsid/$id") eq $dev or die "Devices do not match";
		}
	} elsif ($type eq 'swap' && $dev !~ m!^/dev/label/!) {
		my $id = get_swapid();

		substr($line, 0, length $dev, "/dev/label/$id") eq $dev or die "Devices do not match";

		$swap = 1;
		push @commands, "glabel label $id $dev";
	}

	push @newlines, $line;
}

if ($swap) {
	print "I need to turn off swap in order to label your swap device. Press ENTER to continue.\n";
	<STDIN>;

	print "\n==> Turning off swap\n";
	system "swapoff -a";
}

rename "/etc/fstab", "/etc/fstab.old" or die "Couldn't preserve /etc/fstab as /etc/fstab.old";

for my $command (@commands) {
	print "==> Running \"$command\"\n";
	system $command;
}

print "==> Writing new /etc/fstab\n";

open (FILE, ">/etc/fstab") or die "Couldn't open /tmp/fstab for writing: $!";
print FILE @lines;
close FILE;

if ($swap) {
	print "==> Turning swap back on\n";
	system "swapon -a";
}

sub get_ufsid {
	my ($dev) = @_;

	my @dlines = `dumpfs $dev`;

	if ($dlines[1] =~ /\s+\[ ([0-9a-f]{6,8}) ([0-9a-f]{6,8}) \]\s*$/) {
		return sprintf("%08s%08s", $1, $2);
	}

	return undef;
}

sub get_swapid {
	my $length = 6;

	open(RANDOM, "/dev/urandom") or die "Couldn't open /dev/urandom for reading: $!";
	my $bytes;
	die "Unexpected amount of random bytes read" unless read(RANDOM, $bytes, $length) == $length;
	close RANDOM;

	return "sw_" . sprintf("%02x" x $length, unpack("C*", $bytes));
}
