2 # Functions for disk management under linux
4 BEGIN { push(@INC, ".."); };
7 &foreign_require("mount", "mount-lib.pl");
8 if (&foreign_check("raid")) {
9 &foreign_require("raid", "raid-lib.pl");
12 if (&foreign_check("lvm")) {
13 &foreign_require("lvm", "lvm-lib.pl");
16 &foreign_require("proc", "proc-lib.pl");
17 %access = &get_module_acl();
18 $has_e2label = &has_command("e2label");
19 $has_xfs_db = &has_command("xfs_db");
20 $has_volid = &has_command("vol_id");
21 $has_reiserfstune = &has_command("reiserfstune");
22 $uuid_directory = "/dev/disk/by-uuid";
23 if ($config{'mode'} eq 'parted') {
26 elsif ($config{'mode'} eq 'fdisk') {
30 $has_parted = !$config{'noparted'} && &has_command("parted") &&
31 &get_parted_version() >= 1.8;
35 # list_disks_partitions([include-cds])
36 # Returns a structure containing the details of all disks and partitions
37 sub list_disks_partitions
39 if (scalar(@list_disks_partitions_cache)) {
40 return @list_disks_partitions_cache;
43 local (@pscsi, @dscsi, $dscsi_mode);
44 if (-r "/proc/scsi/sg/devices" && -r "/proc/scsi/sg/device_strs") {
45 # Get device info from various /proc/scsi files
46 open(DEVICES, "/proc/scsi/sg/devices");
49 local @l = split(/\t+/, $_);
50 push(@dscsi, { 'host' => $l[0],
58 open(DEVNAMES, "/proc/scsi/sg/device_strs");
61 local @l = split(/\t+/, $_);
62 $dscsi[$i]->{'make'} = $l[0];
63 $dscsi[$i]->{'model'} = $l[1];
68 @dscsi = grep { $_->{'type'} == 0 } @dscsi;
71 # Check /proc/scsi/scsi for SCSI disk models
72 open(SCSI, "/proc/scsi/scsi");
73 local @lines = <SCSI>;
75 if ($lines[0] =~ /^Attached\s+domains/i) {
80 if (/Device:\s+(.*)(sd[a-z]+)\s+usage/) {
81 $dscsi = { 'dev' => $2 };
87 elsif (/Vendor:\s+(\S+)\s+Model:\s+(\S+)/ && $dscsi) {
88 $dscsi->{'make'} = $1;
89 $dscsi->{'model'} = $2;
91 elsif (/Host:\s+scsi(\d+)\s+Channel:\s+(\d+)\s+Id:\s+(\d+)\s+Lun:\s+(\d+)/ && $dscsi) {
92 $dscsi->{'host'} = $1;
94 $dscsi->{'target'} = $3;
107 elsif (/^\s+\S/ && @pscsi) {
108 $pscsi[$#pscsi] .= $_;
111 @pscsi = grep { /Type:\s+Direct-Access/i } @pscsi;
116 local (@disks, @devs, $d);
117 if (open(PARTS, "/proc/partitions")) {
118 # The list of all disks can come from the kernel
121 if (/\d+\s+\d+\s+\d+\s+sd([a-z]+)\s/ ||
122 /\d+\s+\d+\s+\d+\s+(scsi\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/disc)\s+/) {
123 # New or old style SCSI device
125 local ($host, $bus, $target, $lun) = ($2, $3, $4, $5);
126 if (!$dscsi_mode && $pscsi[$sc] =~ /USB-FDU/) {
127 # USB floppy with scsi emulation!
128 splice(@pscsi, $sc, 1);
132 local $scsidev = "/dev/$d";
135 &number_to_device("sd", $sc));
138 push(@devs, $scsidev);
142 push(@devs, "/dev/sd$d");
146 elsif (/\d+\s+\d+\s+\d+\s+hd([a-z]+)\s/) {
147 # IDE disk (but skip CDs)
149 if (open(MEDIA, "/proc/ide/hd$n/media")) {
150 local $media = <MEDIA>;
152 if ($media =~ /^disk/ && !$_[0]) {
153 push(@devs, "/dev/hd$n");
157 elsif (/\d+\s+\d+\s+\d+\s+(ide\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/disc)\s+/) {
159 local $idedev = "/dev/$1";
160 local ($host, $bus, $target, $lun) = ($2, $3, $4, $5);
163 &hbt_to_device($host, $bus, $target));
166 push(@devs, "/dev/$1");
169 elsif (/\d+\s+\d+\s+\d+\s+(rd\/c(\d+)d\d+)\s/) {
171 push(@devs, "/dev/$1");
173 elsif (/\d+\s+\d+\s+\d+\s+(ida\/c(\d+)d\d+)\s/) {
175 push(@devs, "/dev/$1");
177 elsif (/\d+\s+\d+\s+\d+\s+(cciss\/c(\d+)d\d+)\s/) {
178 # Compaq Smart Array RAID
179 push(@devs, "/dev/$1");
181 elsif (/\d+\s+\d+\s+\d+\s+(ataraid\/disc(\d+)\/disc)\s+/) {
182 # Promise raid controller
183 push(@devs, "/dev/$1");
189 @devs = sort { ($b =~ /\/hd[a-z]+$/ ? 1 : 0) <=>
190 ($a =~ /\/hd[a-z]+$/ ? 1 : 0) } @devs;
193 # Skip cd-rom drive, identified from symlink. Don't do this if we can identify
194 # cds by their media type though
195 if (!-d "/proc/ide") {
196 local @cdstat = stat("/dev/cdrom");
197 if (@cdstat && !$_[0]) {
198 @devs = grep { (stat($_))[1] != $cdstat[1] } @devs;
202 # Get Linux disk ID mapping
204 local $id_dir = "/dev/disk/by-id";
205 opendir(IDS, $id_dir);
206 foreach my $id (readdir(IDS)) {
207 local $id_link = readlink("$id_dir/$id");
209 local $id_real = &simplify_path(&resolve_links("$id_dir/$id"));
210 $id_map{$id_real} = $id;
215 # Call fdisk to get partition and geometry information
216 local $devs = join(" ", @devs);
219 open(FDISK, join(" ; ",
220 map { "parted $_ unit cyl print 2>/dev/null || ".
221 "fdisk -l $_ 2>/dev/null" } @devs)." |");
224 open(FDISK, "fdisk -l $devs 2>/dev/null |");
227 if (/Disk\s+([^ :]+):\s+(\d+)\s+\S+\s+(\d+)\s+\S+\s+(\d+)/ ||
228 ($m2 = ($_ =~ /Disk\s+([^ :]+):\s+(.*)\s+bytes/)) ||
229 ($m3 = ($_ =~ /Disk\s+([^ :]+):\s+([0-9\.]+)cyl/))) {
233 $disk = { 'device' => $1,
239 $disk = { 'device' => $1,
241 'table' => 'msdos', };
242 <FDISK> =~ /(\d+)\s+\S+\s+(\d+)\s+\S+\s+(\d+)/ || next;
243 $disk->{'heads'} = $1;
244 $disk->{'sectors'} = $2;
245 $disk->{'cylinders'} = $3;
249 $disk = { 'device' => $1,
254 'table' => 'msdos', };
256 $disk->{'index'} = scalar(@disks);
257 $disk->{'parts'} = [ ];
259 local @st = stat($disk->{'device'});
260 next if (@cdstat && $st[1] == $cdstat[1]);
261 if ($disk->{'device'} =~ /\/sd([a-z]+)$/) {
262 # Old-style SCSI disk
263 $disk->{'desc'} = &text('select_device', 'SCSI',
265 local ($dscsi) = grep { $_->{'dev'} eq "sd$1" } @dscsi;
266 $disk->{'scsi'} = $dscsi ? &indexof($dscsi, @dscsi)
268 $disk->{'type'} = 'scsi';
270 elsif ($disk->{'device'} =~ /\/hd([a-z]+)$/) {
272 $disk->{'desc'} = &text('select_device', 'IDE', uc($1));
273 $disk->{'type'} = 'ide';
275 elsif ($disk->{'device'} =~ /\/xvd([a-z]+)$/) {
277 $disk->{'desc'} = &text('select_device', 'Xen', uc($1));
278 $disk->{'type'} = 'ide';
280 elsif ($disk->{'device'} =~ /\/(scsi\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/disc)/) {
281 # New complete SCSI disk specification
282 $disk->{'host'} = $2;
284 $disk->{'target'} = $4;
286 $disk->{'desc'} = &text('select_scsi',
287 "$2", "$3", "$4", "$5");
289 # Work out the SCSI index for this disk
292 for($j=0; $j<@dscsi; $j++) {
293 if ($dscsi[$j]->{'host'} == $disk->{'host'} && $dscsi[$j]->{'bus'} == $disk->{'bus'} && $dscsi[$j]->{'target'} == $disk->{'target'} && $dscsi[$j]->{'lnun'} == $disk->{'lun'}) {
294 $disk->{'scsi'} = $j;
300 for($j=0; $j<@pscsi; $j++) {
301 if ($pscsi[$j] =~ /Host:\s+scsi(\d+).*Id:\s+(\d+)/i && $disk->{'host'} == $1 && $disk->{'target'} == $2) {
302 $disk->{'scsi'} = $j;
307 $disk->{'type'} = 'scsi';
308 $disk->{'prefix'} =~ s/disc$/part/g;
310 elsif ($disk->{'device'} =~ /\/(ide\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/disc)/) {
311 # New-style IDE specification
312 $disk->{'host'} = $2;
314 $disk->{'target'} = $4;
316 $disk->{'desc'} = &text('select_newide',
317 "$2", "$3", "$4", "$5");
318 $disk->{'type'} = 'ide';
319 $disk->{'prefix'} =~ s/disc$/part/g;
321 elsif ($disk->{'device'} =~ /\/(rd\/c(\d+)d(\d+))/) {
323 local ($mc, $md) = ($2, $3);
324 $disk->{'desc'} = &text('select_mylex', $mc, $md);
325 open(RD, "/proc/rd/c$mc/current_status");
327 if (/^Configuring\s+(.*)/i) {
328 $disk->{'model'} = $1;
330 elsif (/\s+(\S+):\s+([^, ]+)/ &&
331 $1 eq $disk->{'device'}) {
332 $disk->{'raid'} = $2;
336 $disk->{'type'} = 'raid';
337 $disk->{'prefix'} = $disk->{'device'}.'p';
339 elsif ($disk->{'device'} =~ /\/(ida\/c(\d+)d(\d+))/) {
341 local ($ic, $id) = ($2, $3);
342 $disk->{'desc'} = &text('select_cpq', $ic, $id);
343 open(IDA, -d "/proc/driver/array" ? "/proc/driver/array/ida$ic" : "/proc/driver/cpqarray/ida$ic");
345 if (/^(\S+):\s+(.*)/ && $1 eq "ida$ic") {
346 $disk->{'model'} = $2;
350 $disk->{'type'} = 'raid';
351 $disk->{'prefix'} = $disk->{'device'}.'p';
353 elsif ($disk->{'device'} =~ /\/(cciss\/c(\d+)d(\d+))/) {
354 # Compaq Smart Array RAID
355 local ($ic, $id) = ($2, $3);
356 $disk->{'desc'} = &text('select_smart', $ic, $id);
357 open(CCI, "/proc/driver/cciss/cciss$ic");
359 if (/^\s*(\S+):\s*(.*)/ && $1 eq "cciss$ic") {
360 $disk->{'model'} = $2;
364 $disk->{'type'} = 'raid';
365 $disk->{'prefix'} = $disk->{'device'}.'p';
367 elsif ($disk->{'device'} =~ /\/(ataraid\/disc(\d+)\/disc)/) {
368 # Promise RAID controller
370 $disk->{'desc'} = &text('select_promise', $dd);
371 $disk->{'type'} = 'raid';
372 $disk->{'prefix'} =~ s/disc$/part/g;
375 # Work out short name, like sda
377 if (defined($disk->{'host'})) {
378 $short = &hbt_to_device($disk->{'host'},
383 $short = $disk->{'device'};
384 $short =~ s/^.*\///g;
386 $disk->{'short'} = $short;
388 $disk->{'id'} = $id_map{$disk->{'device'}} ||
389 $id_map{"/dev/$short"};
393 elsif (/^Units\s+=\s+cylinders\s+of\s+(\d+)\s+\*\s+(\d+)/) {
394 # Unit size for disk from fdisk
395 $disk->{'bytes'} = $2;
396 $disk->{'cylsize'} = $disk->{'heads'} * $disk->{'sectors'} *
399 elsif (/BIOS\s+cylinder,head,sector\s+geometry:\s+(\d+),(\d+),(\d+)\.\s+Each\s+cylinder\s+is\s+(\d+)(b|kb|mb)/i) {
400 # Unit size for disk from parted
401 $disk->{'cylinders'} = $1;
402 $disk->{'heads'} = $2;
403 $disk->{'sectors'} = $3;
404 $disk->{'cylsize'} = $4 * (lc($5) eq "b" ? 1 :
405 lc($5) eq "kb" ? 1024 : 1024*1024);
406 $disk->{'bytes'} = $disk->{'cylsize'} / $disk->{'heads'} /
409 elsif (/(\/dev\/\S+?(\d+))[ \t*]+\d+\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S{1,2})\s+(.*)/ || /(\/dev\/\S+?(\d+))[ \t*]+(\d+)\s+(\d+)\s+(\S+)\s+(\S{1,2})\s+(.*)/) {
410 # Partition within the current disk from fdisk
411 local $part = { 'number' => $2,
417 'extended' => $6 eq '5' || $6 eq 'f' ? 1 : 0,
418 'index' => scalar(@{$disk->{'parts'}}),
420 $part->{'desc'} = &partition_description($part->{'device'});
421 push(@{$disk->{'parts'}}, $part);
423 elsif (/^\s*(\d+)\s+(\d+)cyl\s+(\d+)cyl\s+(\d+)cyl(\s+(primary|logical|extended))?\s*(\S*)\s*(\S*)/) {
424 # Partition within the current disk from parted
425 local $part = { 'number' => $1,
426 'device' => $disk->{'device'}.$1,
430 'blocks' => $4 * $disk->{'cylsize'},
431 'extended' => $6 eq 'extended' ? 1 : 0,
432 'index' => scalar(@{$disk->{'parts'}}),
435 $part->{'type'} = 'ext2' if ($part->{'type'} =~ /^ext/);
436 $part->{'desc'} = &partition_description($part->{'device'});
437 push(@{$disk->{'parts'}}, $part);
439 elsif (/Partition\s+Table:\s+(\S+)/) {
440 # Parted partition table type
441 $disk->{'table'} = $1;
446 # Check /proc/ide for IDE disk models
447 foreach $d (@disks) {
448 if ($d->{'type'} eq 'ide') {
449 local $short = $d->{'short'};
450 $d->{'model'} = &read_file_contents("/proc/ide/$short/model");
451 $d->{'model'} =~ s/\r|\n//g;
452 $d->{'media'} = &read_file_contents("/proc/ide/$short/media");
453 $d->{'media'} =~ s/\r|\n//g;
457 # Fill in SCSI information
458 foreach $d (@disks) {
459 if ($d->{'type'} eq 'scsi') {
460 local $s = $d->{'scsi'};
461 local $sysdir = "/sys/block/$d->{'short'}/device";
463 # From kernel 2.6.30+ sys directory
464 $d->{'model'} = &read_file_contents("$sysdir/vendor").
466 &read_file_contents("$sysdir/model");
467 $d->{'model'} =~ s/\r|\n//g;
468 $d->{'media'} = &read_file_contents("$sysdir/media");
469 $d->{'media'} =~ s/\r|\n//g;
471 elsif ($dscsi_mode) {
472 # From other scsi files
473 $d->{'model'} = "$dscsi[$s]->{'make'} $dscsi[$s]->{'model'}";
474 $d->{'controller'} = $dscsi[$s]->{'host'};
475 $d->{'scsiid'} = $dscsi[$s]->{'target'};
478 # From /proc/scsi/scsi lines
479 if ($pscsi[$s] =~ /Vendor:\s+(\S+).*Model:\s+(.*)\s+Rev:/i) {
480 $d->{'model'} = "$1 $2";
482 if ($pscsi[$s] =~ /Host:\s+scsi(\d+).*Id:\s+(\d+)/i) {
483 $d->{'controller'} = int($1);
484 $d->{'scsiid'} = int($2);
487 if ($d->{'model'} =~ /ATA/) {
488 # Fake SCSI disk, actually IDE
490 $d->{'desc'} =~ s/SCSI/SATA/g;
491 foreach my $p (@{$d->{'parts'}}) {
492 $p->{'desc'} =~ s/SCSI/SATA/g;
498 @list_disks_partitions_cache = @disks;
502 # partition_description(device)
503 # Converts a device path like /dev/hda into a human-readable name
504 sub partition_description
507 return $device =~ /(.)d([a-z]+)(\d+)$/ ?
508 &text('select_part', $1 eq 's' ? 'SCSI' : 'IDE', uc($2), "$3") :
509 $device =~ /scsi\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/part(\d+)/ ?
510 &text('select_spart', "$1", "$2", "$3", "$4", "$5") :
511 $device =~ /ide\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/part(\d+)/ ?
512 &text('select_snewide', "$1", "$2", "$3", "$4", "$5") :
513 $device =~ /rd\/c(\d+)d(\d+)p(\d+)$/ ?
514 &text('select_mpart', "$1", "$2", "$3") :
515 $device =~ /ida\/c(\d+)d(\d+)p(\d+)$/ ?
516 &text('select_cpart', "$1", "$2", "$3") :
517 $device =~ /cciss\/c(\d+)d(\d+)p(\d+)$/ ?
518 &text('select_smartpart', "$1", "$2", "$3") :
519 $device =~ /ataraid\/disc(\d+)\/part(\d+)$/ ?
520 &text('select_ppart', "$1", "$2") :
524 # hbt_to_device(host, bus, target)
525 # Converts an IDE device specified as a host, bus and target to an hdX device
528 local ($host, $bus, $target) = @_;
529 local $num = $host*4 + $bus*2 + $target;
530 return &number_to_device("hd", $num);
533 # number_to_device(suffix, number)
536 local ($suffix, $num) = @_;
538 # Just a single letter
539 return $suffix.(('a' .. 'z')[$num]);
543 local $first = int($num / 26);
544 local $second = $num % 26;
545 return $suffix.(('a' .. 'z')[$first]).(('a' .. 'z')[$second]);
549 # change_type(disk, partition, type)
550 # Changes the type of an existing partition
553 &open_fdisk("$_[0]");
555 local $rv = &wait_for($fh, 'Partition.*:', 'Selected partition');
556 &wprint("$_[1]\n") if ($rv == 0);
557 &wait_for($fh, 'Hex.*:');
559 &wait_for($fh, 'Command.*:');
560 &wprint("w\n"); sleep(1);
562 undef(@list_disks_partitions_cache);
565 # delete_partition(disk, partition)
566 # Delete an existing partition
569 my ($disk, $part) = @_;
572 my $cmd = "parted -s ".$disk." rm ".$part;
573 my $out = &backquote_logged("$cmd </dev/null 2>&1");
575 &error("$cmd failed : $out");
582 local $rv = &wait_for($fh, 'Partition.*:', 'Selected partition');
583 &wprint("$part\n") if ($rv == 0);
584 &wait_for($fh, 'Command.*:');
586 &wait_for($fh, 'Syncing');
590 undef(@list_disks_partitions_cache);
593 # create_partition(disk, partition, start, end, type)
594 # Create a new partition with the given extent and type
597 my ($disk, $part, $start, $end, $type) = @_;
600 my $pe = $part > 4 ? "logical" : "primary";
603 $cmd = "parted -s ".$disk." unit cyl mkpartfs ".$pe." ".
604 $type." ".($start-1)." ".$end;
607 $cmd = "parted -s ".$disk." unit cyl mkpart ".$pe." ".
610 my $out = &backquote_logged("$cmd </dev/null 2>&1");
612 &error("$cmd failed : $out");
619 local $wf = &wait_for($fh, 'primary.*\r?\n', 'First.*:');
625 local $wf2 = &wait_for($fh, 'Partition.*:',
626 'Selected partition');
627 &wprint("$part\n") if ($wf2 == 0);
629 &wait_for($fh, 'First.*:') if ($wf != 1);
631 &wait_for($fh, 'Last.*:');
633 &wait_for($fh, 'Command.*:');
636 local $rv = &wait_for($fh, 'Partition.*:', 'Selected partition');
637 &wprint("$part\n") if ($rv == 0);
638 &wait_for($fh, 'Hex.*:');
640 &wait_for($fh, 'Command.*:');
642 &wait_for($fh, 'Syncing'); sleep(3);
645 undef(@list_disks_partitions_cache);
648 # create_extended(disk, partition, start, end)
649 # Create a new extended partition
652 my ($disk, $part, $start, $end) = @_;
654 # Create using parted
655 my $cmd = "parted -s ".$disk." unit cyl mkpart extended ".
657 my $out = &backquote_logged("$cmd </dev/null 2>&1");
659 &error("$cmd failed : $out");
666 &wait_for($fh, 'primary.*\r?\n');
668 &wait_for($fh, 'Partition.*:');
670 &wait_for($fh, 'First.*:');
672 &wait_for($fh, 'Last.*:');
674 &wait_for($fh, 'Command.*:');
677 &wait_for($fh, 'Syncing');
681 undef(@list_disks_partitions_cache);
685 # Returns a list of known partition tag numbers
690 return sort { $a cmp $b } (keys %parted_tags);
693 # Classic fdisk types
694 return sort { hex($a) <=> hex($b) } (keys %tags);
699 # Returns a human-readable version of a tag
702 return $tags{$_[0]} || $parted_tags{$_[0]} || $hidden_tags{$_[0]};
707 return $has_parted ? 'ext2' : '83';
711 # Given a partition tag, returns the filesystem type (assuming it is supported)
717 # Use parted type names
718 if ($tag eq "fat16") {
721 elsif ($tag eq "fat32") {
724 elsif ($tag eq "ext2") {
725 @rv = ( "ext3", "ext4", "ext2", "xfs", "reiserfs" );
727 elsif ($tag eq "hfs") {
730 elsif ($tag eq "linux-swap") {
733 elsif ($tag eq "NTFS") {
736 elsif ($tag eq "reiserfs") {
739 elsif ($tag eq "ufs") {
748 if ($tag eq "4" || $tag eq "6" || $tag eq "1" || $tag eq "e") {
751 elsif ($tag eq "b" || $tag eq "c") {
754 elsif ($tag eq "83") {
755 @rv = ( "ext3", "ext4", "ext2", "xfs", "reiserfs" );
757 elsif ($tag eq "82") {
760 elsif ($tag eq "81") {
767 local %supp = map { $_, 1 } &mount::list_fstypes();
768 @rv = grep { $supp{$_} } @rv;
769 return wantarray ? @rv : $rv[0];
773 # Returns a readable name for a filesystem type
776 return $text{"fs_".$_[0]};
781 if ($_[0] eq "ext2") {
782 &opt_input("ext2_b", $text{'bytes'}, 1);
783 &opt_input("ext2_f", $text{'bytes'}, 0);
784 &opt_input("ext2_i", "", 1);
785 &opt_input("ext2_m", "%", 0);
786 &opt_input("ext2_g", "", 1);
787 print &ui_table_row($text{'ext2_c'},
788 &ui_yesno_radio("ext2_c", 0));
790 elsif ($_[0] eq "msdos" || $_[0] eq "vfat") {
791 &opt_input("msdos_ff", "", 1);
792 print &ui_table_row($text{'msdos_F'},
793 &ui_select("msdos_F", undef,
794 [ [ undef, $text{'default'} ],
795 [ 12 ], [ 16 ], [ 32 ],
796 [ "*", $text{'msdos_F_other'} ] ])." ".
797 &ui_textbox("msdos_F_other", undef, 4));
798 &opt_input("msdos_i", "", 1);
799 &opt_input("msdos_n", "", 0);
800 &opt_input("msdos_r", "", 1);
801 &opt_input("msdos_s", "sectors", 0);
802 print &ui_table_row($text{'msdos_c'},
803 &ui_yesno_radio("msdos_c", 0));
805 elsif ($_[0] eq "minix") {
806 &opt_input("minix_n", "", 1);
807 &opt_input("minix_i", "", 0);
808 &opt_input("minix_b", "", 1);
809 print &ui_table_row($text{'minix_c'},
810 &ui_yesno_radio("minix_c", 0));
812 elsif ($_[0] eq "reiserfs") {
813 print &ui_table_row($text{'reiserfs_force'},
814 &ui_yesno_radio("reiserfs_f", 0));
816 print &ui_table_row($text{'reiserfs_hash'},
817 &ui_select("reiserfs_h", "",
818 [ [ "", $text{'default'} ],
819 [ "rupasov", "tea" ] ]));
821 elsif ($_[0] =~ /^ext\d+$/) {
822 &opt_input("ext2_b", $text{'bytes'}, 1);
823 &opt_input("ext2_f", $text{'bytes'}, 0);
824 &opt_input("ext2_i", "", 1);
825 &opt_input("ext2_m", "%", 0);
826 &opt_input("ext3_j", "MB", 1);
827 print &ui_table_row($text{'ext2_c'},
828 &ui_yesno_radio("ext2_c", 0));
830 elsif ($_[0] eq "xfs") {
831 print &ui_table_row($text{'xfs_force'},
832 &ui_yesno_radio("xfs_f", 0));
833 &opt_input("xfs_b", $text{'bytes'}, 0);
835 elsif ($_[0] eq "jfs") {
836 &opt_input("jfs_s", $text{'megabytes'}, 1);
837 print &ui_table_row($text{'jfs_c'},
838 &ui_yesno_radio("jfs_c", 0));
840 elsif ($_[0] eq "fatx") {
842 print &ui_table_row(undef, $text{'fatx_none'}, 4);
846 # mkfs_parse(type, device)
847 # Returns a command to build a new filesystem of the given type on the
848 # given device. Options are taken from %in.
852 if ($_[0] eq "ext2") {
853 $cmd = "mkfs -t ext2";
854 $cmd .= &opt_check("ext2_b", '\d+', "-b");
855 $cmd .= &opt_check("ext2_f", '\d+', "-f");
856 $cmd .= &opt_check("ext2_i", '\d{4,}', "-i");
857 $cmd .= &opt_check("ext2_m", '\d+', "-m");
858 $cmd .= &opt_check("ext2_g", '\d+', "-g");
859 $cmd .= $in{'ext2_c'} ? " -c" : "";
863 elsif ($_[0] eq "msdos" || $_[0] eq "vfat") {
864 $cmd = "mkfs -t $_[0]";
865 $cmd .= &opt_check("msdos_ff", '[1-2]', "-f");
866 if ($in{'msdos_F'} eq '*') {
867 $in{'msdos_F_other'} =~ /^\d+$/ ||
868 &error(&text('opt_error', $in{'msdos_F_other'},
870 $cmd .= " -F ".$in{'msdos_F_other'};
872 elsif ($in{'msdos_F'}) {
873 $cmd .= " -F ".$in{'msdos_F'};
875 $cmd .= &opt_check("msdos_i", '[0-9a-f]{8}', "-i");
876 $cmd .= &opt_check("msdos_n", '\S{1,11}', "-n");
877 $cmd .= &opt_check("msdos_r", '\d+', "-r");
878 $cmd .= &opt_check("msdos_s", '\d+', "-s");
879 $cmd .= $in{'msdos_c'} ? " -c" : "";
882 elsif ($_[0] eq "minix") {
883 local(@plist, $disk, $part, $i, @pinfo);
884 $cmd = "mkfs -t minix";
885 $cmd .= &opt_check("minix_n", '14|30', "-n ");
886 $cmd .= &opt_check("minix_i", '\d+', "-i ");
887 $cmd .= $in{'minix_c'} ? " -c" : "";
888 $cmd .= &opt_check("minix_b", '\d+', " ");
891 elsif ($_[0] eq "reiserfs") {
892 $cmd = "yes | mkreiserfs";
893 $cmd .= " -f" if ($in{'reiserfs_f'});
894 $cmd .= " -h $in{'reiserfs_h'}" if ($in{'reiserfs_h'});
897 elsif ($_[0] =~ /^ext\d+$/) {
898 if (&has_command("mkfs.$_[0]")) {
899 $cmd = "mkfs -t $_[0]";
900 $cmd .= &opt_check("ext3_j", '\d+', "-j");
902 elsif ($_[0] eq "ext3" && &has_command("mke3fs")) {
904 $cmd .= &opt_check("ext3_j", '\d+', "-j");
906 elsif ($_[0] eq "ext4" && &has_command("mke4fs")) {
908 $cmd .= &opt_check("ext3_j", '\d+', "-j");
911 $cmd = "mkfs.ext2 -j";
912 if (!$in{'ext3_j_def'}) {
913 $in{'ext3_j'} =~ /^\d+$/ ||
914 &error(&text('opt_error', $in{'ext3_j'},
916 $cmd .= " -J size=$in{'ext3_j'}";
919 $cmd .= &opt_check("ext2_b", '\d+', "-b");
920 $cmd .= &opt_check("ext2_f", '\d+', "-f");
921 $cmd .= &opt_check("ext2_i", '\d{4,}', "-i");
922 $cmd .= &opt_check("ext2_m", '\d+', "-m");
923 $cmd .= $in{'ext2_c'} ? " -c" : "";
927 elsif ($_[0] eq "xfs") {
928 $cmd = "mkfs -t $_[0]";
929 $cmd .= " -f" if ($in{'xfs_f'});
930 $cmd .= " -b size=$in{'xfs_b'}" if (!$in{'xfs_b_def'});
933 elsif ($_[0] eq "jfs") {
934 $cmd = "mkfs -t $_[0] -q";
935 $cmd .= &opt_check("jfs_s", '\d+', "-s");
936 $cmd .= " -c" if ($in{'jfs_c'});
939 elsif ($_[0] eq "fatx") {
940 $cmd = "mkfs -t $_[0] $_[1]";
942 if (&has_command("partprobe")) {
943 $cmd .= "partprobe ; $cmd";
949 # Returns 1 if this filesystem type can be tuned
952 return $_[0] =~ /^ext\d+$/;
955 # tunefs_options(type)
956 # Output HTML for tuning options for some filesystem type
959 if ($_[0] =~ /^ext\d+$/) {
960 # Gaps between checks
961 &opt_input("tunefs_c", "", 1);
964 print &ui_table_row($text{'tunefs_e'},
965 &ui_radio("tunefs_e_def", 1,
966 [ [ 1, $text{'opt_default'} ],
967 [ 0, &ui_select("tunefs_e", undef,
968 [ [ "continue", $text{'tunefs_continue'} ],
969 [ "remount-ro", $text{'tunefs_remount'} ],
970 [ "panic", $text{'tunefs_panic'} ] ]) ] ]));
973 print &ui_table_row($text{'tunefs_u'},
974 &ui_opt_textbox("tunefs_u", undef, 13, $text{'opt_default'})." ".
975 &user_chooser_button("tunefs_u", 0));
978 print &ui_table_row($text{'tunefs_g'},
979 &ui_opt_textbox("tunefs_g", undef, 13, $text{'opt_default'})." ".
980 &group_chooser_button("tunefs_g", 0));
983 &opt_input("tunefs_m", "%", 1);
985 # Time between checks
986 $tsel = &ui_select("tunefs_i_unit", undef,
987 [ [ "d", $text{'tunefs_days'} ],
988 [ "w", $text{'tunefs_weeks'} ],
989 [ "m", $text{'tunefs_months'} ] ]);
990 &opt_input("tunefs_i", $tsel, 0);
994 # tunefs_parse(type, device)
995 # Returns the tuning command based on user inputs
998 if ($_[0] =~ /^ext\d+$/) {
1000 $cmd .= &opt_check("tunefs_c", '\d+', "-c");
1001 $cmd .= $in{'tunefs_e_def'} ? "" : " -e$in{'tunefs_e'}";
1002 $cmd .= $in{'tunefs_u_def'} ? "" : " -u".getpwnam($in{'tunefs_u'});
1003 $cmd .= $in{'tunefs_g_def'} ? "" : " -g".getgrnam($in{'tunefs_g'});
1004 $cmd .= &opt_check("tunefs_m",'\d+',"-m");
1005 $cmd .= &opt_check("tunefs_i", '\d+', "-i").
1006 ($in{'tunefs_i_def'} ? "" : $in{'tunefs_i_unit'});
1013 # Returns 1 if a reboot is needed after changing the partitions on some disk
1016 local $un = `uname -r`;
1017 return $un =~ /^2\.0\./ || $un =~ /^1\./ || $un =~ /^0\./;
1020 # device_status(device)
1021 # Returns an array of directory, type, mounted
1024 @mounted = &foreign_call("mount", "list_mounted") if (!@mounted);
1025 @mounts = &foreign_call("mount", "list_mounts") if (!@mounts);
1026 local $label = &get_label($_[0]);
1027 local $volid = &get_volid($_[0]);
1029 local ($mounted) = grep { &same_file($_->[1], $_[0]) ||
1030 $_->[1] eq "LABEL=$label" ||
1031 $_->[1] eq "UUID=$volid" } @mounted;
1032 local ($mount) = grep { &same_file($_->[1], $_[0]) ||
1033 $_->[1] eq "LABEL=$label" ||
1034 $_->[1] eq "UUID=$volid" } @mounts;
1035 if ($mounted) { return ($mounted->[0], $mounted->[2], 1,
1036 &indexof($mount, @mounts),
1037 &indexof($mounted, @mounted)); }
1038 elsif ($mount) { return ($mount->[0], $mount->[2], 0,
1039 &indexof($mount, @mounts)); }
1041 $raidconf = &foreign_call("raid", "get_raidtab") if (!$raidconf);
1042 foreach $c (@$raidconf) {
1043 foreach $d (&raid::find_value('device', $c->{'members'})) {
1044 return ( $c->{'value'}, "raid", 1 ) if ($d eq $_[0]);
1049 if (!scalar(@physical_volumes)) {
1050 @physical_volumes = ();
1051 foreach $vg (&foreign_call("lvm", "list_volume_groups")) {
1052 push(@physical_volumes,
1053 &foreign_call("lvm", "list_physical_volumes",
1057 foreach $pv (@physical_volumes) {
1058 return ( $pv->{'vg'}, "lvm", 1)
1059 if ($pv->{'device'} eq $_[0]);
1066 # Returns 1 if some filesystem type can fsck'd
1069 return ($_[0] =~ /^ext\d+$/ && &has_command("fsck.$_[0]") ||
1070 $_[0] eq "minix" && &has_command("fsck.minix"));
1073 # fsck_command(type, device)
1074 # Returns the fsck command to unconditionally check a filesystem
1077 if ($_[0] =~ /^ext\d+$/) {
1078 return "fsck -t $_[0] -p $_[1]";
1080 elsif ($_[0] eq "minix") {
1081 return "fsck -t minix -a $_[1]";
1086 # Returns a description of an exit code from fsck
1089 return $text{"fsck_err$_[0]"} ? $text{"fsck_err$_[0]"}
1090 : &text("fsck_unknown", $_[0]);
1093 # partition_select(name, value, mode, [&found], [disk_regexp])
1094 # Returns HTML for selecting a disk or partition
1095 # mode 0 = floppies and disk partitions
1097 # 2 = floppies and disks and disk partitions
1098 # 3 = disk partitions
1099 sub partition_select
1101 local $rv = "<select name=$_[0]>\n";
1102 local ($found, $d, $p);
1103 if (($_[2] == 0 || $_[2] == 2) &&
1104 (-r "/dev/fd0" || $_[1] =~ /^\/dev\/fd[01]$/)) {
1105 $rv .= sprintf "<option %s value=/dev/fd0>%s\n",
1106 $_[1] eq "/dev/fd0" ? "selected" : "",
1107 &text('select_fd', 0) if (!$_[4] || "/dev/fd0" =~ /$_[4]/);
1108 $rv .= sprintf "<option %s value=/dev/fd1>%s\n",
1109 $_[1] eq "/dev/fd1" ? "selected" : "",
1110 &text('select_fd', 1) if (!$_[4] || "/dev/fd1" =~ /$_[4]/);
1111 $found++ if ($_[1] =~ /^\/dev\/fd[01]$/);
1113 local @dlist = &list_disks_partitions();
1114 foreach $d (@dlist) {
1115 local $dev = $d->{'device'};
1116 next if ($_[4] && $dev !~ /$_[4]/);
1117 if ($_[2] == 1 || $_[2] == 2) {
1118 local $name = $d->{'desc'};
1119 $name .= " ($d->{'model'})" if ($d->{'model'});
1120 $rv .= sprintf "<option value=%s %s>%s\n",
1121 $dev, $_[1] eq $dev ? "selected" : "", $name;
1122 $found++ if ($dev eq $_[1]);
1124 if ($_[2] == 0 || $_[2] == 2 || $_[2] == 3) {
1125 foreach $p (@{$d->{'parts'}}) {
1126 next if ($p->{'extended'});
1127 local $name = $p->{'desc'};
1128 $name .= " (".&tag_name($p->{'type'}).")"
1129 if (&tag_name($p->{'type'}));
1130 $rv .= sprintf "<option %s value=%s>%s\n",
1131 $_[1] eq $p->{'device'} ? "selected" : "",
1132 $p->{'device'}, $name;
1133 $found++ if ($_[1] eq $p->{'device'});
1137 if (!$found && $_[1] && !$_[3]) {
1138 $rv .= "<option selected>$_[1]\n";
1143 $rv .= "</select>\n";
1147 # label_select(name, value, &found)
1148 # Returns HTML for selecting a filesystem label
1151 local $rv = "<select name=$_[0]>\n";
1152 local @dlist = &list_disks_partitions();
1154 foreach $d (@dlist) {
1155 local $dev = $d->{'device'};
1156 foreach $p (@{$d->{'parts'}}) {
1157 next if ($p->{'type'} ne '83');
1158 local $label = &get_label($p->{'device'});
1160 $rv .= sprintf "<option %s value=%s>%s (%s)\n",
1161 $_[1] eq $label ? "selected" : "",
1162 $label, $label, $p->{'desc'};
1163 ${$_[2]}++ if ($_[1] eq $label);
1167 if (!$found && $_[1] && !$_[2]) {
1168 $rv .= "<option selected>$_[1]\n";
1170 $rv .= "</select>\n";
1171 return $any ? $rv : undef;
1174 # volid_select(name, value, &found)
1175 # Returns HTML for selecting a filesystem UUID
1178 local ($name, $value, $found) = @_;
1179 local @dlist = &list_disks_partitions();
1181 foreach my $d (@dlist) {
1182 local $dev = $d->{'device'};
1183 foreach $p (@{$d->{'parts'}}) {
1184 next if ($p->{'type'} ne '83' && $p->{'type'} ne '82' &&
1185 $p->{'type'} ne 'b' && $p->{'type'} ne 'c');
1186 local $volid = &get_volid($p->{'device'});
1188 push(@opts, [ $volid, "$volid ($p->{'desc'})" ]);
1189 $$found++ if ($value eq $volid);
1193 return &ui_select($name, $value, \@opts, 1, 0, $value ? 1 : 0);
1200 #############################################################################
1201 # Internal functions
1202 #############################################################################
1205 local $fpath = &check_fdisk();
1206 ($fh, $fpid) = &foreign_call("proc", "pty_process_exec", join(" ",$fpath, @_));
1211 local $sfpath = &has_command("sfdisk");
1212 ($fh, $fpid) = &foreign_call("proc", "pty_process_exec", join(" ",$sfpath, @_));
1217 local $fpath = &has_command("fdisk");
1218 &error(&text('open_error', "<tt>fdisk</tt>")) if (!$fpath);
1224 close($fh); kill('TERM', $fpid);
1229 syswrite($fh, $_[0], length($_[0]));
1234 print &ui_table_row($text{$_[0]},
1235 &ui_opt_textbox($_[0], undef, 6, $text{'opt_default'})." ".$_[1]);
1240 if ($in{"$_[0]_def"}) { return ""; }
1241 elsif ($in{$_[0]} !~ /^$_[1]$/) {
1242 &error(&text('opt_error', $in{$_[0]}, $text{$_[0]}));
1244 else { return " $_[2] $in{$_[0]}"; }
1247 %tags = ('0', 'Empty',
1255 '9', 'AIX bootable',
1256 'a', 'OS/2 boot manager',
1257 'b', 'Windows FAT32',
1258 'c', 'Windows FAT32 LBA',
1259 'e', 'Windows FAT16 LBA',
1261 '11', 'Hidden FAT12',
1262 '12', 'Compaq diagnostic',
1263 '14', 'Hidden FAT16 < 32M',
1264 '16', 'Hidden FAT16',
1265 '17', 'Hidden NTFS',
1266 '18', 'AST Windows swapfile',
1267 '1b', 'Hidden Windows FAT (1b)',
1268 '1c', 'Hidden Windows FAT (1c)',
1269 '1e', 'Hidden Windows FAT (1e)',
1271 '3c', 'PartitionMagic recovery',
1272 '40', 'Venix 80286',
1273 '41', 'PPC PReP boot',
1276 '4e', 'QNX 4.x 2nd partition',
1277 '4f', 'QNX 4.x 3rd partition',
1279 '51', 'OnTrack DM6 Aux1',
1281 '53', 'OnTrack DM6 Aux3',
1282 '54', 'OnTrack DM6',
1285 '5c', 'Priam Edisk',
1287 '63', 'GNU HURD or SysV',
1288 '64', 'Novell Netware 286',
1289 '65', 'Novell Netware 386',
1290 '70', 'DiskSecure Multi-Boot',
1293 '81', 'Minix / Old Linux / Solaris',
1296 '84', 'OS/2 hidden C: drive',
1297 '85', 'Linux extended',
1298 '86', 'NTFS volume set (86)',
1299 '87', 'NTFS volume set (87)',
1303 'a0', 'IBM Thinkpad hibernation',
1307 'b7', 'BSDI filesystem',
1309 'c1', 'DRDOS/sec FAT12',
1310 'c4', 'DRDOS/sec FAT16 <32M',
1311 'c6', 'DRDOS/sec FAT16',
1313 'db', 'CP/M / CTOS',
1315 'e3', 'DOS read-only',
1320 'f4', 'SpeedStor large partition',
1321 'f2', 'DOS secondary',
1329 'f', 'Windows extended LBA',
1334 'fat16', 'Windows FAT16',
1335 'fat32', 'Windows FAT32',
1338 'linux-swap', 'Linux Swap',
1339 'NTFS', 'Windows NTFS',
1340 'reiserfs', 'ReiserFS',
1341 'ufs', 'FreeBSD UFS',
1344 @space_type = ( '1', '4', '5', '6', 'b', 'c', 'e', '83' );
1346 # can_edit_disk(device)
1350 $device =~ s/\d+$//;
1351 foreach (split(/\s+/, $access{'disks'})) {
1352 return 1 if ($_ eq "*" || $_ eq $device);
1357 # disk_space(device, [mountpoint])
1358 # Returns the amount of total and free space for some filesystem, or an
1359 # empty array if not appropriate.
1362 local $w = $_[1] || $_[0];
1363 local $out = `df -k '$w'`;
1364 if ($out =~ /Mounted on\s*\n\s*\S+\s+(\S+)\s+\S+\s+(\S+)/i) {
1367 elsif ($out =~ /Mounted on\s*\n\S+\s*\n\s+(\S+)\s+\S+\s+(\S+)/i) {
1375 # supported_filesystems()
1376 # Returns a list of filesystem types that can have mkfs_options called on them
1377 sub supported_filesystems
1379 local @fstypes = ( "ext2" );
1380 push(@fstypes, "ext3") if (&has_command("mkfs.ext3") ||
1381 &has_command("mke3fs") ||
1382 `mkfs.ext2 -h 2>&1` =~ /\[-j\]/);
1383 push(@fstypes, "ext4") if (&has_command("mkfs.ext4") ||
1384 &has_command("mke4fs"));
1385 push(@fstypes, "reiserfs") if (&has_command("mkreiserfs"));
1386 push(@fstypes, "xfs") if (&has_command("mkfs.xfs"));
1387 push(@fstypes, "jfs") if (&has_command("mkfs.jfs"));
1388 push(@fstypes, "fatx") if (&has_command("mkfs.fatx"));
1389 push(@fstypes, "msdos");
1390 push(@fstypes, "vfat");
1391 push(@fstypes, "minix");
1395 # get_label(device, [type])
1396 # Returns the XFS or EXT label for some device's filesystem
1401 $label = `e2label $_[0] 2>&1`;
1404 if (($? || $label !~ /\S/) && $has_xfs_db) {
1406 local $out = &backquote_with_timeout("xfs_db -x -p xfs_admin -c label -r $_[0] 2>&1", 5);
1407 $label = $1 if ($out =~ /label\s*=\s*"(.*)"/ &&
1410 if (($? || $label !~ /\S/) && $has_reiserfstune) {
1412 local $out = &backquote_command("reiserfstune $_[0]");
1413 if ($out =~ /LABEL:\s*(\S+)/) {
1417 return $? || $label !~ /\S/ ? undef : $label;
1421 # Returns the UUID for some device's filesystem
1424 local ($device) = @_;
1426 if (-d $uuid_directory) {
1427 # Use UUID mapping directory
1428 opendir(DIR, $uuid_directory);
1429 foreach my $f (readdir(DIR)) {
1430 local $linkdest = &simplify_path(
1431 &resolve_links("$uuid_directory/$f"));
1432 if ($linkdest eq $device) {
1439 elsif ($has_volid) {
1440 # Use vol_id command
1441 local $out = &backquote_command(
1442 "vol_id ".quotemeta($device)." 2>&1", 1);
1443 if ($out =~ /ID_FS_UUID=(\S+)/) {
1450 # set_label(device, label, [type])
1451 # Tries to set the label for some device's filesystem
1454 if ($has_e2label && ($_[2] =~ /^ext[23]$/ || !$_[2])) {
1455 &system_logged("e2label '$_[0]' '$_[1]' >/dev/null 2>&1");
1458 if ($has_xfs_db && ($_[2] eq "xfs" || !$_[2])) {
1459 &system_logged("xfs_db -x -p xfs_admin -c \"label $_[1]\" $_[0] >/dev/null 2>&1");
1465 # set_name(&disk, &partition, name)
1466 # Sets the name of a partition, for partition types that support it
1469 my ($dinfo, $pinfo, $name) = @_;
1470 my $cmd = "parted -s ".$dinfo->{'device'}." name ".$pinfo->{'number'}." ";
1472 $cmd .= quotemeta($name);
1477 my $out = &backquote_logged("$cmd </dev/null 2>&1");
1479 &error("$cmd failed : $out");
1483 # set_partition_table(device, table-type)
1484 # Wipe and re-create the partition table on some disk
1485 sub set_partition_table
1487 my ($disk, $table) = @_;
1488 my $cmd = "parted -s ".$disk." mktable ".$table;
1489 my $out = &backquote_logged("$cmd </dev/null 2>&1");
1491 &error("$cmd failed : $out");
1495 # supports_label(&partition)
1496 # Returns 1 if the label can be set on a partition
1500 return $part->{'type'} eq '83' || $part->{'type'} eq 'ext2';
1503 # supports_name(&disk)
1504 # Returns 1 if the name can be set on a disk's partitions
1508 return $disk->{'table'} eq 'gpt';
1511 # supports_hdparm(&disk)
1515 return $d->{'type'} eq 'ide' || $d->{'type'} eq 'scsi' && $d->{'model'} =~ /ATA/;
1518 # supports_relabel(&disk)
1519 # Return 1 if a disk can have it's partition table re-written
1520 sub supports_relabel
1522 return $has_parted ? 1 : 0;
1525 # supports_smart(&disk)
1528 return &foreign_installed("smart-status") &&
1529 &foreign_available("smart-status");
1532 # supports_extended(&disk)
1533 # Return 1 if some disk can support extended partitions
1534 sub supports_extended
1537 return $disk->{'label'} eq 'msdos' ? 1 : 0;
1540 # list_table_types(&disk)
1541 # Returns the list of supported partition table types for a disk
1542 sub list_table_types
1545 return ( 'msdos', 'gpt', 'bsd', 'dvh', 'loop', 'mac', 'pc98', 'sun' );
1552 # get_parted_version()
1553 # Returns the version number of parted that is installed
1554 sub get_parted_version
1556 my $out = &backquote_command("parted -v 2>&1 </dev/null");
1557 return $out =~ /parted.*\s([0-9\.]+)/i ? $1 : undef;