mkextrafs.pl 11.1 KB
Newer Older
1 2 3
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
4
# Copyright (c) 2000-2012 University of Utah and the Flux Group.
5 6 7 8 9 10 11 12
# All rights reserved.
#
use English;
use Getopt::Std;
use Fcntl;
use IO::Handle;
use Socket;

13 14 15 16
# Drag in path stuff so we can find emulab stuff.
BEGIN { require "/etc/emulab/paths.pm"; import emulabpaths; }
my $DOSTYPE = "$BINDIR/dostype";

17 18 19 20 21 22
#
# This file goes in boss:/z/testbed/distributions on boss so that is can
# be passed over via wget to the CDROM on widearea nodes.
#
sub usage()
{
Mike Hibler's avatar
Mike Hibler committed
23
    print("Usage: mkextrafs.pl [-2fl] [-s slice] [-r disk] <mountpoint>\n");
24 25 26 27 28
    print("       [-r disk]  disk device to use (default: ad0)\n");
    print("       [-s slice] DOS partition to use or 0 for entire disk (default: 4)\n");
    print("       <mount>    where the new filesystem will be mounted\n");
    print("       -f         DANGEROUS! force creation of the filesystem\n");
    print("                   even if partition is in use\n");
Mike Hibler's avatar
Mike Hibler committed
29 30
    print("       -2         hack: break DOS partition into two unequal (70/30)\n");
    print("                   BSD partitions and mount the first\n");
31
    print("       -m         do everything but do not mount filesystem\n");
32 33 34
    print("Usage: mkextrafs.pl -l [-f]\n");
    print("       -l  list available partitions\n");
    print("       -f  also list inactive partitions that have non-zero type\n");
35 36
    exit(-1);
}
37
my $optlist    = "2fls:r:cnm";
38 39 40 41
my $diskopt;
my $checkit    = 0;
my $forceit    = 0;
my $noinit     = 0;
42
my $nomount    = 0;
43
my $showonly   = 0;
Mike Hibler's avatar
Mike Hibler committed
44
my $twoparts   = 0;
45
my $debug      = 0;
Mike Hibler's avatar
Mike Hibler committed
46 47 48 49 50 51 52

#
# Yep, hardwired for now.  Should be options or queried via TMCC.
#
my $disk       = "ad0";
my $slice      = "4";
my $partition  = "e";
53

54
sub mysystem($);
55 56
sub showspace($);
sub os_find_freedisk($$); # XXX from liblocsetup.pm
57

58 59 60 61 62 63 64 65 66 67
#
# Turn off line buffering on output
#
STDOUT->autoflush(1);
STDERR->autoflush(1);

#
# Untaint the environment.
# 
$ENV{'PATH'} = "/tmp:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:".
68
    "/usr/local/bin:/usr/site/bin:/usr/site/sbin:/usr/local/etc/emulab";
69 70
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

Mike Hibler's avatar
Mike Hibler committed
71
#
72
# Determine if we should use the "geom" tools to make everything happen.
Mike Hibler's avatar
Mike Hibler committed
73 74
# Empirically, this seems to only be needed for FreeBSD 8 and above.
#
75
my $usegeom = 0;
Mike Hibler's avatar
Mike Hibler committed
76
if (`uname -r` =~ /^(\d+)\./ && $1 > 7) {
77
    $usegeom = $1;
Mike Hibler's avatar
Mike Hibler committed
78 79
}

80 81 82 83 84 85 86 87
#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
Mike Hibler's avatar
Mike Hibler committed
88 89 90
if (defined($options{"f"})) {
    $forceit = 1;
}
91 92 93
if (defined($options{"s"})) {
    $slice = $options{"s"};
}
94 95 96 97 98 99 100 101 102
if (defined($options{"c"})) {
    $checkit = 1;
}
if (defined($options{"r"})) {
    $diskopt = $options{"r"};
}
if (defined($options{"n"})) {
    $noinit = $options{"n"};
}
103 104 105
if (defined($options{"m"})) {
    $nomount = $options{"m"};
}
106 107 108
if (defined($options{"l"})) {
    $showonly = 1;
}
Mike Hibler's avatar
Mike Hibler committed
109 110 111
if (defined($options{"2"})) {
    $twoparts = 1;
}
112 113 114 115 116 117

if ($showonly) {
    showspace($forceit);
    exit(0);
}

118 119 120 121 122 123 124 125 126 127
if (@ARGV != 1) {
    usage();
}
my $mountpoint  = $ARGV[0];

if (! -d $mountpoint) {
    die("*** $0:\n".
	"    $mountpoint does not exist!\n");
}

128
#
129
# XXX determine the disk based on the root fs if not provided.
130
#
131 132
if (defined($diskopt)) {
    $disk = $diskopt;
133
    $disk =~ s/^\/dev\///;
134 135 136 137 138 139
}
else {
    my $rootdev = `df | egrep '/\$'`;
    if ($rootdev =~ /^\/dev\/([a-z]+\d+)s[1-4][a-h]/) {
	$disk = $1;
    }
140 141
}

Mike Hibler's avatar
Mike Hibler committed
142 143 144
my $slicedev   = "${disk}s${slice}";
my $fsdevice   = "/dev/${slicedev}${partition}";

145 146 147 148 149 150 151 152 153
#
# For entire disk, we will use "fdisk -I" to initialize DOS partition 1.
# Note: we will create the BSD 'e' partition later.
#
if ($slice == 0) {
    $slicedev = "${disk}s1";
    $fsdevice = "/dev/${slicedev}e";
}

154
#
Mike Hibler's avatar
Mike Hibler committed
155 156
# An existing fstab entry indicates we have already done this
# XXX override with forceit?  Would require unmounting and removing from fstab.
157 158
#
if (!system("egrep -q -s '^${fsdevice}' /etc/fstab")) {
159
    if ($checkit) {
160
	exit(0);
161
    }
162
    die("*** $0:\n".
Mike Hibler's avatar
Mike Hibler committed
163 164
	"    There is already an entry in /etc/fstab for $fsdevice\n");
}
165
elsif ($checkit) {
166
    exit(1);
167
}
Mike Hibler's avatar
Mike Hibler committed
168 169 170 171 172 173 174 175

#
# Likewise, if already mounted somewhere, fail
#
my $mounted = `mount | egrep '^${fsdevice}'`;
if ($mounted =~ /^${fsdevice} on (\S*)/) {
    die("*** $0:\n".
	"    $fsdevice is already mounted on $1\n");
176
}
Mike Hibler's avatar
Mike Hibler committed
177

178 179 180 181
#
# See what the current type is for the partition
#
my $stype = -1;
182
if (!open(FD, "fdisk -s /dev/$disk 2>&1|")) {
183 184
    die("*** $0:\n".
        "    $disk: could not get partition info\n");
185
}
186 187 188 189 190 191 192 193 194 195 196
while (<FD>) {
    #
    # No MBR should mean that the entire disk is unused.
    # If they want to use the whole disk (slice==0), this is okay.
    #
    if (/invalid fdisk partition table found/) {
	if ($slice != 0) {
	    die("*** $0:\n".
		"    $disk: no partition table!\n");
	}
	$stype = 0;
Leigh B Stoller's avatar
Leigh B Stoller committed
197
	next;
198 199 200 201 202 203 204 205 206 207 208
    }
    #
    # Format of fdisk output is:
    #  /dev/da0: 30394 cyl 255 hd 63 sec
    #  Part        Start Size     Type Flags
    #  1:          63    12305790 0xa5 0x80
    #  ...
    #
    if (/^\s*([1-4]):\s+\d+\s+\d+\s+(0x\S\S)\s+/) {
	my $part = $1;
	my $type = hex($2);
209

210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
	#
	# If there is a valid partition on the disk and they are
	# using the entire disk without forcing, stop them!
	#
	if ($slice == 0) {
	    if ($type != 0 && !$forceit) {
		die("*** $0:\n".
		    "    There are valid partitions on the disk;".
		    " need to specify -f to overwrite entire disk!\n");
	    }
	    $stype = 0;
	}
	if ($slice == $part) {
	    $stype = $type;
	    last;
	}
    }
}	    
close(FD);

if ($stype == -1) {
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
    #
    # XXX if we are in the GEOM world and we do not find the 4th partition
    # it may be because a previous gpart call noticed the unused partition
    # and deleted it. This happens in elabinelab where we first repurpose
    # the second (Linux) partition.
    #
    # If this happens, we just try re-creating the 4th partition. gpart
    # will "do the right thing" in terms of offset and size.
    #
    if ($usegeom && $slice == 4) {
	$stype = 0;
    } else {
	die("*** $0:\n".
	    "    Could not find partition $slice fdisk entry!\n");
    }
246
}
Mike Hibler's avatar
Mike Hibler committed
247 248 249 250 251 252 253 254 255 256 257 258 259 260

#
# Fail if not forcing and the partition type is non-zero.
#
if (!$forceit) {
    if ($stype != 0) {
	die("*** $0:\n".
	    "    non-zero partition type ($stype) for /dev/${slicedev}, ".
	    "use -f to override\n");
    }
} elsif ($stype && $stype != 165) {
    warn("*** $0: WARNING: changing partition type from $stype to 165\n");
}

Mike Hibler's avatar
Mike Hibler committed
261 262 263
#
# Dark magic to allow us to modify the open boot disk
#
264 265 266 267 268 269
if ($usegeom && $slice != 0) {
    if ($stype != 0) {
	mysystem("gpart delete -i $slice $disk");
    }
    mysystem("gpart add -i $slice -t freebsd $disk");
    $stype = 165;
Mike Hibler's avatar
Mike Hibler committed
270 271
}

Mike Hibler's avatar
Mike Hibler committed
272
#
273 274 275 276
# If they are whacking the whole disk, just use "fdisk -I' to initialize
# partition 1 of the disk.
#
if ($slice == 0 && !$noinit) {
277
    mysystem("fdisk -I /dev/$disk");
278
}
Mike Hibler's avatar
Mike Hibler committed
279
#
280 281 282
# Otherwise set the partition type to BSD if not already set.
#
elsif ($stype != 165) {
283
    die("*** $0:\n".
284 285 286
	"    No $DOSTYPE program, cannot set type of DOS partition\n")
	if (! -e "$DOSTYPE");
    mysystem("$DOSTYPE -f /dev/$disk $slice 165");
287
}
288 289 290
#
# If not recreating the filesystems, just try to mount it
#
291 292 293 294 295
elsif ($noinit) {
    mysystem("mount $fsdevice $mountpoint");
    mysystem("echo \"$fsdevice $mountpoint ufs rw 0 2\" >> /etc/fstab");
    exit(0);
}
296 297 298 299

#
# Now create the disklabel
#
300
my $tmpfile = "/tmp/disklabel";
Mike Hibler's avatar
Mike Hibler committed
301
mysystem("disklabel -w -r $slicedev auto");
302
mysystem("disklabel -r $slicedev > $tmpfile");
303

304 305 306 307 308
#
# Tweak the disk label to ensure there is an 'e' partition
# In FreeBSD 4.x, we just add one.
# In FreeBSD 5.x, we replace the 'a' partition which is created by default.
#
Mike Hibler's avatar
Mike Hibler committed
309 310 311 312
# In "two part" mode, break into two partitions 'e' (70%) and 'f' (30%)
# and use the first.  This is primarily for elabinelab use, where we need
# a smaller /share that is not on the same FS as /proj and /users.
#
313
open(DL, "+<$tmpfile") or
314
    die("*** $0:\n".
315 316 317
	"    Could not edit temporary label in $tmpfile!\n");
my @dl = <DL>;
if (scalar(grep(/^  ${partition}: /, @dl)) != 0) {
318
    die("*** $0:\n".
Mike Hibler's avatar
Mike Hibler committed
319
        "    Already an \'$partition\' partition?!\n");
320
}
321 322 323 324
seek(DL, 0, 0);
truncate(DL, 0);

my $done = 0;
Mike Hibler's avatar
Mike Hibler committed
325 326 327
my $fsize = 0;
my $foff = 0;
my $bad = 0;
328 329
foreach my $line (@dl) {
    # we assume that if the 'a' paritition exists, it is the correct one
Mike Hibler's avatar
Mike Hibler committed
330 331 332 333
    if (!$done && $line =~ /^\s+a:\s+(\d+)\s+(\d+)(.*)$/) {
	my $size = $1;
	my $off = $2;
	my $rest = $3;
334
	$rest =~ s/unused/4.2BSD/;
Mike Hibler's avatar
Mike Hibler committed
335 336 337 338 339 340 341 342
	if ($twoparts) {
	    my $esize = int($size / 16 * 0.7) * 16;
	    $foff = $off + $esize;
	    $fsize = $size - $esize;
	    $line = "  e: $esize $off $rest";
	} else {
	    $line = "  e: $size $off $rest";
	}
343 344
	$done = 1;
    }
Mike Hibler's avatar
Mike Hibler committed
345 346 347 348 349 350
    # otherwise we use the 'c' partition as our model
    if (!$done && $line =~ /^\s+c:\s+(\d+)\s+(\d+)(.*)$/) {
        print DL $line;
	my $size = $1;
	my $off = $2;
	my $rest = $3;
351
	$rest =~ s/unused/4.2BSD/;
Mike Hibler's avatar
Mike Hibler committed
352 353 354 355 356 357 358 359
	if ($twoparts) {
	    my $esize = int($size / 16 * 0.7) * 16;
	    $foff = $off + $esize;
	    $fsize = $size - $esize;
	    $line = "  e: $esize $off $rest";
	} else {
	    $line = "  e: $size $off $rest";
	}
360 361
	$done = 1;
    }
Mike Hibler's avatar
Mike Hibler committed
362 363 364
    if ($line =~ /^  f:/) {
	$bad = 1;
    }
365 366
    print DL $line;
}
Mike Hibler's avatar
Mike Hibler committed
367
if (!$bad && $twoparts && $fsize > 0) {
368
    print DL "  f: $fsize $foff 4.2BSD 0 0\n";
Mike Hibler's avatar
Mike Hibler committed
369
}
370 371 372 373
close(DL);
mysystem("disklabel -R -r $slicedev $tmpfile");
unlink($tmpfile);

374 375 376 377
# FreeBSD 5 doesn't have MAKEDEV
mysystem("cd /dev; ./MAKEDEV ${slicedev}c")
    if (-e "/dev/MAKEDEV");

Mike Hibler's avatar
Mike Hibler committed
378
mysystem("newfs -U -i 25000 $fsdevice");
379 380
mysystem("echo \"$fsdevice $mountpoint ufs rw 0 2\" >> /etc/fstab");

381 382 383 384
if (!$nomount) {
    mysystem("mount $mountpoint");
    mysystem("mkdir $mountpoint/local");
}
385
exit(0);
386 387 388 389 390 391 392 393 394 395

sub mysystem($)
{
    my ($command) = @_;

    if (0) {
	print "'$command'\n";
    }
    else {
	print "'$command'\n";
396 397
	my $rv = system($command);
	if ($rv) {
398
	    die("*** $0:\n".
399
		"    Failed ($rv): '$command'\n");
400 401 402 403
	}
    }
    return 0
}
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463

sub sizestr($)
{
    my ($bytes) = @_;
    my ($divisor, $units);

    if ($bytes > 1000 * 1000 * 1000 * 1000) {
	$divisor = 1000 * 1000 * 1000 * 1000;
	$units = "TB";
    } elsif ($bytes > 1000 * 1000 * 1000) {
	$divisor = 1000 * 1000 * 1000;
	$units = "GB";
    } else {
	$divisor = 1000 * 1000;
	$units = "MB";
    }
    return sprintf "%6.2f%s", $bytes / $divisor, $units;
}

sub showspace($)
{
    my ($force) = @_;

    # XXX we keep this dynamic since this script can operate standalone
    require liblocsetup;

    my %diskinfo = liblocsetup::os_find_freedisk(0, $force);
    if (!%diskinfo) {
	print "No space found on node\n";
    }
    print "Found space";
    print " ('*' = in use, but unmounted)"
	if ($force);
    print ":\nDisk\tSlice\tBytes\n";
    foreach my $disk (sort keys %diskinfo) {
	my $ff = "";
	my $iu = "";
	if (exists($diskinfo{$disk}{"size"})) {
	    my $size = sizestr($diskinfo{$disk}{"size"});
	    if ($diskinfo{$disk}{"type"} != 0) {
		$ff = "-f";
		$iu = "*";
	    }
	    print "$disk$iu\t \t$size # mkextrafs -r $disk -s 0 $ff\n";
	} else {
	    foreach my $part (keys %{$diskinfo{$disk}}) {
		my $pnum;
		($pnum = $part) =~ s/.*(\d+)$/$1/;
		if (exists($diskinfo{$disk}{$part}{"size"})) {
		    my $size = sizestr($diskinfo{$disk}{$part}{"size"});
		    if ($diskinfo{$disk}{$part}{"type"} != 0) {
			$ff = "-f";
			$iu = "*";
		    }
		    print "$disk$iu\t$pnum\t$size # mkextrafs -r $disk -s $pnum $ff\n";
		}
	    }
	}
    }
}