while ((my $arg = shift (@kernel_args)) ne '--') {
push (@ARGV, $arg);
}
- GetOptions ("sim=s" => sub { set_sim (@_) },
+ GetOptions ("sim=s" => sub { set_sim ($_[1]) },
"bochs" => sub { set_sim ("bochs") },
"qemu" => sub { set_sim ("qemu") },
"player" => sub { set_sim ("player") },
- "debug=s" => sub { set_debug (@_) },
+ "debug=s" => sub { set_debug ($_[1]) },
"no-debug" => sub { set_debug ("none") },
"monitor" => sub { set_debug ("monitor") },
"gdb" => sub { set_debug ("gdb") },
\f
# Prepare the scratch disk for gets and puts.
sub prepare_scratch_disk {
- # Copy the files to put onto the scratch disk.
- put_scratch_file ($_->[0]) foreach @puts;
+ if (@puts) {
+ # Write ustar header and data for each file.
+ put_scratch_file ($_->[0],
+ defined $_->[1] ? $_->[1] : $_->[0])
+ foreach @puts;
+
+ # Write end-of-archive marker.
+ write_fully ($disks{SCRATCH}{HANDLE}, $disks{SCRATCH}{FILE_NAME},
+ "\0" x 1024);
+ }
# Make sure the scratch disk is big enough to get big files.
extend_disk ($disks{SCRATCH}, @gets * 1024 * 1024) if @gets;
my ($ok) = 1;
foreach my $get (@gets) {
my ($name) = defined ($get->[1]) ? $get->[1] : $get->[0];
- $ok &&= get_scratch_file ($name);
- if (!$ok) {
+ my ($error) = get_scratch_file ($name);
+ if ($error) {
+ print STDERR "getting $name failed ($error)\n";
die "$name: unlink: $!\n" if !unlink ($name) && !$!{ENOENT};
+ $ok = 0;
}
}
}
-# put_scratch_file($file).
+# mk_ustar_field($number, $size)
+#
+# Returns $number in a $size-byte numeric field in the format used by
+# the standard ustar archive header.
+sub mk_ustar_field {
+ my ($number, $size) = @_;
+ my ($len) = $size - 1;
+ my ($out) = sprintf ("%0${len}o", $number) . "\0";
+ die "$number: too large for $size-byte octal ustar field\n"
+ if length ($out) != $size;
+ return $out;
+}
+
+# calc_ustar_chksum($s)
#
-# Copies $file into the scratch disk.
+# Calculates and returns the ustar checksum of 512-byte ustar archive
+# header $s.
+sub calc_ustar_chksum {
+ my ($s) = @_;
+ die if length ($s) != 512;
+ substr ($s, 148, 8, ' ' x 8);
+ return unpack ("%32a*", $s);
+}
+
+# put_scratch_file($src_file_name, $dst_file_name).
+#
+# Copies $src_file_name into the scratch disk for extraction as
+# $dst_file_name.
sub put_scratch_file {
- my ($put_file_name) = @_;
+ my ($src_file_name, $dst_file_name) = @_;
my ($disk_handle, $disk_file_name) = open_disk ($disks{SCRATCH});
- print "Copying $put_file_name into $disk_file_name...\n";
+ print "Copying $src_file_name to scratch partition...\n";
+
+ # ustar format supports up to 100 characters for a file name, and
+ # even longer names given some common properties, but our code in
+ # the Pintos kernel only supports at most 99 characters.
+ die "$dst_file_name: name too long (max 99 characters)\n"
+ if length ($dst_file_name) > 99;
- # Write metadata sector, which consists of a 4-byte signature
- # followed by the file size.
- stat $put_file_name or die "$put_file_name: stat: $!\n";
+ # Compose and write ustar header.
+ stat $src_file_name or die "$src_file_name: stat: $!\n";
my ($size) = -s _;
- my ($metadata) = pack ("a4 V x504", "PUT\0", $size);
- write_fully ($disk_handle, $disk_file_name, $metadata);
+ my ($header) = (pack ("a100", $dst_file_name) # name
+ . mk_ustar_field (0644, 8) # mode
+ . mk_ustar_field (0, 8) # uid
+ . mk_ustar_field (0, 8) # gid
+ . mk_ustar_field ($size, 12) # size
+ . mk_ustar_field (1136102400, 12) # mtime
+ . (' ' x 8) # chksum
+ . '0' # typeflag
+ . ("\0" x 100) # linkname
+ . "ustar\0" # magic
+ . "00" # version
+ . "root" . ("\0" x 28) # uname
+ . "root" . ("\0" x 28) # gname
+ . "\0" x 8 # devmajor
+ . "\0" x 8 # devminor
+ . ("\0" x 155)) # prefix
+ . "\0" x 12; # pad to 512 bytes
+ substr ($header, 148, 8) = mk_ustar_field (calc_ustar_chksum ($header), 8);
+ write_fully ($disk_handle, $disk_file_name, $header);
# Copy file data.
my ($put_handle);
- sysopen ($put_handle, $put_file_name, O_RDONLY)
- or die "$put_file_name: open: $!\n";
- copy_file ($put_handle, $put_file_name, $disk_handle, $disk_file_name,
+ sysopen ($put_handle, $src_file_name, O_RDONLY)
+ or die "$src_file_name: open: $!\n";
+ copy_file ($put_handle, $src_file_name, $disk_handle, $disk_file_name,
$size);
+ die "$src_file_name: changed size while being read\n"
+ if $size != -s $put_handle;
close ($put_handle);
# Round up disk data to beginning of next sector.
print "Copying $get_file_name out of $disk_file_name...\n";
- # Read metadata sector, which has a 4-byte signature followed by
- # the file size.
- my ($metadata) = read_fully ($disk_handle, $disk_file_name, 512);
- my ($signature, $size) = unpack ("a4 V", $metadata);
- (print STDERR "bad signature on scratch disk--did Pintos run fail?\n"),
- return 0
- if $signature ne "GET\0";
+ # Read ustar header sector.
+ my ($header) = read_fully ($disk_handle, $disk_file_name, 512);
+ return "scratch disk tar archive ends unexpectedly"
+ if $header eq ("\0" x 512);
+
+ # Verify magic numbers.
+ return "corrupt ustar signature" if substr ($header, 257, 6) ne "ustar\0";
+ return "invalid ustar version" if substr ($header, 263, 2) ne '00';
+
+ # Verify checksum.
+ my ($chksum) = oct (unpack ("Z*", substr ($header, 148, 8)));
+ my ($correct_chksum) = calc_ustar_chksum ($header);
+ return "checksum mismatch" if $chksum != $correct_chksum;
+
+ # Get type.
+ my ($typeflag) = substr ($header, 156, 1);
+ return "not a regular file" if $typeflag ne '0' && $typeflag ne "\0";
+
+ # Get size.
+ my ($size) = oct (unpack ("Z*", substr ($header, 124, 12)));
+ return "bad size $size\n" if $size < 0;
# Copy file data.
my ($get_handle);
read_fully ($disk_handle, $disk_file_name, 512 - $size % 512)
if $size % 512;
- return 1;
+ return 0;
}
\f
# Prepares the arguments to pass to the Pintos kernel,
my (@args);
push (@args, shift (@kernel_args))
while @kernel_args && $kernel_args[0] =~ /^-/;
- push (@args, 'put', defined $_->[1] ? $_->[1] : $_->[0]) foreach @puts;
+ push (@args, 'extract') if @puts;
push (@args, @kernel_args);
- push (@args, 'get', $_->[0]) foreach @gets;
+ push (@args, 'append', $_->[0]) foreach @gets;
write_cmd_line ($disks{OS}, @args);
}
# Write bochsrc.txt configuration file.
open (BOCHSRC, ">", "bochsrc.txt") or die "bochsrc.txt: create: $!\n";
print BOCHSRC <<EOF;
-romimage: file=\$BXSHARE/BIOS-bochs-latest, address=0xf0000
+romimage: file=\$BXSHARE/BIOS-bochs-latest
vgaromimage: file=\$BXSHARE/VGABIOS-lgpl-latest
boot: disk
cpu: ips=1000000
panic: action=fatal
EOF
print BOCHSRC "gdbstub: enabled=1\n" if $debug eq 'gdb';
- print BOCHSRC "clock: sync=", $realtime ? 'realtime' : 'none',
- ", time0=0\n";
+ if ($realtime) {
+ print BOCHSRC "clock: sync=realtime\n";
+ } else {
+ print BOCHSRC "clock: sync=none, time0=0\n";
+ }
print_bochs_disk_line ("ata0-master", 0);
print_bochs_disk_line ("ata0-slave", 1);
if (defined ($disks_by_iface[2]{FILE_NAME})