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;
464 if ($d->{'short'} =~ /^vd/ && !$d->{'model'}) {
465 # Fake up model for KVM VirtIO disks
466 $d->{'model'} = "KVM VirtIO";
471 # Fill in SCSI information
472 foreach $d (@disks) {
473 if ($d->{'type'} eq 'scsi') {
474 local $s = $d->{'scsi'};
475 local $sysdir = "/sys/block/$d->{'short'}/device";
477 # From kernel 2.6.30+ sys directory
478 $d->{'model'} = &read_file_contents("$sysdir/vendor").
480 &read_file_contents("$sysdir/model");
481 $d->{'model'} =~ s/\r|\n//g;
482 $d->{'media'} = &read_file_contents("$sysdir/media");
483 $d->{'media'} =~ s/\r|\n//g;
485 elsif ($dscsi_mode) {
486 # From other scsi files
487 $d->{'model'} = "$dscsi[$s]->{'make'} $dscsi[$s]->{'model'}";
488 $d->{'controller'} = $dscsi[$s]->{'host'};
489 $d->{'scsiid'} = $dscsi[$s]->{'target'};
492 # From /proc/scsi/scsi lines
493 if ($pscsi[$s] =~ /Vendor:\s+(\S+).*Model:\s+(.*)\s+Rev:/i) {
494 $d->{'model'} = "$1 $2";
496 if ($pscsi[$s] =~ /Host:\s+scsi(\d+).*Id:\s+(\d+)/i) {
497 $d->{'controller'} = int($1);
498 $d->{'scsiid'} = int($2);
501 if ($d->{'model'} =~ /ATA/) {
502 # Fake SCSI disk, actually IDE
504 $d->{'desc'} =~ s/SCSI/SATA/g;
505 foreach my $p (@{$d->{'parts'}}) {
506 $p->{'desc'} =~ s/SCSI/SATA/g;
512 @list_disks_partitions_cache = @disks;
516 # partition_description(device)
517 # Converts a device path like /dev/hda into a human-readable name
518 sub partition_description
521 return $device =~ /(.)d([a-z]+)(\d+)$/ ?
522 &text('select_part', $1 eq 's' ? 'SCSI' : 'IDE', uc($2), "$3") :
523 $device =~ /scsi\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/part(\d+)/ ?
524 &text('select_spart', "$1", "$2", "$3", "$4", "$5") :
525 $device =~ /ide\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/part(\d+)/ ?
526 &text('select_snewide', "$1", "$2", "$3", "$4", "$5") :
527 $device =~ /rd\/c(\d+)d(\d+)p(\d+)$/ ?
528 &text('select_mpart', "$1", "$2", "$3") :
529 $device =~ /ida\/c(\d+)d(\d+)p(\d+)$/ ?
530 &text('select_cpart', "$1", "$2", "$3") :
531 $device =~ /cciss\/c(\d+)d(\d+)p(\d+)$/ ?
532 &text('select_smartpart', "$1", "$2", "$3") :
533 $device =~ /ataraid\/disc(\d+)\/part(\d+)$/ ?
534 &text('select_ppart', "$1", "$2") :
538 # hbt_to_device(host, bus, target)
539 # Converts an IDE device specified as a host, bus and target to an hdX device
542 local ($host, $bus, $target) = @_;
543 local $num = $host*4 + $bus*2 + $target;
544 return &number_to_device("hd", $num);
547 # number_to_device(suffix, number)
550 local ($suffix, $num) = @_;
552 # Just a single letter
553 return $suffix.(('a' .. 'z')[$num]);
557 local $first = int($num / 26);
558 local $second = $num % 26;
559 return $suffix.(('a' .. 'z')[$first]).(('a' .. 'z')[$second]);
563 # change_type(disk, partition, type)
564 # Changes the type of an existing partition
567 &open_fdisk("$_[0]");
569 local $rv = &wait_for($fh, 'Partition.*:', 'Selected partition');
570 &wprint("$_[1]\n") if ($rv == 0);
571 &wait_for($fh, 'Hex.*:');
573 &wait_for($fh, 'Command.*:');
574 &wprint("w\n"); sleep(1);
576 undef(@list_disks_partitions_cache);
579 # delete_partition(disk, partition)
580 # Delete an existing partition
583 my ($disk, $part) = @_;
586 my $cmd = "parted -s ".$disk." rm ".$part;
587 my $out = &backquote_logged("$cmd </dev/null 2>&1");
589 &error("$cmd failed : $out");
596 local $rv = &wait_for($fh, 'Partition.*:', 'Selected partition');
597 &wprint("$part\n") if ($rv == 0);
598 &wait_for($fh, 'Command.*:');
600 &wait_for($fh, 'Syncing');
604 undef(@list_disks_partitions_cache);
607 # create_partition(disk, partition, start, end, type)
608 # Create a new partition with the given extent and type
611 my ($disk, $part, $start, $end, $type) = @_;
614 my $pe = $part > 4 ? "logical" : "primary";
617 $cmd = "parted -s ".$disk." unit cyl mkpartfs ".$pe." ".
618 $type." ".($start-1)." ".$end;
621 $cmd = "parted -s ".$disk." unit cyl mkpart ".$pe." ".
624 my $out = &backquote_logged("$cmd </dev/null 2>&1");
626 &error("$cmd failed : $out");
633 local $wf = &wait_for($fh, 'primary.*\r?\n', 'First.*:');
639 local $wf2 = &wait_for($fh, 'Partition.*:',
640 'Selected partition');
641 &wprint("$part\n") if ($wf2 == 0);
643 &wait_for($fh, 'First.*:') if ($wf != 1);
645 &wait_for($fh, 'Last.*:');
647 &wait_for($fh, 'Command.*:');
650 local $rv = &wait_for($fh, 'Partition.*:', 'Selected partition');
651 &wprint("$part\n") if ($rv == 0);
652 &wait_for($fh, 'Hex.*:');
654 &wait_for($fh, 'Command.*:');
656 &wait_for($fh, 'Syncing'); sleep(3);
659 undef(@list_disks_partitions_cache);
662 # create_extended(disk, partition, start, end)
663 # Create a new extended partition
666 my ($disk, $part, $start, $end) = @_;
668 # Create using parted
669 my $cmd = "parted -s ".$disk." unit cyl mkpart extended ".
671 my $out = &backquote_logged("$cmd </dev/null 2>&1");
673 &error("$cmd failed : $out");
680 &wait_for($fh, 'primary.*\r?\n');
682 &wait_for($fh, 'Partition.*:');
684 &wait_for($fh, 'First.*:');
686 &wait_for($fh, 'Last.*:');
688 &wait_for($fh, 'Command.*:');
691 &wait_for($fh, 'Syncing');
695 undef(@list_disks_partitions_cache);
699 # Returns a list of known partition tag numbers
704 return sort { $a cmp $b } (keys %parted_tags);
707 # Classic fdisk types
708 return sort { hex($a) <=> hex($b) } (keys %tags);
713 # Returns a human-readable version of a tag
716 return $tags{$_[0]} || $parted_tags{$_[0]} || $hidden_tags{$_[0]};
721 return $has_parted ? 'ext2' : '83';
725 # Given a partition tag, returns the filesystem type (assuming it is supported)
731 # Use parted type names
732 if ($tag eq "fat16") {
735 elsif ($tag eq "fat32") {
738 elsif ($tag eq "ext2") {
739 @rv = ( "ext3", "ext4", "ext2", "xfs", "reiserfs" );
741 elsif ($tag eq "hfs") {
744 elsif ($tag eq "linux-swap") {
747 elsif ($tag eq "NTFS") {
750 elsif ($tag eq "reiserfs") {
753 elsif ($tag eq "ufs") {
762 if ($tag eq "4" || $tag eq "6" || $tag eq "1" || $tag eq "e") {
765 elsif ($tag eq "b" || $tag eq "c") {
768 elsif ($tag eq "83") {
769 @rv = ( "ext3", "ext4", "ext2", "xfs", "reiserfs" );
771 elsif ($tag eq "82") {
774 elsif ($tag eq "81") {
781 local %supp = map { $_, 1 } &mount::list_fstypes();
782 @rv = grep { $supp{$_} } @rv;
783 return wantarray ? @rv : $rv[0];
787 # Returns a readable name for a filesystem type
790 return $text{"fs_".$_[0]};
795 if ($_[0] eq "ext2") {
796 &opt_input("ext2_b", $text{'bytes'}, 1);
797 &opt_input("ext2_f", $text{'bytes'}, 0);
798 &opt_input("ext2_i", "", 1);
799 &opt_input("ext2_m", "%", 0);
800 &opt_input("ext2_g", "", 1);
801 print &ui_table_row($text{'ext2_c'},
802 &ui_yesno_radio("ext2_c", 0));
804 elsif ($_[0] eq "msdos" || $_[0] eq "vfat") {
805 &opt_input("msdos_ff", "", 1);
806 print &ui_table_row($text{'msdos_F'},
807 &ui_select("msdos_F", undef,
808 [ [ undef, $text{'default'} ],
809 [ 12 ], [ 16 ], [ 32 ],
810 [ "*", $text{'msdos_F_other'} ] ])." ".
811 &ui_textbox("msdos_F_other", undef, 4));
812 &opt_input("msdos_i", "", 1);
813 &opt_input("msdos_n", "", 0);
814 &opt_input("msdos_r", "", 1);
815 &opt_input("msdos_s", "sectors", 0);
816 print &ui_table_row($text{'msdos_c'},
817 &ui_yesno_radio("msdos_c", 0));
819 elsif ($_[0] eq "minix") {
820 &opt_input("minix_n", "", 1);
821 &opt_input("minix_i", "", 0);
822 &opt_input("minix_b", "", 1);
823 print &ui_table_row($text{'minix_c'},
824 &ui_yesno_radio("minix_c", 0));
826 elsif ($_[0] eq "reiserfs") {
827 print &ui_table_row($text{'reiserfs_force'},
828 &ui_yesno_radio("reiserfs_f", 0));
830 print &ui_table_row($text{'reiserfs_hash'},
831 &ui_select("reiserfs_h", "",
832 [ [ "", $text{'default'} ],
833 [ "rupasov", "tea" ] ]));
835 elsif ($_[0] =~ /^ext\d+$/) {
836 &opt_input("ext2_b", $text{'bytes'}, 1);
837 &opt_input("ext2_f", $text{'bytes'}, 0);
838 &opt_input("ext2_i", "", 1);
839 &opt_input("ext2_m", "%", 0);
840 &opt_input("ext3_j", "MB", 1);
841 print &ui_table_row($text{'ext2_c'},
842 &ui_yesno_radio("ext2_c", 0));
844 elsif ($_[0] eq "xfs") {
845 print &ui_table_row($text{'xfs_force'},
846 &ui_yesno_radio("xfs_f", 0));
847 &opt_input("xfs_b", $text{'bytes'}, 0);
849 elsif ($_[0] eq "jfs") {
850 &opt_input("jfs_s", $text{'megabytes'}, 1);
851 print &ui_table_row($text{'jfs_c'},
852 &ui_yesno_radio("jfs_c", 0));
854 elsif ($_[0] eq "fatx") {
856 print &ui_table_row(undef, $text{'fatx_none'}, 4);
860 # mkfs_parse(type, device)
861 # Returns a command to build a new filesystem of the given type on the
862 # given device. Options are taken from %in.
866 if ($_[0] eq "ext2") {
867 $cmd = "mkfs -t ext2";
868 $cmd .= &opt_check("ext2_b", '\d+', "-b");
869 $cmd .= &opt_check("ext2_f", '\d+', "-f");
870 $cmd .= &opt_check("ext2_i", '\d{4,}', "-i");
871 $cmd .= &opt_check("ext2_m", '\d+', "-m");
872 $cmd .= &opt_check("ext2_g", '\d+', "-g");
873 $cmd .= $in{'ext2_c'} ? " -c" : "";
877 elsif ($_[0] eq "msdos" || $_[0] eq "vfat") {
878 $cmd = "mkfs -t $_[0]";
879 $cmd .= &opt_check("msdos_ff", '[1-2]', "-f");
880 if ($in{'msdos_F'} eq '*') {
881 $in{'msdos_F_other'} =~ /^\d+$/ ||
882 &error(&text('opt_error', $in{'msdos_F_other'},
884 $cmd .= " -F ".$in{'msdos_F_other'};
886 elsif ($in{'msdos_F'}) {
887 $cmd .= " -F ".$in{'msdos_F'};
889 $cmd .= &opt_check("msdos_i", '[0-9a-f]{8}', "-i");
890 $cmd .= &opt_check("msdos_n", '\S{1,11}', "-n");
891 $cmd .= &opt_check("msdos_r", '\d+', "-r");
892 $cmd .= &opt_check("msdos_s", '\d+', "-s");
893 $cmd .= $in{'msdos_c'} ? " -c" : "";
896 elsif ($_[0] eq "minix") {
897 local(@plist, $disk, $part, $i, @pinfo);
898 $cmd = "mkfs -t minix";
899 $cmd .= &opt_check("minix_n", '14|30', "-n ");
900 $cmd .= &opt_check("minix_i", '\d+', "-i ");
901 $cmd .= $in{'minix_c'} ? " -c" : "";
902 $cmd .= &opt_check("minix_b", '\d+', " ");
905 elsif ($_[0] eq "reiserfs") {
906 $cmd = "yes | mkreiserfs";
907 $cmd .= " -f" if ($in{'reiserfs_f'});
908 $cmd .= " -h $in{'reiserfs_h'}" if ($in{'reiserfs_h'});
911 elsif ($_[0] =~ /^ext\d+$/) {
912 if (&has_command("mkfs.$_[0]")) {
913 $cmd = "mkfs -t $_[0]";
914 $cmd .= &opt_check("ext3_j", '\d+', "-j");
916 elsif ($_[0] eq "ext3" && &has_command("mke3fs")) {
918 $cmd .= &opt_check("ext3_j", '\d+', "-j");
920 elsif ($_[0] eq "ext4" && &has_command("mke4fs")) {
922 $cmd .= &opt_check("ext3_j", '\d+', "-j");
925 $cmd = "mkfs.ext2 -j";
926 if (!$in{'ext3_j_def'}) {
927 $in{'ext3_j'} =~ /^\d+$/ ||
928 &error(&text('opt_error', $in{'ext3_j'},
930 $cmd .= " -J size=$in{'ext3_j'}";
933 $cmd .= &opt_check("ext2_b", '\d+', "-b");
934 $cmd .= &opt_check("ext2_f", '\d+', "-f");
935 $cmd .= &opt_check("ext2_i", '\d{4,}', "-i");
936 $cmd .= &opt_check("ext2_m", '\d+', "-m");
937 $cmd .= $in{'ext2_c'} ? " -c" : "";
941 elsif ($_[0] eq "xfs") {
942 $cmd = "mkfs -t $_[0]";
943 $cmd .= " -f" if ($in{'xfs_f'});
944 $cmd .= " -b size=$in{'xfs_b'}" if (!$in{'xfs_b_def'});
947 elsif ($_[0] eq "jfs") {
948 $cmd = "mkfs -t $_[0] -q";
949 $cmd .= &opt_check("jfs_s", '\d+', "-s");
950 $cmd .= " -c" if ($in{'jfs_c'});
953 elsif ($_[0] eq "fatx") {
954 $cmd = "mkfs -t $_[0] $_[1]";
956 if (&has_command("partprobe")) {
957 $cmd = "partprobe ; $cmd";
963 # Returns 1 if this filesystem type can be tuned
966 return $_[0] =~ /^ext\d+$/;
969 # tunefs_options(type)
970 # Output HTML for tuning options for some filesystem type
973 if ($_[0] =~ /^ext\d+$/) {
974 # Gaps between checks
975 &opt_input("tunefs_c", "", 1);
978 print &ui_table_row($text{'tunefs_e'},
979 &ui_radio("tunefs_e_def", 1,
980 [ [ 1, $text{'opt_default'} ],
981 [ 0, &ui_select("tunefs_e", undef,
982 [ [ "continue", $text{'tunefs_continue'} ],
983 [ "remount-ro", $text{'tunefs_remount'} ],
984 [ "panic", $text{'tunefs_panic'} ] ]) ] ]));
987 print &ui_table_row($text{'tunefs_u'},
988 &ui_opt_textbox("tunefs_u", undef, 13, $text{'opt_default'})." ".
989 &user_chooser_button("tunefs_u", 0));
992 print &ui_table_row($text{'tunefs_g'},
993 &ui_opt_textbox("tunefs_g", undef, 13, $text{'opt_default'})." ".
994 &group_chooser_button("tunefs_g", 0));
997 &opt_input("tunefs_m", "%", 1);
999 # Time between checks
1000 $tsel = &ui_select("tunefs_i_unit", undef,
1001 [ [ "d", $text{'tunefs_days'} ],
1002 [ "w", $text{'tunefs_weeks'} ],
1003 [ "m", $text{'tunefs_months'} ] ]);
1004 &opt_input("tunefs_i", $tsel, 0);
1008 # tunefs_parse(type, device)
1009 # Returns the tuning command based on user inputs
1012 if ($_[0] =~ /^ext\d+$/) {
1014 $cmd .= &opt_check("tunefs_c", '\d+', "-c");
1015 $cmd .= $in{'tunefs_e_def'} ? "" : " -e$in{'tunefs_e'}";
1016 $cmd .= $in{'tunefs_u_def'} ? "" : " -u".getpwnam($in{'tunefs_u'});
1017 $cmd .= $in{'tunefs_g_def'} ? "" : " -g".getgrnam($in{'tunefs_g'});
1018 $cmd .= &opt_check("tunefs_m",'\d+',"-m");
1019 $cmd .= &opt_check("tunefs_i", '\d+', "-i").
1020 ($in{'tunefs_i_def'} ? "" : $in{'tunefs_i_unit'});
1027 # Returns 1 if a reboot is needed after changing the partitions on some disk
1030 local $un = `uname -r`;
1031 return $un =~ /^2\.0\./ || $un =~ /^1\./ || $un =~ /^0\./;
1034 # device_status(device)
1035 # Returns an array of directory, type, mounted
1038 @mounted = &foreign_call("mount", "list_mounted") if (!@mounted);
1039 @mounts = &foreign_call("mount", "list_mounts") if (!@mounts);
1040 local $label = &get_label($_[0]);
1041 local $volid = &get_volid($_[0]);
1043 local ($mounted) = grep { &same_file($_->[1], $_[0]) ||
1044 $_->[1] eq "LABEL=$label" ||
1045 $_->[1] eq "UUID=$volid" } @mounted;
1046 local ($mount) = grep { &same_file($_->[1], $_[0]) ||
1047 $_->[1] eq "LABEL=$label" ||
1048 $_->[1] eq "UUID=$volid" } @mounts;
1049 if ($mounted) { return ($mounted->[0], $mounted->[2], 1,
1050 &indexof($mount, @mounts),
1051 &indexof($mounted, @mounted)); }
1052 elsif ($mount) { return ($mount->[0], $mount->[2], 0,
1053 &indexof($mount, @mounts)); }
1055 $raidconf = &foreign_call("raid", "get_raidtab") if (!$raidconf);
1056 foreach $c (@$raidconf) {
1057 foreach $d (&raid::find_value('device', $c->{'members'})) {
1058 return ( $c->{'value'}, "raid", 1 ) if ($d eq $_[0]);
1063 if (!scalar(@physical_volumes)) {
1064 @physical_volumes = ();
1065 foreach $vg (&foreign_call("lvm", "list_volume_groups")) {
1066 push(@physical_volumes,
1067 &foreign_call("lvm", "list_physical_volumes",
1071 foreach $pv (@physical_volumes) {
1072 return ( $pv->{'vg'}, "lvm", 1)
1073 if ($pv->{'device'} eq $_[0]);
1080 # Returns 1 if some filesystem type can fsck'd
1083 return ($_[0] =~ /^ext\d+$/ && &has_command("fsck.$_[0]") ||
1084 $_[0] eq "minix" && &has_command("fsck.minix"));
1087 # fsck_command(type, device)
1088 # Returns the fsck command to unconditionally check a filesystem
1091 if ($_[0] =~ /^ext\d+$/) {
1092 return "fsck -t $_[0] -p $_[1]";
1094 elsif ($_[0] eq "minix") {
1095 return "fsck -t minix -a $_[1]";
1100 # Returns a description of an exit code from fsck
1103 return $text{"fsck_err$_[0]"} ? $text{"fsck_err$_[0]"}
1104 : &text("fsck_unknown", $_[0]);
1107 # partition_select(name, value, mode, [&found], [disk_regexp])
1108 # Returns HTML for selecting a disk or partition
1109 # mode 0 = floppies and disk partitions
1111 # 2 = floppies and disks and disk partitions
1112 # 3 = disk partitions
1113 sub partition_select
1115 local $rv = "<select name=$_[0]>\n";
1116 local ($found, $d, $p);
1117 if (($_[2] == 0 || $_[2] == 2) &&
1118 (-r "/dev/fd0" || $_[1] =~ /^\/dev\/fd[01]$/)) {
1119 $rv .= sprintf "<option %s value=/dev/fd0>%s\n",
1120 $_[1] eq "/dev/fd0" ? "selected" : "",
1121 &text('select_fd', 0) if (!$_[4] || "/dev/fd0" =~ /$_[4]/);
1122 $rv .= sprintf "<option %s value=/dev/fd1>%s\n",
1123 $_[1] eq "/dev/fd1" ? "selected" : "",
1124 &text('select_fd', 1) if (!$_[4] || "/dev/fd1" =~ /$_[4]/);
1125 $found++ if ($_[1] =~ /^\/dev\/fd[01]$/);
1127 local @dlist = &list_disks_partitions();
1128 foreach $d (@dlist) {
1129 local $dev = $d->{'device'};
1130 next if ($_[4] && $dev !~ /$_[4]/);
1131 if ($_[2] == 1 || $_[2] == 2) {
1132 local $name = $d->{'desc'};
1133 $name .= " ($d->{'model'})" if ($d->{'model'});
1134 $rv .= sprintf "<option value=%s %s>%s\n",
1135 $dev, $_[1] eq $dev ? "selected" : "", $name;
1136 $found++ if ($dev eq $_[1]);
1138 if ($_[2] == 0 || $_[2] == 2 || $_[2] == 3) {
1139 foreach $p (@{$d->{'parts'}}) {
1140 next if ($p->{'extended'});
1141 local $name = $p->{'desc'};
1142 $name .= " (".&tag_name($p->{'type'}).")"
1143 if (&tag_name($p->{'type'}));
1144 $rv .= sprintf "<option %s value=%s>%s\n",
1145 $_[1] eq $p->{'device'} ? "selected" : "",
1146 $p->{'device'}, $name;
1147 $found++ if ($_[1] eq $p->{'device'});
1151 if (!$found && $_[1] && !$_[3]) {
1152 $rv .= "<option selected>$_[1]\n";
1157 $rv .= "</select>\n";
1161 # label_select(name, value, &found)
1162 # Returns HTML for selecting a filesystem label
1165 local $rv = "<select name=$_[0]>\n";
1166 local @dlist = &list_disks_partitions();
1168 foreach $d (@dlist) {
1169 local $dev = $d->{'device'};
1170 foreach $p (@{$d->{'parts'}}) {
1171 next if ($p->{'type'} ne '83');
1172 local $label = &get_label($p->{'device'});
1174 $rv .= sprintf "<option %s value=%s>%s (%s)\n",
1175 $_[1] eq $label ? "selected" : "",
1176 $label, $label, $p->{'desc'};
1177 ${$_[2]}++ if ($_[1] eq $label);
1181 if (!$found && $_[1] && !$_[2]) {
1182 $rv .= "<option selected>$_[1]\n";
1184 $rv .= "</select>\n";
1185 return $any ? $rv : undef;
1188 # volid_select(name, value, &found)
1189 # Returns HTML for selecting a filesystem UUID
1192 local ($name, $value, $found) = @_;
1193 local @dlist = &list_disks_partitions();
1195 foreach my $d (@dlist) {
1196 local $dev = $d->{'device'};
1197 foreach $p (@{$d->{'parts'}}) {
1198 next if ($p->{'type'} ne '83' && $p->{'type'} ne '82' &&
1199 $p->{'type'} ne 'b' && $p->{'type'} ne 'c');
1200 local $volid = &get_volid($p->{'device'});
1202 push(@opts, [ $volid, "$volid ($p->{'desc'})" ]);
1203 $$found++ if ($value eq $volid);
1207 return &ui_select($name, $value, \@opts, 1, 0, $value ? 1 : 0);
1214 #############################################################################
1215 # Internal functions
1216 #############################################################################
1219 local $fpath = &check_fdisk();
1220 ($fh, $fpid) = &foreign_call("proc", "pty_process_exec", join(" ",$fpath, @_));
1225 local $sfpath = &has_command("sfdisk");
1226 ($fh, $fpid) = &foreign_call("proc", "pty_process_exec", join(" ",$sfpath, @_));
1231 local $fpath = &has_command("fdisk");
1232 &error(&text('open_error', "<tt>fdisk</tt>")) if (!$fpath);
1238 close($fh); kill('TERM', $fpid);
1243 syswrite($fh, $_[0], length($_[0]));
1248 print &ui_table_row($text{$_[0]},
1249 &ui_opt_textbox($_[0], undef, 6, $text{'opt_default'})." ".$_[1]);
1254 if ($in{"$_[0]_def"}) { return ""; }
1255 elsif ($in{$_[0]} !~ /^$_[1]$/) {
1256 &error(&text('opt_error', $in{$_[0]}, $text{$_[0]}));
1258 else { return " $_[2] $in{$_[0]}"; }
1261 %tags = ('0', 'Empty',
1269 '9', 'AIX bootable',
1270 'a', 'OS/2 boot manager',
1271 'b', 'Windows FAT32',
1272 'c', 'Windows FAT32 LBA',
1273 'e', 'Windows FAT16 LBA',
1275 '11', 'Hidden FAT12',
1276 '12', 'Compaq diagnostic',
1277 '14', 'Hidden FAT16 < 32M',
1278 '16', 'Hidden FAT16',
1279 '17', 'Hidden NTFS',
1280 '18', 'AST Windows swapfile',
1281 '1b', 'Hidden Windows FAT (1b)',
1282 '1c', 'Hidden Windows FAT (1c)',
1283 '1e', 'Hidden Windows FAT (1e)',
1285 '3c', 'PartitionMagic recovery',
1286 '40', 'Venix 80286',
1287 '41', 'PPC PReP boot',
1290 '4e', 'QNX 4.x 2nd partition',
1291 '4f', 'QNX 4.x 3rd partition',
1293 '51', 'OnTrack DM6 Aux1',
1295 '53', 'OnTrack DM6 Aux3',
1296 '54', 'OnTrack DM6',
1299 '5c', 'Priam Edisk',
1301 '63', 'GNU HURD or SysV',
1302 '64', 'Novell Netware 286',
1303 '65', 'Novell Netware 386',
1304 '70', 'DiskSecure Multi-Boot',
1307 '81', 'Minix / Old Linux / Solaris',
1310 '84', 'OS/2 hidden C: drive',
1311 '85', 'Linux extended',
1312 '86', 'NTFS volume set (86)',
1313 '87', 'NTFS volume set (87)',
1317 'a0', 'IBM Thinkpad hibernation',
1321 'b7', 'BSDI filesystem',
1323 'c1', 'DRDOS/sec FAT12',
1324 'c4', 'DRDOS/sec FAT16 <32M',
1325 'c6', 'DRDOS/sec FAT16',
1327 'db', 'CP/M / CTOS',
1329 'e3', 'DOS read-only',
1334 'f4', 'SpeedStor large partition',
1335 'f2', 'DOS secondary',
1343 'f', 'Windows extended LBA',
1348 'fat16', 'Windows FAT16',
1349 'fat32', 'Windows FAT32',
1352 'linux-swap', 'Linux Swap',
1353 'NTFS', 'Windows NTFS',
1354 'reiserfs', 'ReiserFS',
1355 'ufs', 'FreeBSD UFS',
1358 @space_type = ( '1', '4', '5', '6', 'b', 'c', 'e', '83' );
1360 # can_edit_disk(device)
1364 $device =~ s/\d+$//;
1365 foreach (split(/\s+/, $access{'disks'})) {
1366 return 1 if ($_ eq "*" || $_ eq $device);
1371 # disk_space(device, [mountpoint])
1372 # Returns the amount of total and free space for some filesystem, or an
1373 # empty array if not appropriate.
1376 local $w = $_[1] || $_[0];
1377 local $out = `df -k '$w'`;
1378 if ($out =~ /Mounted on\s*\n\s*\S+\s+(\S+)\s+\S+\s+(\S+)/i) {
1381 elsif ($out =~ /Mounted on\s*\n\S+\s*\n\s+(\S+)\s+\S+\s+(\S+)/i) {
1389 # supported_filesystems()
1390 # Returns a list of filesystem types that can have mkfs_options called on them
1391 sub supported_filesystems
1393 local @fstypes = ( "ext2" );
1394 push(@fstypes, "ext3") if (&has_command("mkfs.ext3") ||
1395 &has_command("mke3fs") ||
1396 `mkfs.ext2 -h 2>&1` =~ /\[-j\]/);
1397 push(@fstypes, "ext4") if (&has_command("mkfs.ext4") ||
1398 &has_command("mke4fs"));
1399 push(@fstypes, "reiserfs") if (&has_command("mkreiserfs"));
1400 push(@fstypes, "xfs") if (&has_command("mkfs.xfs"));
1401 push(@fstypes, "jfs") if (&has_command("mkfs.jfs"));
1402 push(@fstypes, "fatx") if (&has_command("mkfs.fatx"));
1403 push(@fstypes, "msdos");
1404 push(@fstypes, "vfat");
1405 push(@fstypes, "minix");
1409 # get_label(device, [type])
1410 # Returns the XFS or EXT label for some device's filesystem
1415 $label = `e2label $_[0] 2>&1`;
1418 if (($? || $label !~ /\S/) && $has_xfs_db) {
1420 local $out = &backquote_with_timeout("xfs_db -x -p xfs_admin -c label -r $_[0] 2>&1", 5);
1421 $label = $1 if ($out =~ /label\s*=\s*"(.*)"/ &&
1424 if (($? || $label !~ /\S/) && $has_reiserfstune) {
1426 local $out = &backquote_command("reiserfstune $_[0]");
1427 if ($out =~ /LABEL:\s*(\S+)/) {
1431 return $? || $label !~ /\S/ ? undef : $label;
1435 # Returns the UUID for some device's filesystem
1438 local ($device) = @_;
1440 if (-d $uuid_directory) {
1441 # Use UUID mapping directory
1442 opendir(DIR, $uuid_directory);
1443 foreach my $f (readdir(DIR)) {
1444 local $linkdest = &simplify_path(
1445 &resolve_links("$uuid_directory/$f"));
1446 if ($linkdest eq $device) {
1453 elsif ($has_volid) {
1454 # Use vol_id command
1455 local $out = &backquote_command(
1456 "vol_id ".quotemeta($device)." 2>&1", 1);
1457 if ($out =~ /ID_FS_UUID=(\S+)/) {
1464 # set_label(device, label, [type])
1465 # Tries to set the label for some device's filesystem
1468 if ($has_e2label && ($_[2] =~ /^ext[23]$/ || !$_[2])) {
1469 &system_logged("e2label '$_[0]' '$_[1]' >/dev/null 2>&1");
1472 if ($has_xfs_db && ($_[2] eq "xfs" || !$_[2])) {
1473 &system_logged("xfs_db -x -p xfs_admin -c \"label $_[1]\" $_[0] >/dev/null 2>&1");
1479 # set_name(&disk, &partition, name)
1480 # Sets the name of a partition, for partition types that support it
1483 my ($dinfo, $pinfo, $name) = @_;
1484 my $cmd = "parted -s ".$dinfo->{'device'}." name ".$pinfo->{'number'}." ";
1486 $cmd .= quotemeta($name);
1491 my $out = &backquote_logged("$cmd </dev/null 2>&1");
1493 &error("$cmd failed : $out");
1497 # set_partition_table(device, table-type)
1498 # Wipe and re-create the partition table on some disk
1499 sub set_partition_table
1501 my ($disk, $table) = @_;
1502 my $cmd = "parted -s ".$disk." mktable ".$table;
1503 my $out = &backquote_logged("$cmd </dev/null 2>&1");
1505 &error("$cmd failed : $out");
1509 # supports_label(&partition)
1510 # Returns 1 if the label can be set on a partition
1514 return $part->{'type'} eq '83' || $part->{'type'} eq 'ext2';
1517 # supports_name(&disk)
1518 # Returns 1 if the name can be set on a disk's partitions
1522 return $disk->{'table'} eq 'gpt';
1525 # supports_hdparm(&disk)
1529 return $d->{'type'} eq 'ide' || $d->{'type'} eq 'scsi' && $d->{'model'} =~ /ATA/;
1532 # supports_relabel(&disk)
1533 # Return 1 if a disk can have it's partition table re-written
1534 sub supports_relabel
1536 return $has_parted ? 1 : 0;
1539 # supports_smart(&disk)
1542 return &foreign_installed("smart-status") &&
1543 &foreign_available("smart-status");
1546 # supports_extended(&disk)
1547 # Return 1 if some disk can support extended partitions
1548 sub supports_extended
1551 return $disk->{'label'} eq 'msdos' ? 1 : 0;
1554 # list_table_types(&disk)
1555 # Returns the list of supported partition table types for a disk
1556 sub list_table_types
1559 return ( 'msdos', 'gpt', 'bsd', 'dvh', 'loop', 'mac', 'pc98', 'sun' );
1566 # get_parted_version()
1567 # Returns the version number of parted that is installed
1568 sub get_parted_version
1570 my $out = &backquote_command("parted -v 2>&1 </dev/null");
1571 return $out =~ /parted.*\s([0-9\.]+)/i ? $1 : undef;