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");
185 elsif (/\d+\s+\d+\s+\d+\s+(vd[a-z]+)\s/) {
186 # Virtio disk from KVM
187 push(@devs, "/dev/$1");
193 @devs = sort { ($b =~ /\/hd[a-z]+$/ ? 1 : 0) <=>
194 ($a =~ /\/hd[a-z]+$/ ? 1 : 0) } @devs;
197 # Skip cd-rom drive, identified from symlink. Don't do this if we can identify
198 # cds by their media type though
199 if (!-d "/proc/ide") {
200 local @cdstat = stat("/dev/cdrom");
201 if (@cdstat && !$_[0]) {
202 @devs = grep { (stat($_))[1] != $cdstat[1] } @devs;
206 # Get Linux disk ID mapping
208 local $id_dir = "/dev/disk/by-id";
209 opendir(IDS, $id_dir);
210 foreach my $id (readdir(IDS)) {
211 local $id_link = readlink("$id_dir/$id");
213 local $id_real = &simplify_path(&resolve_links("$id_dir/$id"));
214 $id_map{$id_real} = $id;
219 # Call fdisk to get partition and geometry information
220 local $devs = join(" ", @devs);
223 open(FDISK, join(" ; ",
224 map { "parted $_ unit cyl print 2>/dev/null || ".
225 "fdisk -l $_ 2>/dev/null" } @devs)." |");
228 open(FDISK, "fdisk -l $devs 2>/dev/null |");
231 if (/Disk\s+([^ :]+):\s+(\d+)\s+\S+\s+(\d+)\s+\S+\s+(\d+)/ ||
232 ($m2 = ($_ =~ /Disk\s+([^ :]+):\s+(.*)\s+bytes/)) ||
233 ($m3 = ($_ =~ /Disk\s+([^ :]+):\s+([0-9\.]+)cyl/))) {
237 $disk = { 'device' => $1,
243 $disk = { 'device' => $1,
245 'table' => 'msdos', };
246 <FDISK> =~ /(\d+)\s+\S+\s+(\d+)\s+\S+\s+(\d+)/ || next;
247 $disk->{'heads'} = $1;
248 $disk->{'sectors'} = $2;
249 $disk->{'cylinders'} = $3;
253 $disk = { 'device' => $1,
258 'table' => 'msdos', };
260 $disk->{'index'} = scalar(@disks);
261 $disk->{'parts'} = [ ];
263 local @st = stat($disk->{'device'});
264 next if (@cdstat && $st[1] == $cdstat[1]);
265 if ($disk->{'device'} =~ /\/sd([a-z]+)$/) {
266 # Old-style SCSI disk
267 $disk->{'desc'} = &text('select_device', 'SCSI',
269 local ($dscsi) = grep { $_->{'dev'} eq "sd$1" } @dscsi;
270 $disk->{'scsi'} = $dscsi ? &indexof($dscsi, @dscsi)
272 $disk->{'type'} = 'scsi';
274 elsif ($disk->{'device'} =~ /\/hd([a-z]+)$/) {
276 $disk->{'desc'} = &text('select_device', 'IDE', uc($1));
277 $disk->{'type'} = 'ide';
279 elsif ($disk->{'device'} =~ /\/xvd([a-z]+)$/) {
281 $disk->{'desc'} = &text('select_device', 'Xen', uc($1));
282 $disk->{'type'} = 'ide';
284 elsif ($disk->{'device'} =~ /\/vd([a-z]+)$/) {
286 $disk->{'desc'} = &text('select_device',
288 $disk->{'type'} = 'ide';
290 elsif ($disk->{'device'} =~ /\/(scsi\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/disc)/) {
291 # New complete SCSI disk specification
292 $disk->{'host'} = $2;
294 $disk->{'target'} = $4;
296 $disk->{'desc'} = &text('select_scsi',
297 "$2", "$3", "$4", "$5");
299 # Work out the SCSI index for this disk
302 for($j=0; $j<@dscsi; $j++) {
303 if ($dscsi[$j]->{'host'} == $disk->{'host'} && $dscsi[$j]->{'bus'} == $disk->{'bus'} && $dscsi[$j]->{'target'} == $disk->{'target'} && $dscsi[$j]->{'lnun'} == $disk->{'lun'}) {
304 $disk->{'scsi'} = $j;
310 for($j=0; $j<@pscsi; $j++) {
311 if ($pscsi[$j] =~ /Host:\s+scsi(\d+).*Id:\s+(\d+)/i && $disk->{'host'} == $1 && $disk->{'target'} == $2) {
312 $disk->{'scsi'} = $j;
317 $disk->{'type'} = 'scsi';
318 $disk->{'prefix'} =~ s/disc$/part/g;
320 elsif ($disk->{'device'} =~ /\/(ide\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/disc)/) {
321 # New-style IDE specification
322 $disk->{'host'} = $2;
324 $disk->{'target'} = $4;
326 $disk->{'desc'} = &text('select_newide',
327 "$2", "$3", "$4", "$5");
328 $disk->{'type'} = 'ide';
329 $disk->{'prefix'} =~ s/disc$/part/g;
331 elsif ($disk->{'device'} =~ /\/(rd\/c(\d+)d(\d+))/) {
333 local ($mc, $md) = ($2, $3);
334 $disk->{'desc'} = &text('select_mylex', $mc, $md);
335 open(RD, "/proc/rd/c$mc/current_status");
337 if (/^Configuring\s+(.*)/i) {
338 $disk->{'model'} = $1;
340 elsif (/\s+(\S+):\s+([^, ]+)/ &&
341 $1 eq $disk->{'device'}) {
342 $disk->{'raid'} = $2;
346 $disk->{'type'} = 'raid';
347 $disk->{'prefix'} = $disk->{'device'}.'p';
349 elsif ($disk->{'device'} =~ /\/(ida\/c(\d+)d(\d+))/) {
351 local ($ic, $id) = ($2, $3);
352 $disk->{'desc'} = &text('select_cpq', $ic, $id);
353 open(IDA, -d "/proc/driver/array" ? "/proc/driver/array/ida$ic" : "/proc/driver/cpqarray/ida$ic");
355 if (/^(\S+):\s+(.*)/ && $1 eq "ida$ic") {
356 $disk->{'model'} = $2;
360 $disk->{'type'} = 'raid';
361 $disk->{'prefix'} = $disk->{'device'}.'p';
363 elsif ($disk->{'device'} =~ /\/(cciss\/c(\d+)d(\d+))/) {
364 # Compaq Smart Array RAID
365 local ($ic, $id) = ($2, $3);
366 $disk->{'desc'} = &text('select_smart', $ic, $id);
367 open(CCI, "/proc/driver/cciss/cciss$ic");
369 if (/^\s*(\S+):\s*(.*)/ && $1 eq "cciss$ic") {
370 $disk->{'model'} = $2;
374 $disk->{'type'} = 'raid';
375 $disk->{'prefix'} = $disk->{'device'}.'p';
377 elsif ($disk->{'device'} =~ /\/(ataraid\/disc(\d+)\/disc)/) {
378 # Promise RAID controller
380 $disk->{'desc'} = &text('select_promise', $dd);
381 $disk->{'type'} = 'raid';
382 $disk->{'prefix'} =~ s/disc$/part/g;
385 # Work out short name, like sda
387 if (defined($disk->{'host'})) {
388 $short = &hbt_to_device($disk->{'host'},
393 $short = $disk->{'device'};
394 $short =~ s/^.*\///g;
396 $disk->{'short'} = $short;
398 $disk->{'id'} = $id_map{$disk->{'device'}} ||
399 $id_map{"/dev/$short"};
403 elsif (/^Units\s+=\s+cylinders\s+of\s+(\d+)\s+\*\s+(\d+)/) {
404 # Unit size for disk from fdisk
405 $disk->{'bytes'} = $2;
406 $disk->{'cylsize'} = $disk->{'heads'} * $disk->{'sectors'} *
409 elsif (/BIOS\s+cylinder,head,sector\s+geometry:\s+(\d+),(\d+),(\d+)\.\s+Each\s+cylinder\s+is\s+(\d+)(b|kb|mb)/i) {
410 # Unit size for disk from parted
411 $disk->{'cylinders'} = $1;
412 $disk->{'heads'} = $2;
413 $disk->{'sectors'} = $3;
414 $disk->{'cylsize'} = $4 * (lc($5) eq "b" ? 1 :
415 lc($5) eq "kb" ? 1024 : 1024*1024);
416 $disk->{'bytes'} = $disk->{'cylsize'} / $disk->{'heads'} /
419 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+(.*)/) {
420 # Partition within the current disk from fdisk
421 local $part = { 'number' => $2,
427 'extended' => $6 eq '5' || $6 eq 'f' ? 1 : 0,
428 'index' => scalar(@{$disk->{'parts'}}),
430 $part->{'desc'} = &partition_description($part->{'device'});
431 push(@{$disk->{'parts'}}, $part);
433 elsif (/^\s*(\d+)\s+(\d+)cyl\s+(\d+)cyl\s+(\d+)cyl(\s+(primary|logical|extended))?\s*(\S*)\s*(\S*)/) {
434 # Partition within the current disk from parted
435 local $part = { 'number' => $1,
436 'device' => $disk->{'device'}.$1,
440 'blocks' => $4 * $disk->{'cylsize'},
441 'extended' => $6 eq 'extended' ? 1 : 0,
442 'index' => scalar(@{$disk->{'parts'}}),
445 $part->{'type'} = 'ext2' if ($part->{'type'} =~ /^ext/);
446 $part->{'desc'} = &partition_description($part->{'device'});
447 push(@{$disk->{'parts'}}, $part);
449 elsif (/Partition\s+Table:\s+(\S+)/) {
450 # Parted partition table type
451 $disk->{'table'} = $1;
456 # Check /proc/ide for IDE disk models
457 foreach $d (@disks) {
458 if ($d->{'type'} eq 'ide') {
459 local $short = $d->{'short'};
460 $d->{'model'} = &read_file_contents("/proc/ide/$short/model");
461 $d->{'model'} =~ s/\r|\n//g;
462 $d->{'media'} = &read_file_contents("/proc/ide/$short/media");
463 $d->{'media'} =~ s/\r|\n//g;
467 # Fill in SCSI information
468 foreach $d (@disks) {
469 if ($d->{'type'} eq 'scsi') {
470 local $s = $d->{'scsi'};
471 local $sysdir = "/sys/block/$d->{'short'}/device";
473 # From kernel 2.6.30+ sys directory
474 $d->{'model'} = &read_file_contents("$sysdir/vendor").
476 &read_file_contents("$sysdir/model");
477 $d->{'model'} =~ s/\r|\n//g;
478 $d->{'media'} = &read_file_contents("$sysdir/media");
479 $d->{'media'} =~ s/\r|\n//g;
481 elsif ($dscsi_mode) {
482 # From other scsi files
483 $d->{'model'} = "$dscsi[$s]->{'make'} $dscsi[$s]->{'model'}";
484 $d->{'controller'} = $dscsi[$s]->{'host'};
485 $d->{'scsiid'} = $dscsi[$s]->{'target'};
488 # From /proc/scsi/scsi lines
489 if ($pscsi[$s] =~ /Vendor:\s+(\S+).*Model:\s+(.*)\s+Rev:/i) {
490 $d->{'model'} = "$1 $2";
492 if ($pscsi[$s] =~ /Host:\s+scsi(\d+).*Id:\s+(\d+)/i) {
493 $d->{'controller'} = int($1);
494 $d->{'scsiid'} = int($2);
497 if ($d->{'model'} =~ /ATA/) {
498 # Fake SCSI disk, actually IDE
500 $d->{'desc'} =~ s/SCSI/SATA/g;
501 foreach my $p (@{$d->{'parts'}}) {
502 $p->{'desc'} =~ s/SCSI/SATA/g;
508 @list_disks_partitions_cache = @disks;
512 # partition_description(device)
513 # Converts a device path like /dev/hda into a human-readable name
514 sub partition_description
517 return $device =~ /(.)d([a-z]+)(\d+)$/ ?
518 &text('select_part', $1 eq 's' ? 'SCSI' : 'IDE', uc($2), "$3") :
519 $device =~ /scsi\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/part(\d+)/ ?
520 &text('select_spart', "$1", "$2", "$3", "$4", "$5") :
521 $device =~ /ide\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/part(\d+)/ ?
522 &text('select_snewide', "$1", "$2", "$3", "$4", "$5") :
523 $device =~ /rd\/c(\d+)d(\d+)p(\d+)$/ ?
524 &text('select_mpart', "$1", "$2", "$3") :
525 $device =~ /ida\/c(\d+)d(\d+)p(\d+)$/ ?
526 &text('select_cpart', "$1", "$2", "$3") :
527 $device =~ /cciss\/c(\d+)d(\d+)p(\d+)$/ ?
528 &text('select_smartpart', "$1", "$2", "$3") :
529 $device =~ /ataraid\/disc(\d+)\/part(\d+)$/ ?
530 &text('select_ppart', "$1", "$2") :
534 # hbt_to_device(host, bus, target)
535 # Converts an IDE device specified as a host, bus and target to an hdX device
538 local ($host, $bus, $target) = @_;
539 local $num = $host*4 + $bus*2 + $target;
540 return &number_to_device("hd", $num);
543 # number_to_device(suffix, number)
546 local ($suffix, $num) = @_;
548 # Just a single letter
549 return $suffix.(('a' .. 'z')[$num]);
553 local $first = int($num / 26);
554 local $second = $num % 26;
555 return $suffix.(('a' .. 'z')[$first]).(('a' .. 'z')[$second]);
559 # change_type(disk, partition, type)
560 # Changes the type of an existing partition
563 &open_fdisk("$_[0]");
565 local $rv = &wait_for($fh, 'Partition.*:', 'Selected partition');
566 &wprint("$_[1]\n") if ($rv == 0);
567 &wait_for($fh, 'Hex.*:');
569 &wait_for($fh, 'Command.*:');
570 &wprint("w\n"); sleep(1);
572 undef(@list_disks_partitions_cache);
575 # delete_partition(disk, partition)
576 # Delete an existing partition
579 my ($disk, $part) = @_;
582 my $cmd = "parted -s ".$disk." rm ".$part;
583 my $out = &backquote_logged("$cmd </dev/null 2>&1");
585 &error("$cmd failed : $out");
592 local $rv = &wait_for($fh, 'Partition.*:', 'Selected partition');
593 &wprint("$part\n") if ($rv == 0);
594 &wait_for($fh, 'Command.*:');
596 &wait_for($fh, 'Syncing');
600 undef(@list_disks_partitions_cache);
603 # create_partition(disk, partition, start, end, type)
604 # Create a new partition with the given extent and type
607 my ($disk, $part, $start, $end, $type) = @_;
610 my $pe = $part > 4 ? "logical" : "primary";
613 $cmd = "parted -s ".$disk." unit cyl mkpartfs ".$pe." ".
614 $type." ".($start-1)." ".$end;
617 $cmd = "parted -s ".$disk." unit cyl mkpart ".$pe." ".
620 my $out = &backquote_logged("$cmd </dev/null 2>&1");
622 &error("$cmd failed : $out");
629 local $wf = &wait_for($fh, 'primary.*\r?\n', 'First.*:');
635 local $wf2 = &wait_for($fh, 'Partition.*:',
636 'Selected partition');
637 &wprint("$part\n") if ($wf2 == 0);
639 &wait_for($fh, 'First.*:') if ($wf != 1);
641 &wait_for($fh, 'Last.*:');
643 &wait_for($fh, 'Command.*:');
646 local $rv = &wait_for($fh, 'Partition.*:', 'Selected partition');
647 &wprint("$part\n") if ($rv == 0);
648 &wait_for($fh, 'Hex.*:');
650 &wait_for($fh, 'Command.*:');
652 &wait_for($fh, 'Syncing'); sleep(3);
655 undef(@list_disks_partitions_cache);
658 # create_extended(disk, partition, start, end)
659 # Create a new extended partition
662 my ($disk, $part, $start, $end) = @_;
664 # Create using parted
665 my $cmd = "parted -s ".$disk." unit cyl mkpart extended ".
667 my $out = &backquote_logged("$cmd </dev/null 2>&1");
669 &error("$cmd failed : $out");
676 &wait_for($fh, 'primary.*\r?\n');
678 &wait_for($fh, 'Partition.*:');
680 &wait_for($fh, 'First.*:');
682 &wait_for($fh, 'Last.*:');
684 &wait_for($fh, 'Command.*:');
687 &wait_for($fh, 'Syncing');
691 undef(@list_disks_partitions_cache);
695 # Returns a list of known partition tag numbers
700 return sort { $a cmp $b } (keys %parted_tags);
703 # Classic fdisk types
704 return sort { hex($a) <=> hex($b) } (keys %tags);
709 # Returns a human-readable version of a tag
712 return $tags{$_[0]} || $parted_tags{$_[0]} || $hidden_tags{$_[0]};
717 return $has_parted ? 'ext2' : '83';
721 # Given a partition tag, returns the filesystem type (assuming it is supported)
727 # Use parted type names
728 if ($tag eq "fat16") {
731 elsif ($tag eq "fat32") {
734 elsif ($tag eq "ext2") {
735 @rv = ( "ext3", "ext4", "ext2", "xfs", "reiserfs" );
737 elsif ($tag eq "hfs") {
740 elsif ($tag eq "linux-swap") {
743 elsif ($tag eq "NTFS") {
746 elsif ($tag eq "reiserfs") {
749 elsif ($tag eq "ufs") {
758 if ($tag eq "4" || $tag eq "6" || $tag eq "1" || $tag eq "e") {
761 elsif ($tag eq "b" || $tag eq "c") {
764 elsif ($tag eq "83") {
765 @rv = ( "ext3", "ext4", "ext2", "xfs", "reiserfs" );
767 elsif ($tag eq "82") {
770 elsif ($tag eq "81") {
777 local %supp = map { $_, 1 } &mount::list_fstypes();
778 @rv = grep { $supp{$_} } @rv;
779 return wantarray ? @rv : $rv[0];
783 # Returns a readable name for a filesystem type
786 return $text{"fs_".$_[0]};
791 if ($_[0] eq "ext2") {
792 &opt_input("ext2_b", $text{'bytes'}, 1);
793 &opt_input("ext2_f", $text{'bytes'}, 0);
794 &opt_input("ext2_i", "", 1);
795 &opt_input("ext2_m", "%", 0);
796 &opt_input("ext2_g", "", 1);
797 print &ui_table_row($text{'ext2_c'},
798 &ui_yesno_radio("ext2_c", 0));
800 elsif ($_[0] eq "msdos" || $_[0] eq "vfat") {
801 &opt_input("msdos_ff", "", 1);
802 print &ui_table_row($text{'msdos_F'},
803 &ui_select("msdos_F", undef,
804 [ [ undef, $text{'default'} ],
805 [ 12 ], [ 16 ], [ 32 ],
806 [ "*", $text{'msdos_F_other'} ] ])." ".
807 &ui_textbox("msdos_F_other", undef, 4));
808 &opt_input("msdos_i", "", 1);
809 &opt_input("msdos_n", "", 0);
810 &opt_input("msdos_r", "", 1);
811 &opt_input("msdos_s", "sectors", 0);
812 print &ui_table_row($text{'msdos_c'},
813 &ui_yesno_radio("msdos_c", 0));
815 elsif ($_[0] eq "minix") {
816 &opt_input("minix_n", "", 1);
817 &opt_input("minix_i", "", 0);
818 &opt_input("minix_b", "", 1);
819 print &ui_table_row($text{'minix_c'},
820 &ui_yesno_radio("minix_c", 0));
822 elsif ($_[0] eq "reiserfs") {
823 print &ui_table_row($text{'reiserfs_force'},
824 &ui_yesno_radio("reiserfs_f", 0));
826 print &ui_table_row($text{'reiserfs_hash'},
827 &ui_select("reiserfs_h", "",
828 [ [ "", $text{'default'} ],
829 [ "rupasov", "tea" ] ]));
831 elsif ($_[0] =~ /^ext\d+$/) {
832 &opt_input("ext2_b", $text{'bytes'}, 1);
833 &opt_input("ext2_f", $text{'bytes'}, 0);
834 &opt_input("ext2_i", "", 1);
835 &opt_input("ext2_m", "%", 0);
836 &opt_input("ext3_j", "MB", 1);
837 print &ui_table_row($text{'ext2_c'},
838 &ui_yesno_radio("ext2_c", 0));
840 elsif ($_[0] eq "xfs") {
841 print &ui_table_row($text{'xfs_force'},
842 &ui_yesno_radio("xfs_f", 0));
843 &opt_input("xfs_b", $text{'bytes'}, 0);
845 elsif ($_[0] eq "jfs") {
846 &opt_input("jfs_s", $text{'megabytes'}, 1);
847 print &ui_table_row($text{'jfs_c'},
848 &ui_yesno_radio("jfs_c", 0));
850 elsif ($_[0] eq "fatx") {
852 print &ui_table_row(undef, $text{'fatx_none'}, 4);
856 # mkfs_parse(type, device)
857 # Returns a command to build a new filesystem of the given type on the
858 # given device. Options are taken from %in.
862 if ($_[0] eq "ext2") {
863 $cmd = "mkfs -t ext2";
864 $cmd .= &opt_check("ext2_b", '\d+', "-b");
865 $cmd .= &opt_check("ext2_f", '\d+', "-f");
866 $cmd .= &opt_check("ext2_i", '\d{4,}', "-i");
867 $cmd .= &opt_check("ext2_m", '\d+', "-m");
868 $cmd .= &opt_check("ext2_g", '\d+', "-g");
869 $cmd .= $in{'ext2_c'} ? " -c" : "";
873 elsif ($_[0] eq "msdos" || $_[0] eq "vfat") {
874 $cmd = "mkfs -t $_[0]";
875 $cmd .= &opt_check("msdos_ff", '[1-2]', "-f");
876 if ($in{'msdos_F'} eq '*') {
877 $in{'msdos_F_other'} =~ /^\d+$/ ||
878 &error(&text('opt_error', $in{'msdos_F_other'},
880 $cmd .= " -F ".$in{'msdos_F_other'};
882 elsif ($in{'msdos_F'}) {
883 $cmd .= " -F ".$in{'msdos_F'};
885 $cmd .= &opt_check("msdos_i", '[0-9a-f]{8}', "-i");
886 $cmd .= &opt_check("msdos_n", '\S{1,11}', "-n");
887 $cmd .= &opt_check("msdos_r", '\d+', "-r");
888 $cmd .= &opt_check("msdos_s", '\d+', "-s");
889 $cmd .= $in{'msdos_c'} ? " -c" : "";
892 elsif ($_[0] eq "minix") {
893 local(@plist, $disk, $part, $i, @pinfo);
894 $cmd = "mkfs -t minix";
895 $cmd .= &opt_check("minix_n", '14|30', "-n ");
896 $cmd .= &opt_check("minix_i", '\d+', "-i ");
897 $cmd .= $in{'minix_c'} ? " -c" : "";
898 $cmd .= &opt_check("minix_b", '\d+', " ");
901 elsif ($_[0] eq "reiserfs") {
902 $cmd = "yes | mkreiserfs";
903 $cmd .= " -f" if ($in{'reiserfs_f'});
904 $cmd .= " -h $in{'reiserfs_h'}" if ($in{'reiserfs_h'});
907 elsif ($_[0] =~ /^ext\d+$/) {
908 if (&has_command("mkfs.$_[0]")) {
909 $cmd = "mkfs -t $_[0]";
910 $cmd .= &opt_check("ext3_j", '\d+', "-j");
912 elsif ($_[0] eq "ext3" && &has_command("mke3fs")) {
914 $cmd .= &opt_check("ext3_j", '\d+', "-j");
916 elsif ($_[0] eq "ext4" && &has_command("mke4fs")) {
918 $cmd .= &opt_check("ext3_j", '\d+', "-j");
921 $cmd = "mkfs.ext2 -j";
922 if (!$in{'ext3_j_def'}) {
923 $in{'ext3_j'} =~ /^\d+$/ ||
924 &error(&text('opt_error', $in{'ext3_j'},
926 $cmd .= " -J size=$in{'ext3_j'}";
929 $cmd .= &opt_check("ext2_b", '\d+', "-b");
930 $cmd .= &opt_check("ext2_f", '\d+', "-f");
931 $cmd .= &opt_check("ext2_i", '\d{4,}', "-i");
932 $cmd .= &opt_check("ext2_m", '\d+', "-m");
933 $cmd .= $in{'ext2_c'} ? " -c" : "";
937 elsif ($_[0] eq "xfs") {
938 $cmd = "mkfs -t $_[0]";
939 $cmd .= " -f" if ($in{'xfs_f'});
940 $cmd .= " -b size=$in{'xfs_b'}" if (!$in{'xfs_b_def'});
943 elsif ($_[0] eq "jfs") {
944 $cmd = "mkfs -t $_[0] -q";
945 $cmd .= &opt_check("jfs_s", '\d+', "-s");
946 $cmd .= " -c" if ($in{'jfs_c'});
949 elsif ($_[0] eq "fatx") {
950 $cmd = "mkfs -t $_[0] $_[1]";
952 if (&has_command("partprobe")) {
953 $cmd .= "partprobe ; $cmd";
959 # Returns 1 if this filesystem type can be tuned
962 return $_[0] =~ /^ext\d+$/;
965 # tunefs_options(type)
966 # Output HTML for tuning options for some filesystem type
969 if ($_[0] =~ /^ext\d+$/) {
970 # Gaps between checks
971 &opt_input("tunefs_c", "", 1);
974 print &ui_table_row($text{'tunefs_e'},
975 &ui_radio("tunefs_e_def", 1,
976 [ [ 1, $text{'opt_default'} ],
977 [ 0, &ui_select("tunefs_e", undef,
978 [ [ "continue", $text{'tunefs_continue'} ],
979 [ "remount-ro", $text{'tunefs_remount'} ],
980 [ "panic", $text{'tunefs_panic'} ] ]) ] ]));
983 print &ui_table_row($text{'tunefs_u'},
984 &ui_opt_textbox("tunefs_u", undef, 13, $text{'opt_default'})." ".
985 &user_chooser_button("tunefs_u", 0));
988 print &ui_table_row($text{'tunefs_g'},
989 &ui_opt_textbox("tunefs_g", undef, 13, $text{'opt_default'})." ".
990 &group_chooser_button("tunefs_g", 0));
993 &opt_input("tunefs_m", "%", 1);
995 # Time between checks
996 $tsel = &ui_select("tunefs_i_unit", undef,
997 [ [ "d", $text{'tunefs_days'} ],
998 [ "w", $text{'tunefs_weeks'} ],
999 [ "m", $text{'tunefs_months'} ] ]);
1000 &opt_input("tunefs_i", $tsel, 0);
1004 # tunefs_parse(type, device)
1005 # Returns the tuning command based on user inputs
1008 if ($_[0] =~ /^ext\d+$/) {
1010 $cmd .= &opt_check("tunefs_c", '\d+', "-c");
1011 $cmd .= $in{'tunefs_e_def'} ? "" : " -e$in{'tunefs_e'}";
1012 $cmd .= $in{'tunefs_u_def'} ? "" : " -u".getpwnam($in{'tunefs_u'});
1013 $cmd .= $in{'tunefs_g_def'} ? "" : " -g".getgrnam($in{'tunefs_g'});
1014 $cmd .= &opt_check("tunefs_m",'\d+',"-m");
1015 $cmd .= &opt_check("tunefs_i", '\d+', "-i").
1016 ($in{'tunefs_i_def'} ? "" : $in{'tunefs_i_unit'});
1023 # Returns 1 if a reboot is needed after changing the partitions on some disk
1026 local $un = `uname -r`;
1027 return $un =~ /^2\.0\./ || $un =~ /^1\./ || $un =~ /^0\./;
1030 # device_status(device)
1031 # Returns an array of directory, type, mounted
1034 @mounted = &foreign_call("mount", "list_mounted") if (!@mounted);
1035 @mounts = &foreign_call("mount", "list_mounts") if (!@mounts);
1036 local $label = &get_label($_[0]);
1037 local $volid = &get_volid($_[0]);
1039 local ($mounted) = grep { &same_file($_->[1], $_[0]) ||
1040 $_->[1] eq "LABEL=$label" ||
1041 $_->[1] eq "UUID=$volid" } @mounted;
1042 local ($mount) = grep { &same_file($_->[1], $_[0]) ||
1043 $_->[1] eq "LABEL=$label" ||
1044 $_->[1] eq "UUID=$volid" } @mounts;
1045 if ($mounted) { return ($mounted->[0], $mounted->[2], 1,
1046 &indexof($mount, @mounts),
1047 &indexof($mounted, @mounted)); }
1048 elsif ($mount) { return ($mount->[0], $mount->[2], 0,
1049 &indexof($mount, @mounts)); }
1051 $raidconf = &foreign_call("raid", "get_raidtab") if (!$raidconf);
1052 foreach $c (@$raidconf) {
1053 foreach $d (&raid::find_value('device', $c->{'members'})) {
1054 return ( $c->{'value'}, "raid", 1 ) if ($d eq $_[0]);
1059 if (!scalar(@physical_volumes)) {
1060 @physical_volumes = ();
1061 foreach $vg (&foreign_call("lvm", "list_volume_groups")) {
1062 push(@physical_volumes,
1063 &foreign_call("lvm", "list_physical_volumes",
1067 foreach $pv (@physical_volumes) {
1068 return ( $pv->{'vg'}, "lvm", 1)
1069 if ($pv->{'device'} eq $_[0]);
1076 # Returns 1 if some filesystem type can fsck'd
1079 return ($_[0] =~ /^ext\d+$/ && &has_command("fsck.$_[0]") ||
1080 $_[0] eq "minix" && &has_command("fsck.minix"));
1083 # fsck_command(type, device)
1084 # Returns the fsck command to unconditionally check a filesystem
1087 if ($_[0] =~ /^ext\d+$/) {
1088 return "fsck -t $_[0] -p $_[1]";
1090 elsif ($_[0] eq "minix") {
1091 return "fsck -t minix -a $_[1]";
1096 # Returns a description of an exit code from fsck
1099 return $text{"fsck_err$_[0]"} ? $text{"fsck_err$_[0]"}
1100 : &text("fsck_unknown", $_[0]);
1103 # partition_select(name, value, mode, [&found], [disk_regexp])
1104 # Returns HTML for selecting a disk or partition
1105 # mode 0 = floppies and disk partitions
1107 # 2 = floppies and disks and disk partitions
1108 # 3 = disk partitions
1109 sub partition_select
1111 local $rv = "<select name=$_[0]>\n";
1112 local ($found, $d, $p);
1113 if (($_[2] == 0 || $_[2] == 2) &&
1114 (-r "/dev/fd0" || $_[1] =~ /^\/dev\/fd[01]$/)) {
1115 $rv .= sprintf "<option %s value=/dev/fd0>%s\n",
1116 $_[1] eq "/dev/fd0" ? "selected" : "",
1117 &text('select_fd', 0) if (!$_[4] || "/dev/fd0" =~ /$_[4]/);
1118 $rv .= sprintf "<option %s value=/dev/fd1>%s\n",
1119 $_[1] eq "/dev/fd1" ? "selected" : "",
1120 &text('select_fd', 1) if (!$_[4] || "/dev/fd1" =~ /$_[4]/);
1121 $found++ if ($_[1] =~ /^\/dev\/fd[01]$/);
1123 local @dlist = &list_disks_partitions();
1124 foreach $d (@dlist) {
1125 local $dev = $d->{'device'};
1126 next if ($_[4] && $dev !~ /$_[4]/);
1127 if ($_[2] == 1 || $_[2] == 2) {
1128 local $name = $d->{'desc'};
1129 $name .= " ($d->{'model'})" if ($d->{'model'});
1130 $rv .= sprintf "<option value=%s %s>%s\n",
1131 $dev, $_[1] eq $dev ? "selected" : "", $name;
1132 $found++ if ($dev eq $_[1]);
1134 if ($_[2] == 0 || $_[2] == 2 || $_[2] == 3) {
1135 foreach $p (@{$d->{'parts'}}) {
1136 next if ($p->{'extended'});
1137 local $name = $p->{'desc'};
1138 $name .= " (".&tag_name($p->{'type'}).")"
1139 if (&tag_name($p->{'type'}));
1140 $rv .= sprintf "<option %s value=%s>%s\n",
1141 $_[1] eq $p->{'device'} ? "selected" : "",
1142 $p->{'device'}, $name;
1143 $found++ if ($_[1] eq $p->{'device'});
1147 if (!$found && $_[1] && !$_[3]) {
1148 $rv .= "<option selected>$_[1]\n";
1153 $rv .= "</select>\n";
1157 # label_select(name, value, &found)
1158 # Returns HTML for selecting a filesystem label
1161 local $rv = "<select name=$_[0]>\n";
1162 local @dlist = &list_disks_partitions();
1164 foreach $d (@dlist) {
1165 local $dev = $d->{'device'};
1166 foreach $p (@{$d->{'parts'}}) {
1167 next if ($p->{'type'} ne '83');
1168 local $label = &get_label($p->{'device'});
1170 $rv .= sprintf "<option %s value=%s>%s (%s)\n",
1171 $_[1] eq $label ? "selected" : "",
1172 $label, $label, $p->{'desc'};
1173 ${$_[2]}++ if ($_[1] eq $label);
1177 if (!$found && $_[1] && !$_[2]) {
1178 $rv .= "<option selected>$_[1]\n";
1180 $rv .= "</select>\n";
1181 return $any ? $rv : undef;
1184 # volid_select(name, value, &found)
1185 # Returns HTML for selecting a filesystem UUID
1188 local ($name, $value, $found) = @_;
1189 local @dlist = &list_disks_partitions();
1191 foreach my $d (@dlist) {
1192 local $dev = $d->{'device'};
1193 foreach $p (@{$d->{'parts'}}) {
1194 next if ($p->{'type'} ne '83' && $p->{'type'} ne '82' &&
1195 $p->{'type'} ne 'b' && $p->{'type'} ne 'c');
1196 local $volid = &get_volid($p->{'device'});
1198 push(@opts, [ $volid, "$volid ($p->{'desc'})" ]);
1199 $$found++ if ($value eq $volid);
1203 return &ui_select($name, $value, \@opts, 1, 0, $value ? 1 : 0);
1210 #############################################################################
1211 # Internal functions
1212 #############################################################################
1215 local $fpath = &check_fdisk();
1216 ($fh, $fpid) = &foreign_call("proc", "pty_process_exec", join(" ",$fpath, @_));
1221 local $sfpath = &has_command("sfdisk");
1222 ($fh, $fpid) = &foreign_call("proc", "pty_process_exec", join(" ",$sfpath, @_));
1227 local $fpath = &has_command("fdisk");
1228 &error(&text('open_error', "<tt>fdisk</tt>")) if (!$fpath);
1234 close($fh); kill('TERM', $fpid);
1239 syswrite($fh, $_[0], length($_[0]));
1244 print &ui_table_row($text{$_[0]},
1245 &ui_opt_textbox($_[0], undef, 6, $text{'opt_default'})." ".$_[1]);
1250 if ($in{"$_[0]_def"}) { return ""; }
1251 elsif ($in{$_[0]} !~ /^$_[1]$/) {
1252 &error(&text('opt_error', $in{$_[0]}, $text{$_[0]}));
1254 else { return " $_[2] $in{$_[0]}"; }
1257 %tags = ('0', 'Empty',
1265 '9', 'AIX bootable',
1266 'a', 'OS/2 boot manager',
1267 'b', 'Windows FAT32',
1268 'c', 'Windows FAT32 LBA',
1269 'e', 'Windows FAT16 LBA',
1271 '11', 'Hidden FAT12',
1272 '12', 'Compaq diagnostic',
1273 '14', 'Hidden FAT16 < 32M',
1274 '16', 'Hidden FAT16',
1275 '17', 'Hidden NTFS',
1276 '18', 'AST Windows swapfile',
1277 '1b', 'Hidden Windows FAT (1b)',
1278 '1c', 'Hidden Windows FAT (1c)',
1279 '1e', 'Hidden Windows FAT (1e)',
1281 '3c', 'PartitionMagic recovery',
1282 '40', 'Venix 80286',
1283 '41', 'PPC PReP boot',
1286 '4e', 'QNX 4.x 2nd partition',
1287 '4f', 'QNX 4.x 3rd partition',
1289 '51', 'OnTrack DM6 Aux1',
1291 '53', 'OnTrack DM6 Aux3',
1292 '54', 'OnTrack DM6',
1295 '5c', 'Priam Edisk',
1297 '63', 'GNU HURD or SysV',
1298 '64', 'Novell Netware 286',
1299 '65', 'Novell Netware 386',
1300 '70', 'DiskSecure Multi-Boot',
1303 '81', 'Minix / Old Linux / Solaris',
1306 '84', 'OS/2 hidden C: drive',
1307 '85', 'Linux extended',
1308 '86', 'NTFS volume set (86)',
1309 '87', 'NTFS volume set (87)',
1313 'a0', 'IBM Thinkpad hibernation',
1317 'b7', 'BSDI filesystem',
1319 'c1', 'DRDOS/sec FAT12',
1320 'c4', 'DRDOS/sec FAT16 <32M',
1321 'c6', 'DRDOS/sec FAT16',
1323 'db', 'CP/M / CTOS',
1325 'e3', 'DOS read-only',
1330 'f4', 'SpeedStor large partition',
1331 'f2', 'DOS secondary',
1339 'f', 'Windows extended LBA',
1344 'fat16', 'Windows FAT16',
1345 'fat32', 'Windows FAT32',
1348 'linux-swap', 'Linux Swap',
1349 'NTFS', 'Windows NTFS',
1350 'reiserfs', 'ReiserFS',
1351 'ufs', 'FreeBSD UFS',
1354 @space_type = ( '1', '4', '5', '6', 'b', 'c', 'e', '83' );
1356 # can_edit_disk(device)
1360 $device =~ s/\d+$//;
1361 foreach (split(/\s+/, $access{'disks'})) {
1362 return 1 if ($_ eq "*" || $_ eq $device);
1367 # disk_space(device, [mountpoint])
1368 # Returns the amount of total and free space for some filesystem, or an
1369 # empty array if not appropriate.
1372 local $w = $_[1] || $_[0];
1373 local $out = `df -k '$w'`;
1374 if ($out =~ /Mounted on\s*\n\s*\S+\s+(\S+)\s+\S+\s+(\S+)/i) {
1377 elsif ($out =~ /Mounted on\s*\n\S+\s*\n\s+(\S+)\s+\S+\s+(\S+)/i) {
1385 # supported_filesystems()
1386 # Returns a list of filesystem types that can have mkfs_options called on them
1387 sub supported_filesystems
1389 local @fstypes = ( "ext2" );
1390 push(@fstypes, "ext3") if (&has_command("mkfs.ext3") ||
1391 &has_command("mke3fs") ||
1392 `mkfs.ext2 -h 2>&1` =~ /\[-j\]/);
1393 push(@fstypes, "ext4") if (&has_command("mkfs.ext4") ||
1394 &has_command("mke4fs"));
1395 push(@fstypes, "reiserfs") if (&has_command("mkreiserfs"));
1396 push(@fstypes, "xfs") if (&has_command("mkfs.xfs"));
1397 push(@fstypes, "jfs") if (&has_command("mkfs.jfs"));
1398 push(@fstypes, "fatx") if (&has_command("mkfs.fatx"));
1399 push(@fstypes, "msdos");
1400 push(@fstypes, "vfat");
1401 push(@fstypes, "minix");
1405 # get_label(device, [type])
1406 # Returns the XFS or EXT label for some device's filesystem
1411 $label = `e2label $_[0] 2>&1`;
1414 if (($? || $label !~ /\S/) && $has_xfs_db) {
1416 local $out = &backquote_with_timeout("xfs_db -x -p xfs_admin -c label -r $_[0] 2>&1", 5);
1417 $label = $1 if ($out =~ /label\s*=\s*"(.*)"/ &&
1420 if (($? || $label !~ /\S/) && $has_reiserfstune) {
1422 local $out = &backquote_command("reiserfstune $_[0]");
1423 if ($out =~ /LABEL:\s*(\S+)/) {
1427 return $? || $label !~ /\S/ ? undef : $label;
1431 # Returns the UUID for some device's filesystem
1434 local ($device) = @_;
1436 if (-d $uuid_directory) {
1437 # Use UUID mapping directory
1438 opendir(DIR, $uuid_directory);
1439 foreach my $f (readdir(DIR)) {
1440 local $linkdest = &simplify_path(
1441 &resolve_links("$uuid_directory/$f"));
1442 if ($linkdest eq $device) {
1449 elsif ($has_volid) {
1450 # Use vol_id command
1451 local $out = &backquote_command(
1452 "vol_id ".quotemeta($device)." 2>&1", 1);
1453 if ($out =~ /ID_FS_UUID=(\S+)/) {
1460 # set_label(device, label, [type])
1461 # Tries to set the label for some device's filesystem
1464 if ($has_e2label && ($_[2] =~ /^ext[23]$/ || !$_[2])) {
1465 &system_logged("e2label '$_[0]' '$_[1]' >/dev/null 2>&1");
1468 if ($has_xfs_db && ($_[2] eq "xfs" || !$_[2])) {
1469 &system_logged("xfs_db -x -p xfs_admin -c \"label $_[1]\" $_[0] >/dev/null 2>&1");
1475 # set_name(&disk, &partition, name)
1476 # Sets the name of a partition, for partition types that support it
1479 my ($dinfo, $pinfo, $name) = @_;
1480 my $cmd = "parted -s ".$dinfo->{'device'}." name ".$pinfo->{'number'}." ";
1482 $cmd .= quotemeta($name);
1487 my $out = &backquote_logged("$cmd </dev/null 2>&1");
1489 &error("$cmd failed : $out");
1493 # set_partition_table(device, table-type)
1494 # Wipe and re-create the partition table on some disk
1495 sub set_partition_table
1497 my ($disk, $table) = @_;
1498 my $cmd = "parted -s ".$disk." mktable ".$table;
1499 my $out = &backquote_logged("$cmd </dev/null 2>&1");
1501 &error("$cmd failed : $out");
1505 # supports_label(&partition)
1506 # Returns 1 if the label can be set on a partition
1510 return $part->{'type'} eq '83' || $part->{'type'} eq 'ext2';
1513 # supports_name(&disk)
1514 # Returns 1 if the name can be set on a disk's partitions
1518 return $disk->{'table'} eq 'gpt';
1521 # supports_hdparm(&disk)
1525 return $d->{'type'} eq 'ide' || $d->{'type'} eq 'scsi' && $d->{'model'} =~ /ATA/;
1528 # supports_relabel(&disk)
1529 # Return 1 if a disk can have it's partition table re-written
1530 sub supports_relabel
1532 return $has_parted ? 1 : 0;
1535 # supports_smart(&disk)
1538 return &foreign_installed("smart-status") &&
1539 &foreign_available("smart-status");
1542 # supports_extended(&disk)
1543 # Return 1 if some disk can support extended partitions
1544 sub supports_extended
1547 return $disk->{'label'} eq 'msdos' ? 1 : 0;
1550 # list_table_types(&disk)
1551 # Returns the list of supported partition table types for a disk
1552 sub list_table_types
1555 return ( 'msdos', 'gpt', 'bsd', 'dvh', 'loop', 'mac', 'pc98', 'sun' );
1562 # get_parted_version()
1563 # Returns the version number of parted that is installed
1564 sub get_parted_version
1566 my $out = &backquote_command("parted -v 2>&1 </dev/null");
1567 return $out =~ /parted.*\s([0-9\.]+)/i ? $1 : undef;