Code

Moving finalized
[gosa.git] / gosa-core / contrib / fai / goto-fai / diversions / setup_harddisks
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.
117 #    "fstab-options":
118 #         These options are copied to the fstab-file. The
119 #         default is "default"
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
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;
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;
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";
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";
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;
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);
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     }
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     }
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     }
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);
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       }
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     }
764 #****************************************************
765 # build a sfdisk dump line
766 #****************************************************
767 sub BuildsfdiskDumpLine{
769   sprintf "/dev/%-5s: start=%10s, size=%10s, Id=%3s",@_;
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     }
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.
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     }
928 #****************************************************
929 # Build fstab line
930 #****************************************************
931 sub BuildfstabLine{
933     sprintf "%-10s   %-15s   %-6s  %-8s  %-4s %-4s\n",@_;
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);