Some working release.
[biosautoraid.git] / biosautoraid.pl
1 #! /usr/bin/perl
2 use strict;
3 use warnings;
4 use bytes;
5 use Getopt::Long;
6 use Fcntl qw(SEEK_SET);
7
8
9 sub signature($$$$;$)
10 {
11 my($hay,$hay_filename,$offset,$needle,$msg)=@_;
12
13         die if length($needle)%1;
14         my $len_bytes=length($needle)/2;
15         substr($hay,$offset,$len_bytes) eq pack("H*",$needle)
16                         or die "\"$hay_filename\" error reading signature 0x$needle @".sprintf("0x%X",$offset)
17                                         .(!$msg ? "" : "; $msg")."\n";
18 }
19
20 my $BACKUP_SECTOR_START=63;
21 my $MAX_BOOT_SIZE=0x1b6;        # LILO: leave some space for NT's and DR DOS' dirty
22
23
24 my $bin_filename="./biosautoraid.bin";
25 my $opt_install;
26 my $opt_uninstall;
27 my $options_error=!GetOptions(
28                 "bin=s"      ,\$bin_filename,
29                 "i|install"  ,\$opt_install,
30                 "u|uninstall",\$opt_uninstall,
31                 );
32 local *BIN;
33 open BIN,$bin_filename or die "open \"$bin_filename\": $!";
34 my $bin=do { local $/; <BIN>; } or die "read \"$bin_filename\": $!";
35 close BIN or die "close \"$bin_filename\": $!";
36
37 my $bin_len=length($bin);
38 $bin_len>0 && !($bin_len%0x200) or die "Invalid length of \"$bin_filename\": $bin_len";
39 my $bin_short=($bin_len==0x200);        # => no 0xBEEF
40
41 signature $bin,$bin_filename,0x1FE     ,"55AA";
42 substr($bin,$MAX_BOOT_SIZE,0x1FE-$MAX_BOOT_SIZE)=~/^\x00*$/s or die "No empty magic+partitiontable: $bin_filename";
43
44 if (substr($bin,3,8) eq "BIOSRAID") {
45         signature $bin,$bin_filename,0x03      ,unpack("H*","BIOSRAID");
46         signature $bin,$bin_filename,0x43      ,"5E81A2".sprintf("%X",$BACKUP_SECTOR_START);
47         signature $bin,$bin_filename,$bin_len-2,"BEEF" if !$bin_short;
48         }
49 else {
50         signature $bin,$bin_filename,0x03      ,unpack("H*","BIOSRAI2")
51                         .sprintf("%X%X",$BACKUP_SECTOR_START,$MAX_BOOT_SIZE-0x100);
52         }
53
54 my $all_ok;
55 END {
56         warn "Syntax: $0 [--bin=<biosautoraid.bin>] [{-i|--install|-u|--uninstall} <device>]" if !$all_ok;
57         }
58
59 if (!@ARGV && !$options_error) {
60         print "OK, \"$bin_filename\" checked.\n";
61         $all_ok=1;
62         exit 0;
63         }
64
65 die "Type -i|--install or -u|--uninstall"                     if (!$opt_install && !$opt_uninstall) || $options_error;
66 die "Both -i|--install and -u|--uninstall forbidden together" if   $opt_install &&  $opt_uninstall;
67
68 @ARGV<=1 or die;
69 my $device_filename=$ARGV[0];
70
71 local *DEVICE;
72 open DEVICE,"+<".$device_filename or die "open \"$device_filename\": $!";
73
74 my $backup_offset=$BACKUP_SECTOR_START*0x200;
75
76 sysseek DEVICE,0,SEEK_SET or die "Error seeking $device_filename";
77 my $master;
78 $bin_len==sysread DEVICE,$master,$bin_len or die "read \"$device_filename\": $!";
79 length($master)==$bin_len or die "read \"$device_filename\": ".length($master)."!=$bin_len";
80 signature $master,$device_filename,0x1FE,"55AA";
81
82 sysseek DEVICE,$backup_offset,SEEK_SET or die "Error seeking $device_filename";
83 my $backup;
84 $bin_len==sysread DEVICE,$backup,$bin_len or die "read \"$device_filename\": $!";
85 length($backup)==$bin_len or die "read \"$device_filename\": ".length($backup)."!=$bin_len";
86
87 my $zeroes="\x00"x$bin_len;
88 die if length($zeroes)!=$bin_len;
89
90 $bin=substr($bin,0,$MAX_BOOT_SIZE).substr($master,$MAX_BOOT_SIZE,0x1FE-$MAX_BOOT_SIZE).substr($bin,0x1FE);
91
92 my $installed=substr($master,0x03,7) eq "BIOSRAI";
93 print "Already instaled? ".($installed ? "YES" : "NO")."\n";
94
95 die "Nothing to uninstall!\n" if $opt_uninstall && !$installed;
96
97 if ($installed) {
98         signature $master,$device_filename,$bin_len-2,"BEEF","Different installed SECTOR_LEN?" if !$bin_short;
99         signature $master,$device_filename,0x03      ,unpack("H*","BIOSRAI");
100         signature $master,$device_filename,0x43      ,"5E81A2".sprintf("%X",$BACKUP_SECTOR_START) if !$bin_short;
101         signature $backup,"backup",0x1FE,"55AA";
102         }
103 if ($opt_install) {
104         if (!$installed) {
105                 if ($backup ne $zeroes) {
106                         # Permit different DOS-volume area and 0x1FC..0x1FD bytes.
107                         die "Sensitive bytes at backup sectors" if !(1
108                                 && (0
109                                         || substr($backup,0x200) eq substr($zeroes,0x200)
110                                         || (substr($backup,$bin_len-2) eq pack("H*","BEEF") && !$bin_short)
111                                         )
112                                 && substr($backup,0,0x03) eq substr($master,0,0x03)
113                                 && substr($backup,0x5A,$MAX_BOOT_SIZE-0x5A) eq substr($master,0x5A,$MAX_BOOT_SIZE-0x5A)
114                                 );
115                         warn "WARNING: Dropping obsolete (but similiar) backup! Use --uninstall next time.\n";
116                         }
117                 sysseek DEVICE,$backup_offset,SEEK_SET or die "Error seeking $device_filename";
118                 $bin_len==syswrite DEVICE,$master or die "Error backing up the master";
119                 }
120         sysseek DEVICE,0,SEEK_SET or die "Error seeking $device_filename";
121         $bin_len==syswrite DEVICE,$bin or die "Error writing new master of BIOSautoRAID";
122         }
123 if ($opt_uninstall) {
124         sysseek DEVICE,0,SEEK_SET or die "Error seeking $device_filename";
125         $bin_len==syswrite DEVICE,$backup or die "Error writing backup back to the master";
126         sysseek DEVICE,$backup_offset,SEEK_SET or die "Error seeking $device_filename";
127         $bin_len==syswrite DEVICE,$zeroes or die "Error clearing the backup area";
128         }
129
130 close DEVICE or die "close \"$device_filename\": $!";
131
132 print "OK; ".($opt_install ? ($installed ? "re" : "")."installed" : "uninstalled")."\n";
133 $all_ok=1;