syslinux-3.08-2 sources from FC4
[bootcd.git] / syslinux / mkdiskimage.in
1 #!/usr/bin/perl
2 ## -----------------------------------------------------------------------
3 ##  $Id: mkdiskimage.in,v 1.14 2004/12/30 23:41:01 hpa Exp $
4 ##   
5 ##   Copyright 2002-2004 H. Peter Anvin - All Rights Reserved
6 ##
7 ##   This program is free software; you can redistribute it and/or modify
8 ##   it under the terms of the GNU General Public License as published by
9 ##   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
10 ##   Boston MA 02111-1307, USA; either version 2 of the License, or
11 ##   (at your option) any later version; incorporated herein by reference.
12 ##
13 ## -----------------------------------------------------------------------
14
15 #
16 # Creates a blank MS-DOS formatted hard disk image
17 #
18
19 use bytes;
20 use integer;
21 use Fcntl;
22 use Errno;
23 use Cwd;
24 use IO::Handle;                 # For flush()
25
26 sub absolute_path($) {
27     my($f) = @_;
28     my($c);
29
30     return $f if ( $f =~ /^\// );
31     $c = cwd();
32     $c = '' if ( $c eq '/' );
33     return $c.'/'.$f;
34 }
35
36 sub is_linux() {
37     return !!eval '{ '.
38         'use POSIX; '.
39         '($sysname, $nodename, $release, $version, $machine) = POSIX::uname(); '.
40         "return \$sysname eq \'Linux\'; }";
41 }
42
43
44 $is_linux = is_linux();
45 if ( $is_linux ) {
46     # IOCTL numbers
47     $BLKRRPART    = 0x125f;
48     $BLKGETSIZE   = 0x1260;
49 }
50
51 %opt = ();
52 @args = ();
53
54 for $a ( @ARGV ) {
55     if ( $a =~ /^\-/ ) {
56         foreach $o ( split(//, substr($a,1)) ) {
57             $opt{$o} = 1;
58         }
59     } else {
60         push(@args, $a);
61     }
62 }
63
64 ($file,$c,$h,$s) = @args;
65 $c += 0;  $h += 0;  $s += 0;
66
67 $pentry = 1;
68 $pentry = 2 if ( $opt{'2'} );
69 $pentry = 3 if ( $opt{'3'} );
70 $pentry = 4 if ( $opt{'4'} );
71
72 if ( $opt{'M'} ) {
73     # Specify size in megabytes, not in cylinders
74     $c = ($c*1024*2)/($h*$s);
75 }
76
77 $is_open = 0;
78
79 if ( $c == 0 ) {
80     $len = 0;
81     if ( sysopen(OUTPUT, $file, O_RDWR, 0666) ) {
82         $is_open = 1;
83
84         if ( (@filestat = stat(OUTPUT)) && S_ISREG($filestat[2]) ) {
85             $len = $filestat[7] >> 9;
86         } elsif ( $is_linux && S_ISBLK($filestat[2]) ) {
87             $blksize = pack("L!", 0);
88             if ( ioctl(OUTPUT, $BLKGETSIZE, $blksize) == 0 ) {
89                 $len = unpack("L!", $blksize); # In 512-byte sectors!
90             }
91         }
92     }
93
94     if ( !$len ) {
95         print STDERR "$0: $file: don't know how to determine the size of this device\n";
96         exit 1;
97     }
98
99     $c = $len/($h*$s);
100 }
101
102 if ( !$file || $c < 1 || $c > 1024 ||
103      $h < 1 || $h > 256 || $s < 1 || $s > 63 ) {
104     print STDERR "Usage: $0 [-doF4] file c h s (max: 1024 256 63)\n";
105     print STDERR "    -d    add DOSEMU header\n";
106     print STDERR "    -o    print filesystem offset to stdout\n";
107     print STDERR "    -F    format partition as FAT32\n";
108     print STDERR "    -M    \"c\" argument is megabytes, calculate cylinders\n";
109     print STDERR "    -4    use partition entry 4 (standard for zipdisks)\n";
110     exit 1;
111 }
112
113 $cylsize = $h*$s*512;
114
115 if ( !$is_open ) {
116     sysopen(OUTPUT, $file, O_CREAT|O_RDWR|O_TRUNC, 0666)
117         or die "$0: Cannot open: $file\n";
118 }
119 binmode OUTPUT;
120
121 # Print out DOSEMU header, if requested
122 if ( $opt{'d'} ) {
123     $emuhdr = "DOSEMU\0" . pack("VVVV", $h, $s, $c, 128);
124     $emuhdr .= "\0" x (128 - length($emuhdr));
125     print OUTPUT $emuhdr;
126 }
127
128 # Print the MBR and partition table
129 $mbr = '';
130 while ( $line = <DATA> ) {
131     chomp $line;
132     foreach $byte ( split(/\s+/, $line) ) {
133         $mbr .= chr(hex($byte));
134     }
135 }
136 if ( length($mbr) > 446 ) {
137     die "$0: Bad MBR code\n";
138 }
139
140 $mbr .= "\0" x (446 - length($mbr));
141
142 print OUTPUT $mbr;
143
144 # Print partition table
145 $psize = $c*$h*$s-$s;
146 $bhead   = ($h > 1) ? 1 : 0;
147 $bsect   = 1;
148 $bcyl    = ($h > 1) ? 0 : 1;
149 $ehead   = $h-1;
150 $esect   = $s + ((($c-1) & 0x300) >> 2);
151 $ecyl    = ($c-1) & 0xff;
152 if ( $psize > 65536 ) {
153     $fstype = 0x06;
154 } else {
155     $fstype = 0x04;
156 }
157 for ( $i = 1 ; $i <= 4 ; $i++ ) {
158     if ( $i == $pentry ) {
159         print OUTPUT pack("CCCCCCCCVV", 0x80, $bhead, $bsect, $bcyl, $fstype,
160                           $ehead, $esect, $ecyl, $s, $psize);
161     } else {
162         print OUTPUT "\0" x 16;
163     }
164 }
165 print OUTPUT "\x55\xaa";
166
167 # Output blank file
168 $totalsize = $c*$h*$s;
169 $tracks    = $c*$h;
170
171 $track = "\0" x (512*$s);
172
173 # Print fractional track
174 print OUTPUT "\0" x (512 * ($s-1));
175
176 for ( $i = 1 ; $i < $tracks ; $i++ ) {
177     print OUTPUT $track;
178 }
179
180 # Print mtools temp file
181 $n = 0;
182 while ( !defined($tmpdir) ) {
183     $tmpdir = "/tmp/mkdiskimage.$$.".($n++);
184     if ( !mkdir($tmpdir, 0700) ) {
185         die "$0: Failed to make temp directory: $tmpdir\n"
186             if ( $! != EEXIST );
187         undef $tmpdir;
188     }
189 }
190
191 $cfgfile = $tmpdir.'/mtools.conf';
192 $imglink = $tmpdir.'/disk.img';
193 die "$0: Failed to create symlink $imglink\n"
194     if ( !symlink(absolute_path($file), $imglink) );
195
196 $header_size = ($opt{'d'} ? 128 : 0);
197
198 # Start of filesystem
199 $offset = $s*512 + $header_size;
200
201 # Start of partition table entry
202 $pstart = $header_size + 446 + 16*($pentry-1);
203
204 open(MCONFIG, "> ${cfgfile}") or die "$0: Cannot make mtools config\n";
205 print MCONFIG "drive z:\n";
206 print MCONFIG "file=\"${imglink}\"\n";
207 print MCONFIG "cylinders=${c}\n";
208 print MCONFIG "heads=${h}\n";
209 print MCONFIG "sectors=${s}\n";
210 print MCONFIG "offset=${offset}\n";
211 print MCONFIG "mformat_only\n";
212 close(MCONFIG);
213
214 # Output the filesystem offset to stdout if appropriate
215 if ( $opt{'o'} ) {
216     print $offset, "\n";
217 }
218
219 $ENV{'MTOOLSRC'} = $cfgfile;
220 if ( $opt{'F'} ) {
221     system('mformat', '-F', 'z:');
222 } else {
223     system('mformat', 'z:');
224 }
225
226 # Clean up in /tmp
227 unlink($cfgfile);
228 unlink($imglink);
229 rmdir($tmpdir);
230
231 # MTOOLS doesn't write the bsHiddenSecs field correctly
232 seek(OUTPUT, $offset + 0x1c, 0);
233 print OUTPUT pack("V", ($offset-$header_size)>>9);
234
235 # Set the partition type
236 if ( $opt{'F'} ) {
237     $fstype = 0x0b;             # FAT32
238 } else {
239     if ( $psize > 65536 ) {
240         $fstype = 0x06;         # FAT16 > 32MB
241     } else {
242         $fstype = 0x04;         # FAT16 <= 32MB
243     }
244     seek(OUTPUT, $offset + 0x36, 0);
245     read(OUTPUT, $fsname, 8);
246     
247     # FAT12: adjust partition type
248     if ( $fsname eq 'FAT12   ' ) {
249         $fstype = 0x01;         # FAT12
250     }
251 }
252 seek(OUTPUT, $pstart+4, 0);
253 print OUTPUT pack("C", $fstype);
254
255 flush OUTPUT;
256
257 # Just in case this is a block device, try to flush the partition table
258 if ( $is_linux ) {
259     ioctl(OUTPUT, $BLKRRPART, 0);
260 };
261
262 exit 0;
263 __END__