#!/usr/local/bin/perl -w
# $Aero: util/diskprep/diskprep.pl,v 1.8 2006/01/11 22:27:08 brooks Exp $

#
# Simple program to prepare an entire disk, safely, to be a FreeBSD volume.
# Derived from phk's prep.fla.sh, but converted to perl
#
# Copyright (c) 2000 M. Warner Losh.  All Rights Reserved.
#
# "THE BEER-WARE LICENSE" (Revision 42): (stolen shamelessly from phk)
# <imp@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
# can do whatever you want with this stuff. If we meet some day, and you think
# this stuff is worth it, you can buy me a beer in return.   Warner Losh.
#

use POSIX qw(floor);

# make sure that we're root.
sub check_root {
    die "You are not root!\n" if $<;
}

# set variables that can be overriden.  They are overriden in files read
# in by -config
sub variables {
    $cyl = -1;
    $hd = -1;
    $sec = -1;

    $start = -1;
    $size = -1;

    $fsize = 512;
    $bsize = 4096;
    $cpg = 16;

    $all_part = 'c';

    $fbsd_type = 165;

    $minfree = 8;
    $opt_perf = "time";

    $label = "";

    $slices{1}{active} = 1;
    $slices{1}{size} = "*";
    $slices{1}{type} = $fbsd_type;
    $slices{1}{a}{size} = "*";
    $slices{1}{a}{type} = "4.2BSD";

    @slice_list = (1, 2, 3, 4);
    @parts = ("a", "b", "c", "d", "e", "f", "g", "h");
}

# And how do I use this again?
sub usage {
    warn <<USAGE;
usage: diskprep [-config fn] disk
USAGE
    exit 1;
}

# Parse the command line
sub parse {
    local (@argv) = @_;

    while ($_ = $argv[0], /^-/) {
	shift @argv;
	last if /^--$/;
	if    (/^--?(c|config)$/)	{ do $argv[0]; shift @argv }
	elsif    (/^--?(l|label)$/)	{ $label = $argv[0]; shift @argv }
	else				{ &usage }
    }
    &usage if $#argv < 0;
    $drive = $argv[0];
}

# If specified, apply a label to the disk, otherwise detroy any there.
sub label_disk {
	my $dev = "/dev/${drive}";
	if ($label ne "") {
		system "glabel label $label $dev";
		$drive = "label/$label"
	} else {
		system "glabel clear $dev"
	}
}

# Nuke the disk and ask fdisk about the parameters
#   we go the extra mile and dd zeros on the the first 64k.  When we don't
#   do this, we cannot actually boot the disk.
sub get_disk_params {
    local($junk);

    # Clear the drive so we can figure out how big it is.
    $dev = "/dev/${drive}";
    system "/bin/dd if=/dev/zero of=$dev count=128 > /dev/null 2>&1";
    system "/sbin/fdisk -I $drive > /dev/null 2>&1";
    exit ($?) if $?;

    open FD, "/sbin/fdisk -s $drive |" || exit(1);
    $_ = <FD>;
    chop;
    ($junk, $cyl, $junk, $hd, $junk, $sec, $junk) = split;
    $_ = <FD>;
    chop;
    $_ = <FD>;
    chop;
    ($junk, $start, $size, $junk, $junk) = split;
    close FD;
}

# Inititalize the disk
sub init_fdisk {
    local ($dev);

    foreach $s (@slice_list) {
	next unless(defined($slices{$s}));
	if ($slices{$s}{size} eq "*") {
	    $hog_slice = $s;
	    last;
	}
    }
    print "===> Initalizing slices\n";
    if (defined($hog_slice)) {
	print "------> Hog slice is $hog_slice.\n";
    } else {
	print "------> No hog slice given, hope that's right\n";
    }

    open FD, "| /sbin/fdisk -i -f - $drive" || exit(1);
    $hog_size = $size;
    foreach $s (@slice_list) {
	next if ($s eq $hog_slice);
	$hog_size -= $slices{$s}{size} if (defined($slices{$s}));
    }
    $slices{$hog_slice}{size} = $hog_size;

    # Calc the slices
    $off = $start;
    foreach $s (@slice_list) {
	if (defined($slices{$s})) {
	    $slices{$s}{offset} = $off;
	    $off += $slices{$s}{size};
	}
    }

    # dump the slices
    foreach $s (@slice_list) {
	if (defined($slices{$s})) {
#	    print  "p $s $slices{$s}{type} $slices{$s}{offset} $slices{$s}{size}\n";
	    print FD "p $s $slices{$s}{type} $slices{$s}{offset} $slices{$s}{size}\n";
#	    print "a $s\n" if (defined($slices{$s}{active}) && $slices{$s}{active});
	    print FD "a $s\n" if (defined($slices{$s}{active}) && $slices{$s}{active});
	}
    }
    close FD;
}

sub update_sizes {
	print "===> Getting adjusting sizes\n";
	open FD, "/sbin/fdisk -s $drive |" || exit(1);
	$_ = <FD>;
	chop;
	($junk, $cyl, $junk, $hd, $junk, $sec, $junk) = split;
	$_ = <FD>;
	chop;
	while(<FD>) {
		chop;
		($slice, $start, $size, $junk, $junk) = split;
		$slice =~ s/(\d):/$1/;
		$slices{$slice}{size} = $size;
		$slices{$slice}{cyl} = floor($size/($hd*$sec));
		print "------> slice $slice is $size sectors\n";
	}
	close FD;
}

sub init_slices {
	foreach $s (@slice_list) {
		next unless(defined($slices{$s}));

		print "===> initializing slice $s\n";
		if ($slices{$s}{type} == $fbsd_type and !defined($slices{$s}{action})) {
			init_disklabel($s);
			next;
		}
		if (defined($slices{$s}{action})) {
			$slices{$s}{action} =~ s%__DEVICE__%/dev/${drive}s$s%g;
			system $slices{$s}{action};
		}
	}
}

# Fake up a disklabel based on what we know about the drive.
sub init_disklabel {
    my $s = shift;
    local ($off);

    foreach $p (@parts) {
	next unless(defined($slices{$s}{$p}));
	if ($slices{$s}{$p}{size} eq "*") {
	    $hog_part = $p;
	    last;
	}
    }
    if (defined($hog_part)) {
	print "------> Hog partition is '$hog_part'\n";
    } else {
	print "------> No hog partition given, hope that's right\n";
    }

    open DL, "| /sbin/disklabel -R -r ${drive}s$s /dev/stdin";
    print DL "# $drive\n";
    $_ = $drive;
    if    (/^fla/)	{ print DL "type: DOC2K\n"; }
    elsif (/^(ad|wd)/)	{ print DL "type: ESDI\n"; }
    elsif (/^(da)/)	{ print DL "type: SCSI\n"; }
    else		{ print DL "type: unknown\n"; }
    print DL "disk: $drive$s\n";
    print DL "label: diskprep\n";
    print DL "flags:\n";
    print DL "bytes/sector: 512\n";
    print DL "sectors/track: $sec\n";
    print DL "tracks/cylinder: $hd\n";
    print DL "sectors/cylinder: ", $sec*$hd, "\n";
    print DL "cylinders: $slices{$s}{cyl}\n";
    print DL "sectors/unit: $slices{$s}{size}\n";
    print DL "rpm: 42000\n";
    print DL "interleave: 1\n";
    print DL "trackskew: 0\n";
    print DL "cylinderskew: 0\n";
    print DL "headswitch: 0\n";
    print DL "track-to-track seek: 0\n";
    print DL "drivedata: 0\n";
    print DL "\n";
    print DL "8 partitions:\n";
    
    # Calc the hog size
    $hog_size = $slices{$s}{size};
    foreach $p (@parts) {
	next if ($p eq $hog_part); 
	next if ($p eq $all_part); 
	$hog_size -= $slices{$s}{$p}{size} if (defined($slices{$s}{$p}));
    }
    $slices{$s}{$hog_part}{size} = $hog_size;

    # Calc 'c' partition.  It is special.  It is the whole slice
    $slices{$s}{c}{size} = $slices{$s}{size};
    $slices{$s}{c}{offset} = 0;
    $slices{$s}{c}{type} = "unused";

    # Calc the rest of the partitions
    $off = 0;
    foreach $p (@parts) {
	next if ($p eq $all_part); 
	if (defined($slices{$s}{$p})) {
	    $slices{$s}{$p}{offset} = $off;
	    $off += $slices{$s}{$p}{size};
	}
    }

    # Dump the partitions
    foreach $p (@parts) {
	if (defined($slices{$s}{$p})) {
#	    print "$p: $slices{$s}{$p}{size} $slices{$s}{$p}{offset} $slices{$s}{$p}{type} $fsize $bsize $cpg\n";
	    print DL "$p: $slices{$s}{$p}{size} $slices{$s}{$p}{offset} $slices{$s}{$p}{type} $fsize $bsize $cpg\n";
	}
    }
    close DL;

    # Label the disk!
    system "/sbin/disklabel -B -r $drive > /dev/null 2>&1";
}

# Ah, what the heck, go ahead and newfs the partitions.
sub newfs {
    local($d);
    my $l;

    foreach $s (@slice_list) {
    next unless (defined($slices{$s}) && $slices{$s}{type} == $fbsd_type);
	foreach $p (@parts) {
	    next if ($p eq $all_part); 
	    next unless (defined($slices{$s}{$p}));
	    $d = "/dev/${drive}s$s$p";
	    if ($slices{$s}{$p}{type} eq "4.2BSD") {
		system "/sbin/newfs -U $d";
		system "/sbin/tunefs -o $opt_perf -m $minfree $d > /dev/null 2>&1";
		if (defined($slices{$s}{$p}{label})) {
		    $l = $slices{$s}{$p}{"label"};
		    system "/sbin/tunefs -L $l $d > /dev/null 2>&1";
		}
	    } elsif (defined($slices{$s}{$p}{"label"})) {
		$l = $slices{$s}{$p}{"label"};
		system "glabel label $l $d";
	    }
	}
    }
}

# Start here
&check_root;
&variables;
&parse(@ARGV);
&label_disk;
&get_disk_params;
&init_fdisk;
&update_sizes;
&init_slices;
&newfs;


syntax highlighted by Code2HTML, v. 0.9.1