1 #!/usr/bin/perl
3 # $Id: setup_harddisks,v 1.41 2005/04/08 10:08:54 lange Exp $
4 #*********************************************************************
5 #
6 # setup_harddisks -- create partitions and filesystems on harddisk
7 #
8 # This script is part of FAI (Fully Automatic Installation)
9 # Copyright (c) 1999, 2000 by ScALE Workgroup, Universitaet zu Koeln
10 # Copyright (c) 2000-2005 by Thomas Lange, Uni Koeln
11 #
12 #*********************************************************************
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
17 #
18 # This program is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
22 #
23 # You should have received a copy of the GNU General Public License
24 # along with this program; see the file COPYING. If not, write to the
25 # Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
26 # MA 02111-1307, USA.
27 #*********************************************************************
28 #
29 # This program first read the configfiles, partitions and formats the harddisks,
30 # produces fstab and FAI-variables-file. It uses sfdisk, mke2fs, mkswap
31 #
32 # Parameters:
33 # [-X] no test, your harddisks will be formated
34 # default: only test, no real formating
35 # [-f<config-filename>] default: parse classes
36 # [-c<class-path>] default: $FAI/disk_config/
37 # [-d] default: no DOS alignment
38 #
39 #---------------------------------------------------
40 # Last changes: 31.3.2005 by Thomas Lange add sub mapdisk{}
41 # Last changes: 8.11.2004 by Thomas Lange add $devdisklist when calling sfdisk
42 # Last changes: 3.2.2004 by Thomas Lange typos
43 # Last changes: 14.07.2003 by Thomas Lange add xfs filesystem support
44 # Last changes: 23.01.2003 by Thomas Lange print info data to stdout
45 # Last changes: 03.12.2002 by Thomas Lange remove ida, cciss stuff. Just match everything
46 # Last changes: 27.11.2002 by Thomas Lange allow more that 3 primary partitions
47 # Last changes: 14.05.2002 by Thomas Lange use strict
48 # Last changes: 04.05.2002 by Thomas Lange use strict
49 # Last changes: 29.04.2002 by Thomas Lange add swaplist
50 # Last changes: 12.01.2002 by Thomas Lange
51 # /dev/ida/ patch 12.01.2002 by Marc Martinez <lastxit+fai@technogeeks.org>
52 # Last changes: 9.11.2001 by Thomas Lange
53 # reiserfs patch 8.11.2001 by Diane Trout <diane@caltech.edu>
54 # Last changes: 25.10.2001 by Thomas Lange
55 # Last changes: 09.07.2001 by Thomas Lange
56 # Last changes: 04.07.2001 by Thomas Lange
57 # Last changes: 06.05.2001 by Thomas Lange
58 # Last changes: 09.03.2001 by Thomas Lange
59 # Last changes: 05.12.2000 by Thomas Lange
60 # Last changes: 03.05.2000 by Thomas Lange
61 # Last changes: 03.04.2000 by Mattias Gaertner
62 #---------------------------------------------------
63 #
64 # config-file format:
65 # lines beginning with # are comments
66 #
67 # "disk_config <device>|first|end"
68 # The disk_config command starts the parsing.
69 # It has to be the first command.
70 # <device> is the harddisk to format in short form like "hda" or "sdc".
71 # if first is used, the first of $ENV{disklist} is used
72 # "end" = end parsing here
73 # Example: "disk_config hdb"
74 # Example: "disk_config first"
75 #
76 # Defining one partition:
77 # "primary|logical mountpoint|swap|- <size in mb>|preserve<No> [fstab-options][;extraordinary options]"
78 # "primary|logical":
79 # "primary": this are the bootable partitions like the
80 # root directory "/" or the DOS "C:" disk.
81 # "logical": this are all other partitions like a linux
82 # "/var" or a swap partition or a DOS disk.
83 #
84 # "mountpoint|swap|-":
85 # "mountpoint":
86 # This is the mount-point for fstab.
87 # For example "/","/var","/usr". There must not
88 # be a space in the mountpoint.
89 # "swap":
90 # swap-partitions
91 # "-":
92 # do not mount this partition.
93 #
94 # "<size in mb>|preserve<No>":
95 # "<size in mb>":
96 # The size of the partition in megabyte
97 # Examples:
98 # "30" = 30 mb
99 # "10-100" = 10 to 100 mb
100 # "20-" = minimum of 20 mb
101 # "-500" = 1 to 500 mb
102 # The megabytes will be rounded up to cylinders.
103 # "preserve<No>":
104 # This is the alternative for the size attribute.
105 # <No> is the partition number. For example
106 # preserve3 for the third partition. If the
107 # <device> was hda then this results in hda3.
108 # The partition will be left unchanged. This
109 # is useful if you have partitions that do not
110 # need re-installation or if you want to have
111 # other operation systems on the device together
112 # with Linux. Extended Partitions can not be preserved.
113 # The bootable flag will not be preserved.
114 # Preserved partitions are mounted readonly during
115 # installation.
116 #
117 # "fstab-options":
118 # These options are copied to the fstab-file. The
119 # default is "default"
120 #
121 # After the semicolon there could be extra options like:
122 # -i <bytes> : Bytes per inodes
123 # (only ext2/3 filesystem)
124 # -m <blocks>% : reserved blocks percentage for superuser
125 # (only ext2/3 filesystem)
126 # -j : format in ext3
127 # -c : check for bad blocks
128 # format : Always format this partition even if preserve
129 # lazyformat : Do not format if partition has not moved
130 # (useful for testing the installation)
131 # boot : make this partition the boot-partition (the
132 # linux root filesystem is the default)
133 # ext2 : Extended 2 filesystem (this is the default)
134 # swap : swap partition
135 # dosfat16 : DOS 16bit FAT file system
136 # winfat32 : Win95 FAT32 file system
137 # writable : mounts a preserved partition writable
138 # xfs : xfs
139 # reiser : reiserfs
140 # -h <hash> : set reiserfs hash
141 # -v <ver> : set reiserfs version
142 #
143 use strict;
144 # getopts variables:
145 our ($opt_X, $opt_f, $opt_c, $opt_d);
146 my $test;
148 $| = 1; # flush always
150 #****************************************************
151 # Variables
152 #****************************************************
154 my $Version = "version 0.35fai";
156 my $megabyte = 1024 * 1024; # guess
157 # $gigabyte = 1024 * $megabyte;
158 my $sectorsize = 512;
160 # used programs
161 my $sfdisk_options = "-q $ENV{sfdisk}"; # be quiet
162 my $mke2fs_options = "-q"; # be quiet
163 my $mkreiserfs_options = "";
164 my $mkxfs_options = "-f";
165 my $mkswap_options = "";
167 # FAI input variables
168 my $ClassPath = "$ENV{FAI}/disk_config";# this directory contains the classes
169 my $ConfigFileName = ""; # alternative classfile, only for tests
170 my $DOS_Alignment = ""; # align partitions for tracks
172 # FAI output variables
173 my $BootPartition = ""; # the boot partition like "hda1"
174 my $BOOT_DEVICE = ""; # the root device like "hda" or "sdb"
175 my $FAIOutputFile = $ENV{diskvar}; # write output variables to this file
177 # old partition tables
178 my %DiskUnits = (); # unit size of each disk in sectors
179 my %DiskSize = (); # size of every disk in units
180 my %SectorsAlignment = (); # tracksize in sectors
181 my %PartOldBoot = (); # partition was bootable. "yes"=yes
182 my %PartOldStart = (); # old startunit of partition
183 my %PartOldEnd = (); # old endunit of partition
184 my %PartOldStartSec = (); # old startsector of partition
185 my %PartOldEndSec = (); # old endsector of partition
186 my %PartOldID = (); # old ID of partition
187 my %OldNotAligned = (); # "yes" if old partition boundaries are not DOS aligned
189 # mountpoints ("/<path>" or "swap<No>" or "no<No>" or "extended<disk>")
190 my $NofSwapPart = 0; # number of swap partitions
191 my $NofNotMoPart = 0; # number of not mountet partitions
192 my %DiskMountpoints = (); # mountpoints of every disk. separated by spaces
193 my %MountpointPart = (); # partition of every mountpoint. e.g. "hda2"
194 my %PartMountpoint = (); # mountpoint of every partition.
195 my @swaplist; # list of all swpa devices
197 # size of partition/mountpoint
198 my %MPMinSize = (); # minimum size of mountpoint in units
199 my %MPMaxSize = (); # maximum size of mountpoint in units
200 my %MPPreserve = (); # preserve partition: "yes"=yes
201 my %MPPrimary = (); # primary partition: "yes"=yes
202 my %MPStart = (); # start of partition in units
203 my %MPSize = (); # size of partition in units
204 my %MPID = (); # id of partition
206 # options
207 my %MPfstaboptions = (); # fstab options for every mountpoint
208 my %MPOptions = (); # extra options for every mountpoint
210 # sfdisk partition tables
211 my %sfdiskTables = (); # partition tables for sfdisk
213 my $verbose = 0;
214 $verbose = $ENV{verbose} if $ENV{verbose};
216 # Parse command line
218 use Getopt::Std;
219 &getopts('Xf:c:d') || die "
220 USAGE: [-X] no test, your harddisks will be formated
221 default: only test, no real formating
222 [-f<config-filename>] default: parse classes
223 [-c<class-path>] default: \$FAI/disk_config/
224 [-d] default: no DOS alignment
225 ";
227 print "setup_harddisks $Version\n";
228 if (defined $opt_X){
229 $test = 2;
230 } else {
231 print "TEST ONLY - no real formating\n\n";
232 $test = 1;
233 }
234 $ConfigFileName = $opt_f if $opt_f;# alternative config file
235 $ClassPath = $opt_c if $opt_c;# search classes here
236 $DOS_Alignment = "yes" if $opt_d; # track alignment
238 # main part
239 &GetAllDisks;
240 &ParseAllConfigFiles;
241 &BuildNewPartTables;
242 &PartitionPersfdisk;
243 &FormatDisks;
244 &WriteFSTab;
245 &WriteFAIVariables;
246 exit 0;
247 #****************************************************
249 #****************************************************
250 # get a partition pathname
251 #****************************************************
252 sub PartName {
253 my ($disk, $partno) = @_;
254 my $ppath;
255 for ($disk) {
256 /^[a-z]+$/ and $ppath = "${disk}${partno}";
257 /\d$/ and $ppath = "${disk}p${partno}";
258 }
259 return $ppath;
260 }
262 #****************************************************
263 # Read all partition tables of this machine
264 #****************************************************
265 sub GetAllDisks{
266 my $line=""; my $disk=""; my $device=""; my $rest; my $result; my $divi;
267 my $devdisklist="";
269 foreach my $device(split(/\s/,$ENV{disklist})){
270 $devdisklist = "$devdisklist /dev/$device";
271 }
272 print "Probing disks: $devdisklist\n";
273 print "Disks found:";
274 $result = `sh -c "LC_ALL=C sfdisk -g -q $devdisklist"`;
275 foreach my $line(split(/\n/,$result)){
276 if($line =~ m'^/dev/(.+?):\s+(\d+)\s+cylinders,\s+(\d+)\s+heads,\s+(\d+)\s+sectors'i){
277 $disk = $1;
278 $DiskUnits{$disk} = $3 * $4;# heads * sectors = cylinder size in sectors
279 $DiskSize{$disk} = $2; # cylinders
280 ($DOS_Alignment eq "yes") ? ($SectorsAlignment{$disk} = $4) : ($SectorsAlignment{$disk} = 1);
281 print " $disk";
282 }
283 }
284 $result = `sh -c "LC_ALL=C sfdisk -d -q $devdisklist"`;
285 foreach my $line(split(/\n/,$result)){
286 # if($line =~ m'# partition table of /dev/(cciss/c\dd\d|ida/c\dd\d|rd/c\dd\d|[a-z]+)'i){
287 # now just match all devices
288 if($line =~ m'# partition table of /dev/(\S+)$'i){
289 $disk = $1;
290 }
291 if($line =~ m#^/dev/(.+?)\s*:\s+start=\s*(\d+),\s+size=\s*(\d+),\s+Id=\s*([a-z0-9]+)\b(.*)$#i){
292 $device = $1;
293 # Sectors
294 $PartOldStartSec{$device} = $2;
295 $PartOldEndSec{$device} = $2 + $3 - 1;
296 # DiskUnits
297 $PartOldStart{$device} = int ($2 / $DiskUnits{$disk});
298 $PartOldEnd{$device} = int (($2 + $3 - 1) / $DiskUnits{$disk});
299 $divi = $2 / $SectorsAlignment{$disk};
300 ($divi != int ($divi)) && ($OldNotAligned{$device} = "yes");
301 $divi = $3 / $SectorsAlignment{$disk};
302 ($divi != int ($divi)) && ($OldNotAligned{$device} = "yes");
303 $PartOldID{$device} = $4;
304 $rest = $5;
305 $PartOldBoot{$device} = ($rest =~ /bootable/) ? "yes" : "";
306 }
307 }
308 print "\n\n";
309 }
311 #****************************************************
312 # parse config file or all class files
313 #****************************************************
314 sub ParseAllConfigFiles{
315 my $ConfigFileExists = 0; # no config file parsed yet
316 if ($ConfigFileName){
317 # Read config filename
318 &ParseConfigFile($ConfigFileName);
319 $ConfigFileExists = 1;
320 } else {
321 # Read classes
322 foreach my $classfile (reverse split(/\s+/,$ENV{"classes"})){
323 my $filename = "$ClassPath/$classfile";
324 if (($classfile) && (-r $filename)) {
325 &ParseConfigFile($filename);
326 $ConfigFileExists = 1;
327 }
328 ($ConfigFileExists) && last;
329 }
330 }
331 ($ConfigFileExists == 0) && die "ERROR: no config file for setup_harddisk found. Please check you classes and files in disk_config.\n";
332 }
334 #****************************************************
335 # map "disk_config first" to real disk device
336 #****************************************************
337 sub mapdisk {
339 my ($disk) = @_;
340 my @dlist = split /\s+/,$ENV{disklist};
342 if ($disk eq "disk1") {
343 print "Mapping disk name disk1 to $dlist[0]\n";
344 $disk = $dlist[0];
345 }
346 if ($disk eq "disk2") {
347 print "Mapping disk name disk2 to $dlist[1]\n";
348 $disk = $dlist[1];
349 }
350 return $disk;
351 }
353 #****************************************************
354 # parse config-file
355 #****************************************************
356 sub ParseConfigFile{
357 my $size=""; my $mountpoint=""; my $device ="";
358 my $fstaboptions=""; my $options=""; my $disk=""; my $command = "";
359 my $LogPartNo; my $PrimPartNo; my $NoMoreLogicals;
360 my $LastPresPart; my $extmp; my $Min; my $Max;
361 my $filename = shift;
362 open (FILE,"$filename")
363 || die "config file not found: $filename\n";
364 (print "Using config file: $filename\n");
365 $disk = "";
366 my $a = 1, my $paras ="", my $number=0;
367 while (my $line = <FILE>){
368 chomp($line);
369 $a++;
370 next if( $line =~ /^#|^\s*$/ );
372 # disk_config - command
373 if ($line =~ /^disk_config(.*)/i){
374 $paras = $1;
375 if ($paras =~ / end/i){
376 $disk = "";
377 } else {
378 # if($paras =~ m# (/dev/)?(cciss/c\dd\d|ida/c\dd\d|rd/c\dd\d|[a-z]+)#i){
379 # now match all devives
380 if($paras =~ m# (/dev/)?(\S+)#i){
381 $disk = mapdisk($2);
382 ($DiskMountpoints{$disk})
383 && die "ERROR: there are more than one configuration of disk $disk.\n";
384 ($DiskSize{$disk}) || die "ERROR: could not read device /dev/$disk\n";
385 ($test != 1) || (print "config: $disk\n");
386 $DiskMountpoints{$disk} = "";
387 $MPPrimary{"extended$disk"} = "";
388 $LogPartNo = 4;
389 $PrimPartNo = 0;
390 $NoMoreLogicals = 0;
391 $LastPresPart = "";
392 $extmp = "extended$disk";
393 } else {
394 die "SYNTAX ERROR: in config file line $a, unknown disk_config parameter $paras\n$line\n";
395 }
396 }
397 }
399 if ($disk){
400 # primary|partition - command
401 if($line =~ /^\s*(primary|logical)\s+(.*)$/i){
402 $command = $1;
403 # split variables
404 $paras = $2;
405 $options = "";
406 if($paras =~ /(.*?)\s*;\s*(.*)$/){
407 $paras = $1;
408 $options = $2;
409 }
410 $size="";
411 $mountpoint ="";
412 $fstaboptions = "";
413 ($mountpoint,$size,$fstaboptions)=split(/\s+/,$paras);
414 # mountpoint
415 ($mountpoint =~ m#^/.*|^swap$|^-$#i)
416 || die "SYNTAX ERROR in config file line $a, mountpoint: $mountpoint\n$line\n";
417 ($MountpointPart{$mountpoint})
418 && die "SYNTAX ERROR in config file line $a. Mountpoint $mountpoint redefined.\n$line\n";
419 if($mountpoint eq "/"){
420 ($BootPartition) || ($BOOT_DEVICE = $disk);
421 }
422 if($mountpoint eq "-"){
423 $NofNotMoPart++;
424 $mountpoint = "no$NofNotMoPart";
425 }
426 if($mountpoint eq "swap"){
427 $NofSwapPart++;
428 $mountpoint = "swap$NofSwapPart";
429 ($options !~ /\bswap\b/i) && ($options .= " swap");
430 ($fstaboptions) || ($fstaboptions = "sw");
431 }
432 if($mountpoint =~ m#^/#){
433 ($fstaboptions) || ($fstaboptions = "defaults");
434 }
435 if ($command eq "primary") {
436 ($MPPrimary{$extmp} eq "yes") && ($NoMoreLogicals = 1);
437 $MPPrimary{$mountpoint} = "yes";
438 $PrimPartNo++;
439 # ($PrimPartNo == 3) && ($disk =~ /^sd/) && ($PrimPartNo++);
440 ($PrimPartNo >4 ) && die "ERROR: Too much primary partitions (max 4).".
441 " All logicals together need one primary too.\n";
442 $MountpointPart{$mountpoint} = PartName($disk,$PrimPartNo);
443 if($options =~ /\bboot\b/i){
444 ($BootPartition) && die "ERROR: only one partition can be bootable at a time.";
445 $BootPartition = $MountpointPart{$mountpoint};
446 $BOOT_DEVICE = $disk;
447 }
448 } else {
449 ($NoMoreLogicals != 0) && die "ERROR: the logical partitions must be together.\n";
450 $MPPrimary{$mountpoint} = "";
451 $LogPartNo++;
452 $MountpointPart{$mountpoint} = PartName($disk,$LogPartNo);
453 if (!$MPPrimary{$extmp}){
454 $MPPreserve{$extmp} = "";
455 $MPPrimary{$extmp} = "yes";
456 $MPMinSize{$extmp} = 0;
457 $MPMaxSize{$extmp} = 0;
458 $MPID{$extmp} = 5;
459 $PrimPartNo++;
460 ($PrimPartNo == 3) && ($disk =~ /^sd/) && ($PrimPartNo++);
461 ($PrimPartNo >4 )
462 && die "ERROR: too much primary partitions (max 4).".
463 " All logicals together need one primary too.\n";
464 $MountpointPart{$extmp} = PartName($disk,$PrimPartNo);
465 $DiskMountpoints{$disk} .= " $extmp";
466 }
467 # ($options =~ /\bboot\b/i) && die "ERROR: line $a, only primary partitions can be bootable.\n";
468 }
469 $DiskMountpoints{$disk} .= " $mountpoint";
470 # size
471 ($size =~ /^preserve\d+$|^\d+\-?\d*$|^-\d+$/i)
472 || die "SYNTAX ERROR in config file line $a, size: $size\n$line\n";
473 if($size =~ /^preserve(\d+)$/i){
474 my $number = $1;
475 $device = PartName($disk,$number);
476 ($OldNotAligned{$device} eq "yes")
477 && die "ERROR: unable to preserve partition /dev/$device. Partition is not DOS aligned.";
478 ($command eq "primary") && ($number != $PrimPartNo)
479 && die "NUMERATION ERROR in line $a, the number of the partition can not be preserved:\n$line\n";
480 ($command eq "logical") && ($number != $LogPartNo)
481 && die "NUMERATION ERROR in line $a, the number of the partition can not be preserved:\n$line\n";
482 if ($PartOldEnd{$device}){
483 (($PartOldID{$device} == 5) || ($PartOldID{$device} == 85)) &&
484 die "ERROR in config file line $a.".
485 " Extended partitions can not be preserved. /dev/$device\n$line\n";
486 $MPPreserve{$mountpoint}="yes";
487 $MPMinSize{$mountpoint} = $PartOldEnd{$device}-$PartOldStart{$device}+1;
488 $MPMaxSize{$mountpoint} = $MPMinSize{$mountpoint}; # Max=Min
489 $MPStart{$mountpoint} = $PartOldStart{$device};
490 $MPSize{$mountpoint} = $MPMinSize{$mountpoint};
491 $MPID{$mountpoint} = $PartOldID{$device};
492 } else {
493 die "ERROR: cannot preserve partition $device. partition not found.$PartOldEnd{$device}\n";
494 }
495 if ($LastPresPart) {
496 ($PartOldStart{$device} < $PartOldStart{$LastPresPart}) &&
497 die "ERROR: misordered partitions: cannot preserve partitions $LastPresPart and $device\n".
498 " in this order because of their positions on disk.";
499 }
500 $LastPresPart = $device;
501 ($MPMinSize{$mountpoint} < 1)
502 && die "ERROR: unable to preserve partitions of size 0.\n$line\n ";
503 } else {
504 # If not preserve we must know the filesystemtype
505 ($options !~ /\b(ext2|ext3|auto|swap|dosfat16|winfat32|reiser|xfs)\b/i ) && ($options .= " auto");
506 }
507 if($size =~ /^(\d*)(\-?)(\d*)$/){
508 $Min = $1;
509 $Min||= 1;
510 $Max = $3;
511 $MPMinSize{$mountpoint} = int (($Min * $megabyte - 1) / ($DiskUnits{$disk} * $sectorsize)) + 1;
512 if ($2 eq "-"){
513 if($Max =~ /\d+/){
514 $MPMaxSize{$mountpoint} = int (($Max * $megabyte - 1) / ($DiskUnits{$disk} * $sectorsize)) + 1;
515 } else {
516 $MPMaxSize{$mountpoint} = $DiskSize{$disk};
517 }
518 } else {
519 $MPMaxSize{$mountpoint} = $MPMinSize{$mountpoint}; # Max=Min
520 }
521 ($MPMinSize{$mountpoint} > $DiskSize{$disk})
522 && die "ERROR in config file line $a: Minsize larger than disk.\n$line\n";
523 ($MPMinSize{$mountpoint} > $MPMaxSize{$mountpoint})
524 && die "SYNTAX ERROR in config file line $a, MIN-MAX-size: $MPMinSize{$mountpoint}-$MPMaxSize{$mountpoint}\n$line\n";
525 ($MPMinSize{$mountpoint} < 1)
526 && die "SYNTAX ERROR in config file line $a. Minsize must be greater than 1.\n$line\n";
527 $MPPreserve{$mountpoint} = "";
528 }
529 # fstaboptions
530 $MPfstaboptions{$mountpoint} = $fstaboptions;
531 # extra options
532 ($options =~ /\b(ext[23]|auto)\b/i) && ($MPID{$mountpoint} = 83); # Linux native
533 ($options =~ /\bswap\b/i) && ($MPID{$mountpoint} = 82); # Linux swap
534 ($options =~ /\bdosfat16\b/i) && ($MPID{$mountpoint} = 6); # DOS FAT 16bit (>=32MB, will be changed later)
535 ($options =~ /\bwinfat32\b/i) && ($MPID{$mountpoint} = "b"); # Win 95 FAT 32
536 $MPOptions{$mountpoint} = $options;
537 if($test == 1){
538 print "$mountpoint,$MPMinSize{$mountpoint}-$MPMaxSize{$mountpoint},";
539 print "$fstaboptions,$options";
540 ($MPPreserve{$mountpoint} eq "yes") && (print " Preserve: $MountpointPart{$mountpoint}");
541 print "\n";
542 }
543 }
544 }
545 }
546 close(FILE);
547 }
549 #****************************************************
550 # Build all partition tables
551 #****************************************************
552 sub BuildNewPartTables{
553 my ($disk, $mountpoint, $part, $PrimaryMP, $LogicalMP);
554 ($test != 1) || (print "\nBuilding partition tables:\n");
555 # Build PartMountpoint array
556 foreach $disk(keys %DiskMountpoints) {
557 $DiskMountpoints{$disk} =~ s/\s(\s)/$1/g;
558 $DiskMountpoints{$disk} =~ s/^\s//;
559 $DiskMountpoints{$disk} =~ s/\s$//;
560 foreach $mountpoint(split(/\s/,$DiskMountpoints{$disk})) {
561 $PartMountpoint{$MountpointPart{$mountpoint}} = $mountpoint;
562 }
563 }
564 foreach $disk(keys %DiskMountpoints) {
565 &SetPartitionPositions($disk);
566 # change units to sectors
567 foreach $mountpoint(split(/\s/,$DiskMountpoints{$disk})) {
568 if($MPPreserve{$mountpoint} eq "yes"){
569 $MPStart{$mountpoint} = $PartOldStartSec{$MountpointPart{$mountpoint}};
570 $MPSize{$mountpoint} = $PartOldEndSec{$MountpointPart{$mountpoint}} - $MPStart{$mountpoint} + 1;
571 } else {
572 $MPStart{$mountpoint} *= $DiskUnits{$disk};
573 $MPSize{$mountpoint} *= $DiskUnits{$disk};
574 # align first partition for mbr
575 if($MPStart{$mountpoint} == 0){
576 $MPStart{$mountpoint} += $SectorsAlignment{$disk};
577 $MPSize{$mountpoint} -= $SectorsAlignment{$disk};
578 }
579 }
580 }
581 # align all logical partitions
582 foreach $mountpoint(split(/\s/,$DiskMountpoints{$disk})) {
583 next if ($MPPrimary{$mountpoint} eq "yes");
584 if ($MountpointPart{$mountpoint} eq "${disk}5") {
585 # partition with number 5 is first logical partition and start of extended partition
586 $MPStart{"extended$disk"} = $MPStart{$mountpoint};
587 ($MPPreserve{$mountpoint} eq "yes") && ($MPStart{"extended$disk"} -= $SectorsAlignment{$disk});
588 }
589 if ($MPPreserve{$mountpoint} ne "yes") {
590 $MPStart{$mountpoint} += $SectorsAlignment{$disk};
591 $MPSize{$mountpoint} -= $SectorsAlignment{$disk};
592 }
593 }
594 &CalculateExtPartSize($disk);
595 # sort mountpoints of partition number
596 $PrimaryMP = "";
597 $LogicalMP = "";
598 foreach $mountpoint(split(/\s/,$DiskMountpoints{$disk})) {
599 ($MPPrimary{$mountpoint} eq "yes") ? ($PrimaryMP .= " $mountpoint") : ($LogicalMP .= " $mountpoint");
600 }
601 $DiskMountpoints{$disk} = "$PrimaryMP$LogicalMP";
602 $DiskMountpoints{$disk} =~ s/^\s//;
603 # print partition table
604 ($test != 1) || (PrintPartitionTable($disk));
605 }
606 if (!$BootPartition){
607 $BootPartition = $MountpointPart{"/"};
608 }
609 }
611 #****************************************************
612 # set position for every partition
613 #****************************************************
614 sub SetPartitionPositions{
615 my $disk = shift;
616 my $mountpoint; my $DynGroup =""; my $StartPos; my $EndPos;
617 # Build groups of unpreserved partitions between
618 # preserved partitions
619 $StartPos = 0;
620 foreach $mountpoint(split(/\s/,$DiskMountpoints{$disk})) {
621 if ($MPPreserve{$mountpoint} eq "yes") {
622 $EndPos = $PartOldStart{$MountpointPart{$mountpoint}} - 1;
623 &SetGroupPos($DynGroup,$StartPos,$EndPos);
624 $DynGroup = "";
625 $StartPos = $PartOldEnd{$MountpointPart{$mountpoint}} + 1;
626 } else {
627 $DynGroup .= " $mountpoint";
628 }
629 }
630 $EndPos = $DiskSize{$disk} - 1;
631 &SetGroupPos($DynGroup,$StartPos,$EndPos);
632 foreach $mountpoint(split(/\s/,$DiskMountpoints{$disk})) {
633 ($MPOptions{$mountpoint} =~ /\bdosfat16\b/i)
634 && (($MPSize{$mountpoint} * $DiskUnits{$disk} * $sectorsize) < 32 * $megabyte)
635 && ($MPID{$mountpoint} = 4); # DOS 16-bit FAT <32MB
636 }
637 }
639 #****************************************************
640 # set position for a group of unpreserved partitions
641 # between start and end
642 #****************************************************
643 sub SetGroupPos{
644 my ($PartGroup,$Start,$End) = @_;
645 $PartGroup =~ s/^ //;
646 ($PartGroup) || return;
647 my $totalsize = $End - $Start + 1;
648 ($totalsize <= 0) && return;
649 my $mountpoint; my $mintotal = 0; my $maxmintotal = 0; my $rest = 0; my $EndUnit = 0;
650 # compute total of MinSizes and difference to MaxSizes
651 foreach $mountpoint (split(/\s/,$PartGroup)) {
652 $mintotal += $MPMinSize{$mountpoint};
653 $maxmintotal += ($MPMaxSize{$mountpoint} - $MPMinSize{$mountpoint});
654 $MPSize{$mountpoint} = $MPMinSize{$mountpoint};
655 }
656 # Test if partitions fit
657 ($mintotal > $totalsize)
658 && die "ERROR: Mountpoints $PartGroup do not fit.\n";
659 # Maximize partitions
660 $rest = $totalsize - $mintotal;
661 ($rest > $maxmintotal) && ($rest = $maxmintotal);
662 if ($rest > 0) {
663 foreach $mountpoint (split(/\s/,$PartGroup)) {
664 $MPSize{$mountpoint} += int ((($MPMaxSize{$mountpoint} - $MPMinSize{$mountpoint}) * $rest) / $maxmintotal);
665 }
666 }
667 # compute rest
668 $rest = $totalsize;
669 foreach $mountpoint (split(/\s/,$PartGroup)) {
670 $rest -= $MPSize{$mountpoint};
671 }
672 # Minimize rest
673 foreach $mountpoint (split(/\s/,$PartGroup)) {
674 if (($rest >0) && ($MPSize{$mountpoint} < $MPMaxSize{$mountpoint})){
675 $MPSize{$mountpoint}++;
676 $rest--;
677 }
678 }
679 # Set start for every partition
680 foreach $mountpoint (split(/\s/,$PartGroup)) {
681 $MPStart{$mountpoint} = $Start;
682 $Start += $MPSize{$mountpoint};
683 $EndUnit = $MPStart{$mountpoint} + $MPSize{$mountpoint} - 1;
684 }
685 }
687 #****************************************************
688 # calculate extended partition size
689 #****************************************************
690 sub CalculateExtPartSize{
691 my ($disk) = @_;
692 my $extmp = "extended$disk";
693 my $mountpoint; my $ExtEnd; my $NewEnd;
694 ($MPPrimary{$extmp}) || return;
695 $ExtEnd = $MPStart{$extmp};
696 foreach $mountpoint(split(/\s/,$DiskMountpoints{$disk})) {
697 next if ($MPPrimary{$mountpoint} eq "yes");
698 $NewEnd = $MPStart{$mountpoint} + $MPSize{$mountpoint} - 1;
699 ($NewEnd > $ExtEnd) && ($ExtEnd = $NewEnd);
700 }
701 $MPSize{$extmp} = ($ExtEnd - $MPStart{$extmp} + 1);
702 }
704 #****************************************************
705 # Print partition "number - mountpoint" table
706 #****************************************************
707 sub PrintPartitionTable{
708 my ($disk) = @_;
709 my $part; my $mountpoint; my $mountpointname; my $end;
710 foreach $part (sort %MountpointPart) {
711 next if($part !~ /^$disk/);
712 $mountpoint = $PartMountpoint{$part};
713 if ($mountpoint =~ /^no(.*)/){
714 $mountpointname = "no mountpoint ($1)";
715 } else {
716 $mountpointname = $mountpoint;
717 }
718 $end = $MPStart{$mountpoint} + $MPSize{$mountpoint} - 1;
719 print <<"EOM";
720 /dev/$part $mountpointname start=$MPStart{$mountpoint} size=$MPSize{$mountpoint} end=$end id=$MPID{$mountpoint}
721 EOM
722 }
723 }
725 #****************************************************
726 # build all partition tables for sfdisk
727 #****************************************************
728 sub PartitionPersfdisk{
729 my ($disk, $mountpoint, $line, $part, $PrimaryNo);
730 my ($command, $result, $filename, $number);
731 print "Creating partition table: ";
732 foreach $disk(keys %DiskMountpoints) {
733 $sfdiskTables{$disk} = "# partition table of device: /dev/$disk\nunit: sectors\n\n";
734 $PrimaryNo = 1;
735 foreach $mountpoint(split(/\s/,$DiskMountpoints{$disk})) {
736 $part = $MountpointPart{$mountpoint};
737 $part =~ /(\d+)$/;
738 ($1 < 5) && ($PrimaryNo++);
739 if ( ($1 == 5) && ($PrimaryNo < 5) ){
740 for my $number($PrimaryNo..4) {
741 $sfdiskTables{$disk} .= BuildsfdiskDumpLine(PartName($disk,$number),0,0,0)."\n";
742 }
743 }
744 $line = BuildsfdiskDumpLine($MountpointPart{$mountpoint},$MPStart{$mountpoint},$MPSize{$mountpoint},$MPID{$mountpoint});
745 ($part eq $BootPartition) && ($line .= ", bootable");
746 $sfdiskTables{$disk} .= "$line\n";
747 }
748 # print $sfdiskTables{$disk};
749 $filename = "$ENV{LOGDIR}/partition." . (($disk=~ m#/#) ? join('_', split('/', $disk)) : $disk);
750 if(($test != 1) && ($filename)){
751 open(FILE, ">$filename") || die "unable to write temporary file $filename\n";
752 print FILE $sfdiskTables{$disk};
753 close(FILE);
754 }
755 $command = "LC_ALL=C sfdisk $sfdisk_options /dev/$disk < $filename";
756 if($test != 1){
757 print "$command\n";
758 $result = `sh -c "$command"`;
759 (($? >> 8) == 0) || (die "\nSFDISK ERROR:\n $result\n");
760 }
761 }
762 }
764 #****************************************************
765 # build a sfdisk dump line
766 #****************************************************
767 sub BuildsfdiskDumpLine{
769 sprintf "/dev/%-5s: start=%10s, size=%10s, Id=%3s",@_;
770 }
772 #****************************************************
773 # Format all disks
774 #****************************************************
775 sub FormatDisks{
776 my ($disk, $device, $mountpoint, $mountpointname, $command, $result);
777 print "Creating file systems:\n";
778 foreach $disk(keys %DiskMountpoints) {
779 foreach $mountpoint (split(/\s/,$DiskMountpoints{$disk})) {
780 $device = $MountpointPart{$mountpoint};
781 if ($mountpoint =~ /^no/){
782 $mountpointname = "no mountpoint";
783 } else {
784 $mountpointname = $mountpoint;
785 }
786 # preserved partition
787 if ( ($MPPreserve{$mountpoint} eq "yes") && ($MPOptions{$mountpoint} !~ /\bformat\b/i)){
788 print "Preserve partition $device";
789 if ($mountpoint =~ /^no$1/){
790 print " with no mountpoint\n";
791 } else {
792 print " with mountpoint $mountpoint\n";
793 }
794 next;
795 }
796 # lazy format
797 if ( ( $MPOptions{$mountpoint} =~ /\blazyformat\b/i )
798 && ($MPStart{$mountpoint} == $PartOldStartSec{$device})
799 && (($MPStart{$mountpoint} + $MPSize{$mountpoint} - 1) == $PartOldEndSec{$device}) ){
800 print "Lazy format: $device";
801 if ($mountpoint =~ /^no$1/){
802 print " with no mountpoint";
803 } else {
804 print " with mountpoint $mountpoint";
805 }
806 print " was neither moved nor formated.\n";
807 next;
808 }
809 # swap
810 if ($mountpoint =~ /^swap/i) {
811 # print "Make swap partition:\n";
812 $command = "mkswap $mkswap_options";
813 ($MPOptions{$mountpoint} =~ /(\-c)\b/i) && ($command .= " $1");
814 push @swaplist, "/dev/$device";
815 $command .= " /dev/$device";
816 print " $command\n";
817 if($test != 1){
818 $result = `$command`;
819 (($? >> 8) == 0) || (die "\nMKSWAP ERROR:\n $result\n");
820 }
821 next;
822 }
823 # Linux Reiser file system
824 if ($MPOptions{$mountpoint} =~ /\breiser\b/i) {
825 # print "Make Reiser Filesystem:\n";
826 $command = "echo y | mkreiserfs $mkreiserfs_options";
827 ($MPOptions{$mountpoint} =~ /(\-h\s*\w+)\b/) && ($command .= " $1");
828 ($MPOptions{$mountpoint} =~ /(\-v\s*\d+)\b/) && ($command .= " $1");
829 $command .= " /dev/$device";
830 print " $command\n";
831 if ($test != 1){
832 $result = `$command`;
833 (($? >> 8) == 0) || die "\nMKREISERFS ERROR:\n $result\n";
834 }
835 next;
836 }
837 # Linux XFS file system
838 if ($MPOptions{$mountpoint} =~ /\bxfs\b/i) {
839 # print "Make XFS Filesystem:\n";
840 $command = "mkfs.xfs $mkxfs_options";
841 $command .= " /dev/$device";
842 print " $command\n";
843 if ($test != 1){
844 $result = `$command`;
845 (($? >> 8) == 0) || die "\nMKFS.XFS ERROR:\n $result\n";
846 }
847 next;
848 }
849 # Linux Extended 2 file system
850 if ($MPOptions{$mountpoint} =~ /\b(ext[23]|auto)\b/i) {
851 # print "Make Extended 2/3 Filesystem:\n";
852 $command = "mke2fs $mke2fs_options";
853 ($MPOptions{$mountpoint} =~ /(\-c)\b/i) && ($command .= " $1");
854 ($MPOptions{$mountpoint} =~ /(\-i\s*\d+)\b/) && ($command .= " $1");
855 ($MPOptions{$mountpoint} =~ /(\-m\s*\d+)\b/) && ($command .= " $1");
856 ($MPOptions{$mountpoint} =~ /(\-j)\b/) && ($command .= " $1");
857 $command .= " /dev/$device";
858 print " $command\n";
859 if ($test != 1){
860 $result = `$command`;
861 (($? >> 8) == 0) || die "\nMKE2FS ERROR:\n $result\n";
862 }
863 next;
864 }
865 # DOS 16bit FAT / Win95 FAT 32
866 if ($MPOptions{$mountpoint} =~ /\b(dosfat16|winfat32)\b/i) {
867 print "Clear first sector for DOS/Windows\n";
868 $command = "dd if=/dev/zero of=/dev/$MountpointPart{$mountpoint} bs=512 count=1";
869 print " $command\n";
870 if ($test != 1){
871 $result = `$command`;
872 (($? >> 8) == 0) || die "\nDD ERROR:\n $result\n";
873 }
874 next;
875 }
876 }
877 }
878 }
880 #****************************************************
881 # Build fstab and write it to <root>/etc/fstab
882 #****************************************************
883 sub WriteFSTab{
884 my ($FileSystemTab, $device, $type, $filename);
885 $FileSystemTab = << "EOM";
886 # /etc/fstab: static file system information.
887 #
888 #<file sys> <mount point> <type> <options> <dump> <pass>
889 EOM
890 # 1. /
891 $type = "ext2";
892 ($MPOptions{'/'} =~ /\b(reiser)\b/i) && ($type = "reiserfs");
893 ($MPOptions{'/'} =~ /\b(xfs)\b/i) && ($type = "xfs");
894 ($MPOptions{'/'} =~ /\b(ext3)\b/i) && ($type = "ext3");
895 ($MPOptions{'/'} =~ /\b(ext2)\b/i) && ($type = "ext2");
896 $FileSystemTab .= BuildfstabLine("/dev/$MountpointPart{'/'}","/",$type,$MPfstaboptions{'/'},0,1);
897 # 2. swap partitions
898 foreach my $mountpoint (%PartMountpoint){
899 next if( $mountpoint !~ /^swap/i);
900 $FileSystemTab .= BuildfstabLine("/dev/$MountpointPart{$mountpoint}",
901 "none","swap",$MPfstaboptions{$mountpoint},0,0);
902 }
903 # 3. /proc
904 $FileSystemTab .= BuildfstabLine("none","/proc","proc","defaults",0,0);
905 # 4. sorted others
906 foreach my $mountpoint (sort %PartMountpoint){
907 next if ( ($mountpoint !~ m#^/#) || ($mountpoint eq "/"));
908 $device = $MountpointPart{$mountpoint};
909 $type = "ext2";
910 ($MPOptions{$mountpoint} =~ /\b(dosfat16|winfat32)\b/i) && ($type = "vfat");
911 ($MPOptions{$mountpoint} =~ /\b(reiser)\b/i) && ($type = "reiserfs");
912 ($MPOptions{$mountpoint} =~ /\b(xfs)\b/i) && ($type = "xfs");
913 ($MPOptions{$mountpoint} =~ /\b(ext3)\b/i) && ($type = "ext3");
914 ($MPOptions{$mountpoint} =~ /\b(ext2)\b/i) && ($type = "ext2");
915 $FileSystemTab .= BuildfstabLine("/dev/$device",$mountpoint,$type,$MPfstaboptions{$mountpoint},0,2);
916 }
917 # write it
918 $filename = "$ENV{LOGDIR}/fstab";
919 # print $FileSystemTab;
920 print "Write fstab to $filename\n" if $verbose;
921 if($test != 1){
922 open(FILE, ">$filename") || die "unable to write fstab $filename\n";
923 print FILE $FileSystemTab;
924 close(FILE);
925 }
926 }
928 #****************************************************
929 # Build fstab line
930 #****************************************************
931 sub BuildfstabLine{
933 sprintf "%-10s %-15s %-6s %-8s %-4s %-4s\n",@_;
934 }
936 #****************************************************
937 # Write all FAI variables of this program to file
938 #****************************************************
939 sub WriteFAIVariables{
941 my $swaps;
943 print "Write FAI variables to file $FAIOutputFile\n" if $verbose;
944 return if ($test == 1);
945 $swaps = join ' ',@swaplist;
946 open(FILE, ">$FAIOutputFile") || die "Unable to write file $FAIOutputFile\n";
947 print FILE << "EOM";
948 BOOT_DEVICE=/dev/$BOOT_DEVICE
949 ROOT_PARTITION=/dev/$MountpointPart{'/'}
950 BOOT_PARTITION=/dev/$BootPartition
951 SWAPLIST="$swaps"
952 EOM
953 close(FILE);
954 }