A. bootvinum Perl Script

The bootvinum Perl script below reads /etc/fstab and current drive partitioning. It then writes several files in the current directory and several variants of /etc/fstab in /etc. These files significantly simplify the installation of Vinum and recovery from spindle failures.

#!/usr/bin/perl -w
use strict;
use FileHandle;

my $config_tag1 = '$Id: VinumBootstrap.sgml,v 1.28 2001/10/14 14:08:39 bob Exp bob $';
# Copyright (C) 2001 Robert A. Van Valzah 
# Bootstrap Vinum
# Read /etc/fstab and current partitioning for all spindles mentioned there.
# Generate files needed to mirror all filesystems on root spindle.
#  A new partition table for each spindle
#  Input for the vinum create command to create Vinum objects on each spindle
#  A copy of fstab mounting Vinum volumes instead of BSD partitions
#  Copies of fstab altered for server's degraded modes of operation
# See handbook for instructions on how to use the files generated.
# N.B. This bootstrapping method shrinks size of swap partition by the size
# of Vinum's on-disk configuration (265 sectors).  It embeds existing file
# systems on the root spindle in Vinum objects without having to copy them.
# Thanks to Greg Lehey for suggesting this bootstrapping method.
# Expectations:
#  The root spindle must contain at least root, swap, and /usr partitions
#  The rootback spindle must have matching /rootback and swap partitions
#  Other spindles should only have a /NOFUTURE* filesystem and maybe swap
#  File systems named /NOFUTURE* will be replaced with Vinum drives

# Change configuration variables below to suit your taste
my $vip = 'h';                                  # VInum Partition
my @drv = ('YouCrazy', 'UpWindow', 'ThruBank',  # Vinum DRiVe names
  'OutSnakes', 'MeWild', 'InMovie', 'HomeJames', 'DownPrices', 'WhileBlind');
# No configuration variables beyond this point

my %vols;               # One entry per Vinum volume to be created
my @spndl;              # One entry per SPiNDLe
my $rsp;                # Root SPindle (as in /dev/$rsp)
my $rbsp;               # RootBack SPindle (as in /dev/$rbsp)
my $cfgsiz = 265;       # Size of Vinum on-disk configuration info in sectors
my $nxtpas = 2;         # Next fsck pass number for non-root filesystems

# Parse fstab, generating the version we'll need for Vinum and noting
# spindles in use.
my $fsin = "/etc/fstab";
#my $fsin = "simu/fstab";
open(FSIN, "$fsin") || die("Couldn't open $fsin: $!\n");

my $fsout = "/etc/fstab.vinum";
open(FSOUT, ">$fsout") || die("Couldn't open $fsout for writing: $!\n");

while (<FSIN>) {
  my ($dev, $mnt, $fstyp, $opt, $dump, $pass) = split;
  next if $dev =~ /^#/;
  if ($mnt eq '/' || $mnt eq '/rootback' || $mnt =~ /^\/NOFUTURE/) {
    my $dn = substr($dev, 5, length($dev)-6);   # Device Name without /dev/
    push(@spndl, $dn) unless grep($_ eq $dn, @spndl);
    $rsp = $dn if $mnt eq '/';
    next if $mnt =~ /^\/NOFUTURE/;
  # Move /rootback from partition e to a
  if ($mnt =~ /^\/rootback/) {
    $dev =~ s/e$/a/;
    $pass = 1;
    $rbsp = substr($dev, 5, length($dev)-6);
    print FSOUT "$dev\t\t$mnt\t$fstyp\t$opt\t\t$dump\t$pass\n";
  # Move non-root filesystems on smallest spindle into Vinum
  if (defined($rsp) && $dev =~ /^\/dev\/$rsp/ && $dev =~ /[d-h]$/) {
    $pass = $nxtpas++;
    print FSOUT "/dev/vinum$mnt\t\t$mnt\t\t$fstyp\t$opt\t\t$dump\t$pass\n";
    $vols{$dev}->{mnt} = substr($mnt, 1);
  print FSOUT $_;
die("Found more spindles than we have abstract names\n") if $#spndl > $#drv;
die("Didn't find a root partition!\n") if !defined($rsp);
die("Didn't find a /rootback partition!\n") if !defined($rbsp);

# Table of server's Degraded Modes
# One row per mode with hash keys
#   fn  FileName
#   xpr eXPRession needed to convert fstab lines for this mode
#   cm1 CoMment 1 describing this mode
#   cm2 CoMment 2 describing this mode
#   FH  FileHandle (dynamically initialized below)
my @DM = (
  { cm1 => "When we only have $rsp, comment out lines using $rbsp",
    fn  => "/etc/fstab_only_have_$rsp",
    xpr => "s:^/dev/$rbsp:#\$&:",
  { cm1 => "When we only have $rbsp, comment out lines using $rsp and",
    cm2 => "rootback becomes root",
    fn  => "/etc/fstab_only_have_$rbsp",
    xpr => "s:^/dev/$rsp:#\$&: || s:/rootback:/\t:",
  { cm1 => "When only $rsp root is bad, /rootback becomes root and",
    cm2 => "root becomes /rootbad",
    fn  => "/etc/fstab_${rsp}_root_bad",
    xpr => "s:\t/\t:\t/rootbad: || s:/rootback:/\t:",

# Initialize output FileHandles and write comments
foreach my $dm (@DM) {
  my $fh = new FileHandle;
  $fh->open(">$dm->{fn}") || die("Can't write $dm->{fn}: $!\n");
  print $fh "# $dm->{cm1}\n" if $dm->{cm1};
  print $fh "# $dm->{cm2}\n" if $dm->{cm2};
  $dm->{FH} = $fh;

# Parse the Vinum version of fstab written above and write versions needed
# for server's degraded modes.
open(FSOUT, "$fsout") || die("Couldn't open $fsout: $!\n");
while (<FSOUT>) {
  my $line = $_;
  foreach my $dm (@DM) {
    $_ = $line;
    eval $dm->{xpr};
    print {$dm->{FH}} $_;

# Parse partition table for each spindle and write versions needed for Vinum
my $rootsiz;    # ROOT partition SIZe
my $swapsiz;    # SWAP partition SIZe
my $rspminoff;  # Root SPindle MINimum OFFset of non-root, non-swap, non-c parts
my $rspsiz;     # Root SPindle SIZe
my $rbspsiz;    # RootBack SPindle SIZe
foreach my $i (0..$#spndl) {
  my $dlin = "disklabel $spndl[$i] |";
#  my $dlin = "simu/disklabel.$spndl[$i]";
  open(DLIN, "$dlin") || die("Couldn't open $dlin: $!\n");

  my $dlout = "disklabel.$spndl[$i]";
  open(DLOUT, ">$dlout") || die("Couldn't open $dlout for writing: $!\n");

  my $dlb4 = "$dlout.b4vinum";
  open(DLB4, ">$dlb4") || die("Couldn't open $dlb4 for writing: $!\n");

  my $minoff;           # MINimum OFFset of non-root, non-swap, non-c partitions
  my $totsiz = 0;       # TOTal SIZe of all non-root, non-swap, non-c partitions
  my $swapspndl = 0;    # True if SWAP partition on this SPiNDLe
  while (<DLIN>) {
    print DLB4 $_;
    my ($part, $siz, $off, $fstyp, $fsiz, $bsiz, $bps) = split;

    if ($part && $part eq 'a:' && $spndl[$i] eq $rsp) {
        $rootsiz = $siz;
    if ($part && $part eq 'e:' && $spndl[$i] eq $rbsp) {
      if ($rootsiz != $siz) {
        die("Rootback size ($siz) != root size ($rootsiz)\n");
    if ($part && $part eq 'c:') {
      $rspsiz  = $siz if $spndl[$i] eq $rsp;
      $rbspsiz = $siz if $spndl[$i] eq $rbsp;
    # Make swap partition $cfgsiz sectors smaller
    if ($part && $part eq 'b:') {
      if ($spndl[$i] eq $rsp) {
        $swapsiz = $siz;
      } else {
        if ($swapsiz != $siz) {
          die("Swap partition sizes unequal across spindles\n");
      printf DLOUT "%4s%9d%9d%10s\n", $part, $siz-$cfgsiz, $off, $fstyp;
      $swapspndl = 1;
    # Move rootback spindle e partitions to a
    if ($part && $part eq 'e:' && $spndl[$i] eq $rbsp) {
      printf DLOUT "%4s%9d%9d%10s%9d%6d%6d\n", 'a:', $siz, $off, $fstyp,
        $fsiz, $bsiz, $bps;
    # Delete non-root, non-swap, non-c partitions but note their minimum
    # offset and total size that're needed below.
    if ($part && $part =~ /^[d-h]:$/) {
      $minoff = $off unless $minoff;
      $minoff = $off if $off < $minoff;
      $totsiz += $siz;
      if ($spndl[$i] eq $rsp) { # If doing spindle containing root
        my $dev = "/dev/$spndl[$i]" . substr($part, 0, 1);
        $vols{$dev}->{siz} = $siz;
        $vols{$dev}->{off} = $off;
        $rspminoff = $minoff;
    print DLOUT $_;
  if ($swapspndl) {     # If there was a swap partition on this spindle
    # Make a Vinum partition the size of all non-root, non-swap,
    # non-c partitions + the size of Vinum's on-disk configuration.
    # Set its offset so that the start of the first subdisk it contains
    # coincides with the first filesystem we're embedding in Vinum.
    printf DLOUT "%4s%9d%9d%10s\n", "$vip:", $totsiz+$cfgsiz, $minoff-$cfgsiz,
  } else {
    # No need to mess with size size and offset if there was no swap
    printf DLOUT "%4s%9d%9d%10s\n", "$vip:", $totsiz, $minoff,
die("Swap partition not found\n") unless $swapsiz;
die("Swap partition not larger than $cfgsiz blocks\n") unless $swapsiz>$cfgsiz;
die("Rootback spindle size not >= root spindle size\n") unless $rbspsiz>=$rspsiz;

# Generate input to vinum create command needed for each spindle.
foreach my $i (0..$#spndl) {
  my $cfn = "create.$drv[$i]";  # Create File Name
  open(CF, ">$cfn") || die("Can't open $cfn for writing: $!\n");
  print CF "drive $drv[$i] device /dev/$spndl[$i]$vip\n";
  next unless $spndl[$i] eq $rsp || $spndl[$i] eq $rbsp;
  foreach my $dev (keys(%vols)) {
    my $mnt = $vols{$dev}->{mnt};
    my $siz = $vols{$dev}->{siz};
    my $off = $vols{$dev}->{off}-$rspminoff+$cfgsiz;
    print CF "volume $mnt\n" if $spndl[$i] eq $rsp;
    print CF <<EOF;
  plex name $mnt.p$i org concat volume $mnt
    sd name $mnt.p$i.s0 drive $drv[$i] plex $mnt.p$i len ${siz}s driveoffset ${off}s