[ Avaa Bypassed ]




Upload:

Command:

www-data@3.144.134.101: ~ $
# fdisk-lib.pl
# Functions for disk management under linux

BEGIN { push(@INC, ".."); };
use WebminCore;
&init_config();
&foreign_require("mount", "mount-lib.pl");
if (&foreign_check("raid")) {
	&foreign_require("raid");
	$raid_module++;
	}
if (&foreign_check("lvm")) {
	&foreign_require("lvm");
	$lvm_module++;
	}
if (&foreign_check("iscsi-server")) {
	&foreign_require("iscsi-server");
	$iscsi_server_module++;
	}
if (&foreign_check("iscsi-target")) {
	&foreign_require("iscsi-target");
	$iscsi_target_module++;
	}
&foreign_require("proc", "proc-lib.pl");
%access = &get_module_acl();
$has_e2label = &has_command("e2label");
$has_xfs_db = &has_command("xfs_db");
$has_volid = &has_command("vol_id");
$has_reiserfstune = &has_command("reiserfstune");
$uuid_directory = "/dev/disk/by-uuid";
if ($config{'mode'} eq 'parted') {
	$has_parted = 1;
	}
elsif ($config{'mode'} eq 'fdisk') {
	$has_parted = 0;
	}
else {
	$has_parted = !$config{'noparted'} && &has_command("parted") &&
		      &get_parted_version() >= 1.8;
	}
$| = 1;

# list_disks_partitions([include-cds])
# Returns a structure containing the details of all disks and partitions
sub list_disks_partitions
{
if (scalar(@list_disks_partitions_cache)) {
	return @list_disks_partitions_cache;
	}

local (@pscsi, @dscsi, $dscsi_mode);
if (-r "/proc/scsi/sg/devices" && -r "/proc/scsi/sg/device_strs") {
	# Get device info from various /proc/scsi files
	open(DEVICES, "</proc/scsi/sg/devices");
	while(<DEVICES>) {
		s/\r|\n//g;
		local @l = split(/\t+/, $_);
		push(@dscsi, { 'host' => $l[0],
			       'bus' => $l[1],
			       'target' => $l[2],
			       'lun' => $l[3],
			       'type' => $l[4] });
		}
	close(DEVICES);
	local $i = 0;
	open(DEVNAMES, "</proc/scsi/sg/device_strs");
	while(<DEVNAMES>) {
		s/\r|\n//g;
		local @l = split(/\t+/, $_);
		$dscsi[$i]->{'make'} = $l[0];
		$dscsi[$i]->{'model'} = $l[1];
		$i++;
		}
	close(DEVNAMES);
	$dscsi_mode = 1;
	@dscsi = grep { $_->{'type'} == 0 } @dscsi;
	}
else {
	# Check /proc/scsi/scsi for SCSI disk models
	open(SCSI, "</proc/scsi/scsi");
	local @lines = <SCSI>;
	close(SCSI);
	if ($lines[0] =~ /^Attached\s+domains/i) {
		# New domains format
		local $dscsi;
		foreach (@lines) {
			s/\s/ /g;
			if (/Device:\s+(.*)(sd[a-z]+)\s+usage/) {
				$dscsi = { 'dev' => $2 };
				push(@dscsi, $dscsi);
				}
			elsif (/Device:/) {
				$dscsi = undef;
				}
			elsif (/Vendor:\s+(\S+)\s+Model:\s+(\S+)/ && $dscsi) {
				$dscsi->{'make'} = $1;
				$dscsi->{'model'} = $2;
				}
			elsif (/Host:\s+scsi(\d+)\s+Channel:\s+(\d+)\s+Id:\s+(\d+)\s+Lun:\s+(\d+)/ && $dscsi) {
				$dscsi->{'host'} = $1;
				$dscsi->{'bus'} = $2;
				$dscsi->{'target'} = $3;
				$dscsi->{'lun'} = $4;
				}
			}
		$dscsi_mode = 1;
		}
	else {
		# Standard format
		foreach (@lines) {
			s/\s/ /g;
			if (/^Host:/) {
				push(@pscsi, $_);
				}
			elsif (/^\s+\S/ && @pscsi) {
				$pscsi[$#pscsi] .= $_;
				}
			}
		@pscsi = grep { /Type:\s+Direct-Access/i } @pscsi;
		$dscsi_mode = 0;
		}
	}

local (@disks, @devs, $d);
if (open(PARTS, "</proc/partitions")) {
	# The list of all disks can come from the kernel
	local $sc = 0;
	while(<PARTS>) {
		if (/\d+\s+\d+\s+\d+\s+sd([a-z]+)\s/ ||
		    /\d+\s+\d+\s+\d+\s+(scsi\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/disc)\s+/) {
			# New or old style SCSI device
			local $d = $1;
			local ($host, $bus, $target, $lun) = ($2, $3, $4, $5);
			if (!$dscsi_mode && $pscsi[$sc] =~ /USB-FDU/) {
				# USB floppy with scsi emulation!
				splice(@pscsi, $sc, 1);
				next;
				}
			if ($host ne '') {
				local $scsidev = "/dev/$d";
				if (!-r $scsidev) {
					push(@devs, "/dev/".
						  &number_to_device("sd", $sc));
					}
				else {
					push(@devs, $scsidev);
					}
				}
			else {
				push(@devs, "/dev/sd$d");
				}
			$sc++;
			}
		elsif (/\d+\s+\d+\s+\d+\s+hd([a-z]+)\s/) {
			# IDE disk (but skip CDs)
			local $n = $1;
			if (open(MEDIA, "</proc/ide/hd$n/media")) {
				local $media = <MEDIA>;
				close(MEDIA);
				if ($media =~ /^disk/ && !$_[0]) {
					push(@devs, "/dev/hd$n");
					}
				}
			}
		elsif (/\d+\s+\d+\s+\d+\s+(ide\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/disc)\s+/) {
			# New-style IDE disk
			local $idedev = "/dev/$1";
			local ($host, $bus, $target, $lun) = ($2, $3, $4, $5);
			if (!-r $idedev) {
				push(@devs, "/dev/".
				    &hbt_to_device($host, $bus, $target));
				}
			else {
				push(@devs, "/dev/$1");
				}
			}
		elsif (/\d+\s+\d+\s+\d+\s+(rd\/c(\d+)d\d+)\s/) {
			# Mylex raid device
			push(@devs, "/dev/$1");
			}
		elsif (/\d+\s+\d+\s+\d+\s+(ida\/c(\d+)d\d+)\s/) {
			# Compaq raid device
			push(@devs, "/dev/$1");
			}
		elsif (/\d+\s+\d+\s+\d+\s+(cciss\/c(\d+)d\d+)\s/) {
			# Compaq Smart Array RAID
			push(@devs, "/dev/$1");
			}
		elsif (/\d+\s+\d+\s+\d+\s+(ataraid\/disc(\d+)\/disc)\s+/) {
			# Promise raid controller
			push(@devs, "/dev/$1");
			}
		elsif (/\d+\s+\d+\s+\d+\s+(vd[a-z]+)\s/) {
			# Virtio disk from KVM
			push(@devs, "/dev/$1");
			}
		elsif (/\d+\s+\d+\s+\d+\s+(xvd[a-z]+)\s/) {
			# PV disk from Xen
			push(@devs, "/dev/$1");
			}
		elsif (/\d+\s+\d+\s+\d+\s+(mmcblk\d+)\s/) {
			# SD card / MMC, seen on Raspberry Pi
			push(@devs, "/dev/$1");
			}
		elsif (/\d+\s+\d+\s+\d+\s+(nvme\d+n\d+)\s/) {
			# NVME SSD
			push(@devs, "/dev/$1");
			}
		}
	close(PARTS);

	# Sort IDE first
	@devs = sort { ($b =~ /\/hd[a-z]+$/ ? 1 : 0) <=>
		       ($a =~ /\/hd[a-z]+$/ ? 1 : 0) } @devs;
	}
return ( ) if (!@devs);		# No disks, ie on Xen

# Skip cd-rom drive, identified from symlink. Don't do this if we can identify
# cds by their media type though
if (!-d "/proc/ide") {
	local @cdstat = stat("/dev/cdrom");
	if (@cdstat && !$_[0]) {
		@devs = grep { (stat($_))[1] != $cdstat[1] } @devs;
		}
	}

# Get Linux disk ID mapping
local %id_map;
local %all_id_map;
local $id_dir = "/dev/disk/by-id";
opendir(IDS, $id_dir);
foreach my $id (readdir(IDS)) {
	local $id_link = readlink("$id_dir/$id");
	if ($id_link) {
		local $id_real = &simplify_path(&resolve_links("$id_dir/$id"));
		$id_map{$id_real} = $id;
		$all_id_map{$id_real} ||= [];
		push(@{$all_id_map{$id_real}}, $id);
		}
	}
closedir(IDS);

# Call fdisk to get partition and geometry information
local $devs = join(" ", @devs);
local ($disk, $m2);
if ($has_parted) {
	open(FDISK, join(" ; ",
		map { "parted $_ unit cyl print 2>/dev/null" } @devs)." |");
	}
else {
	open(FDISK, "fdisk -l -u=cylinders $devs 2>/dev/null || fdisk -l $devs 2>/dev/null |");
	}
while(<FDISK>) {
	if (($m4 = ($_ =~ /Disk\s+([^ :]+):\s+([\d\.]+)\s+(\S+),\s+(\d+)\s+bytes,\s+(\d+)\s+sectors/)) ||
	    ($m1 = ($_ =~ /Disk\s+([^ :]+):\s+(\d+)\s+\S+\s+(\d+)\s+\S+\s+(\d+)/)) ||
	    ($m2 = ($_ =~ /Disk\s+([^ :]+):\s+(.*)\s+bytes/)) ||
	    ($m3 = ($_ =~ /Disk\s+([^ :]+):\s+([0-9\.]+)cyl/))) {
		# New disk section
		if ($m3) {
			# Parted format
			# Disk /dev/sda: 121601cyl
			$disk = { 'device' => $1,
				  'prefix' => $1,
				  'cylinders' => $2 };
			}
		elsif ($m2) {
			# New style fdisk
			# Disk /dev/sda: 85.8 GB, 85899345920 bytes
			$disk = { 'device' => $1,
				  'prefix' => $1,
				  'table' => 'msdos', };
			<FDISK> =~ /(\d+)\s+\S+\s+(\d+)\s+\S+\s+(\d+)/ || next;
			$disk->{'heads'} = $1;
			$disk->{'sectors'} = $2;
			$disk->{'cylinders'} = $3;
			}
		elsif ($m4) {
			# Even newer disk (sectors/etc come later)
			# Disk /dev/sda: 10 GiB, 10737418240 bytes, 20971520 sectors
			$disk = { 'device' => $1,
				  'prefix' => $1,
				  'size' => $4,
				  'table' => 'msdos', };
			}
		else {
			# Old style fdisk
			$disk = { 'device' => $1,
				  'prefix' => $1,
				  'heads' => $2,
				  'sectors' => $3,
				  'cylinders' => $4,
				  'table' => 'msdos', };
			}
		$disk->{'index'} = scalar(@disks);
		$disk->{'parts'} = [ ];

		local @st = stat($disk->{'device'});
		next if (@cdstat && $st[1] == $cdstat[1]);
		if ($disk->{'device'} =~ /\/sd([a-z]+)$/) {
			# Old-style SCSI disk
			$disk->{'desc'} = &text('select_device', 'SCSI',
						uc($1));
			local ($dscsi) = grep { $_->{'dev'} eq "sd$1" } @dscsi;
			$disk->{'scsi'} = $dscsi ? &indexof($dscsi, @dscsi)
						 : ord(uc($1))-65;
			$disk->{'type'} = 'scsi';
			}
		elsif ($disk->{'device'} =~ /\/hd([a-z]+)$/) {
			# IDE disk
			$disk->{'desc'} = &text('select_device', 'IDE', uc($1));
			$disk->{'type'} = 'ide';
			}
		elsif ($disk->{'device'} =~ /\/xvd([a-z]+)$/) {
			# Xen virtual disk
			$disk->{'desc'} = &text('select_device', 'Xen', uc($1));
			$disk->{'type'} = 'ide';
			}
		elsif ($disk->{'device'} =~ /\/mmcblk([0-9]+)$/) {
			# SD-card / MMC
			$disk->{'desc'} = &text('select_device', 'SD-Card', $1);
			$disk->{'type'} = 'ide';
			}
		elsif ($disk->{'device'} =~ /\/vd([a-z]+)$/) {
			# KVM virtual disk
			$disk->{'desc'} = &text('select_device',
						'VirtIO', uc($1));
			$disk->{'type'} = 'ide';
			}
		elsif ($disk->{'device'} =~ /\/(scsi\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/disc)/) {
			# New complete SCSI disk specification
			$disk->{'host'} = $2;
			$disk->{'bus'} = $3;
			$disk->{'target'} = $4;
			$disk->{'lun'} = $5;
			$disk->{'desc'} = &text('select_scsi',
						"$2", "$3", "$4", "$5");

			# Work out the SCSI index for this disk
			local $j;
			if ($dscsi_mode) {
				for($j=0; $j<@dscsi; $j++) {
					if ($dscsi[$j]->{'host'} == $disk->{'host'} && $dscsi[$j]->{'bus'} == $disk->{'bus'} && $dscsi[$j]->{'target'} == $disk->{'target'} && $dscsi[$j]->{'lnun'} == $disk->{'lun'}) {
						$disk->{'scsi'} = $j;
						last;
						}
					}
				}
			else {
				for($j=0; $j<@pscsi; $j++) {
					if ($pscsi[$j] =~ /Host:\s+scsi(\d+).*Id:\s+(\d+)/i && $disk->{'host'} == $1 && $disk->{'target'} == $2) {
						$disk->{'scsi'} = $j;
						last;
						}
					}
				}
			$disk->{'type'} = 'scsi';
			$disk->{'prefix'} =~ s/disc$/part/g;
			}
		elsif ($disk->{'device'} =~ /\/(ide\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/disc)/) {
			# New-style IDE specification
			$disk->{'host'} = $2;
			$disk->{'bus'} = $3;
			$disk->{'target'} = $4;
			$disk->{'lun'} = $5;
			$disk->{'desc'} = &text('select_newide',
						"$2", "$3", "$4", "$5");
			$disk->{'type'} = 'ide';
			$disk->{'prefix'} =~ s/disc$/part/g;
			}
		elsif ($disk->{'device'} =~ /\/(rd\/c(\d+)d(\d+))/) {
			# Mylex raid device
			local ($mc, $md) = ($2, $3);
			$disk->{'desc'} = &text('select_mylex', $mc, $md);
			open(RD, "</proc/rd/c$mc/current_status");
			while(<RD>) {
				if (/^Configuring\s+(.*)/i) {
					$disk->{'model'} = $1;
					}
				elsif (/\s+(\S+):\s+([^, ]+)/ &&
				       $1 eq $disk->{'device'}) {
					$disk->{'raid'} = $2;
					}
				}
			close(RD);
			$disk->{'type'} = 'raid';
			$disk->{'prefix'} = $disk->{'device'}.'p';
			}
		elsif ($disk->{'device'} =~ /\/(ida\/c(\d+)d(\d+))/) {
			# Compaq RAID device
			local ($ic, $id) = ($2, $3);
			$disk->{'desc'} = &text('select_cpq', $ic, $id);
			open(IDA, -d "/proc/driver/array" ? "</proc/driver/array/ida$ic" : "</proc/driver/cpqarray/ida$ic");
			while(<IDA>) {
				if (/^(\S+):\s+(.*)/ && $1 eq "ida$ic") {
					$disk->{'model'} = $2;
					}
				}
			close(IDA);
			$disk->{'type'} = 'raid';
			$disk->{'prefix'} = $disk->{'device'}.'p';
			}
		elsif ($disk->{'device'} =~ /\/(cciss\/c(\d+)d(\d+))/) {
			# Compaq Smart Array RAID
			local ($ic, $id) = ($2, $3);
			$disk->{'desc'} = &text('select_smart', $ic, $id);
			open(CCI, "</proc/driver/cciss/cciss$ic");
			while(<CCI>) {
				if (/^\s*(\S+):\s*(.*)/ && $1 eq "cciss$ic") {
					$disk->{'model'} = $2;
					}
				}
			close(CCI);
			$disk->{'type'} = 'raid';
			$disk->{'prefix'} = $disk->{'device'}.'p';
			}
		elsif ($disk->{'device'} =~ /\/(ataraid\/disc(\d+)\/disc)/) {
			# Promise RAID controller
			local $dd = $2;
			$disk->{'desc'} = &text('select_promise', $dd);
			$disk->{'type'} = 'raid';
			$disk->{'prefix'} =~ s/disc$/part/g;
			}
		elsif ($disk->{'device'} =~ /\/nvme(\d+)n(\d+)$/) {
			# NVME SSD controller
			$disk->{'desc'} = &text('select_nvme', "$1", "$2");
			$disk->{'type'} = 'scsi';
			$disk->{'prefix'} = $disk->{'device'}.'p';
			}

		# Work out short name, like sda
		local $short;
		if (defined($disk->{'host'})) {
			$short = &hbt_to_device($disk->{'host'},
						$disk->{'bus'},
						$disk->{'target'});
			}
		else {
			$short = $disk->{'device'};
			$short =~ s/^.*\///g;
			}
		$disk->{'short'} = $short;

		$disk->{'id'} = $id_map{$disk->{'device'}} ||
				$id_map{"/dev/$short"};
		$disk->{'ids'} = $all_id_map{$disk->{'device'}} ||
				 $all_id_map{"/dev/$short"};

		push(@disks, $disk);
		}
	elsif (/Geometry:\s+(\d+)\s+heads,\s+(\d+)\s+sectors\/track,\s+(\d+)\s+cylinders/) {
		# Separate geometry line
		# Geometry: 255 heads, 63 sectors/track, 1305 cylinders
		$disk->{'heads'} = $1;
		$disk->{'sectors'} = $2;
		$disk->{'cylinders'} = $3;
		}
	elsif (/^Units\s*[=:]\s+cylinders\s+of\s+(\d+)\s+\*\s+(\d+)/) {
		# Unit size for disk from fdisk
		$disk->{'bytes'} = $2;
		$disk->{'cylsize'} = $disk->{'heads'} * $disk->{'sectors'} *
				     $disk->{'bytes'};
		$disk->{'size'} = $disk->{'cylinders'} * $disk->{'cylsize'};
		}
	elsif (/BIOS\s+cylinder,head,sector\s+geometry:\s+(\d+),(\d+),(\d+)\.\s+Each\s+cylinder\s+is\s+(\d+)(b|kb|mb)/i) {
		# Unit size for disk from parted
		$disk->{'cylinders'} = $1;
		$disk->{'heads'} = $2;
		$disk->{'sectors'} = $3;
		$disk->{'cylsize'} = $4 * (lc($5) eq "b" ? 1 :
					   lc($5) eq "kb" ? 1024 : 1024*1024);
		$disk->{'bytes'} = $disk->{'cylsize'} / $disk->{'heads'} /
						        $disk->{'sectors'};
		$disk->{'size'} = $disk->{'cylinders'} * $disk->{'cylsize'};
		}
	elsif (/(\/dev\/\S+?(\d+))[ \t*]+(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S{1,2})\s+(.*)/) {
		# Partition within the current disk from fdisk (msdos format)
		# /dev/sda1 * 1 1306 1306 10G 83 Linux
		local $part = { 'number' => $2,
				'device' => $1,
				'type' => $7,
				'start' => $3,
				'end' => $4,
				'extended' => $7 eq '5' || $6 eq 'f' ? 1 : 0,
				'index' => scalar(@{$disk->{'parts'}}),
			 	'edittype' => 1, };
		$part->{'desc'} = &partition_description($part->{'device'});
		$part->{'size'} = ($part->{'end'} - $part->{'start'} + 1) *
				  $disk->{'cylsize'};
		push(@{$disk->{'parts'}}, $part);
		}
	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+(.*)/) {
		# Partition within the current disk from fdisk (msdos format)
		# /dev/sda1 * 1 9327 74919096 83 Linux
		local $part = { 'number' => $2,
				'device' => $1,
				'type' => $6,
				'start' => $3,
				'end' => $4,
				'blocks' => int($5),
				'extended' => $6 eq '5' || $6 eq 'f' ? 1 : 0,
				'index' => scalar(@{$disk->{'parts'}}),
			 	'edittype' => 1, };
		$part->{'desc'} = &partition_description($part->{'device'});
		$part->{'size'} = ($part->{'end'} - $part->{'start'} + 1) *
				  $disk->{'cylsize'};
		push(@{$disk->{'parts'}}, $part);
		}
	elsif (/(?<dev>\/dev\/\S+?(?<num>\d+))\s+(?<start>\d+)\s+(?<end>\d+)\s+(?<blocks>\d+)\s+(?<size>[0-9\.]+[kMGTP])\s+(?<type>\S.*)/ ||
	       /(?<dev>\/dev\/\S+?(?<num>\d+))\s+(?<start>\d+)\s+(?<end>\d+)\s+(?<size>[0-9\.]+[kMGTP])\s+(?<type>\S.*)/) {
		# Partition within the current disk from fdisk (gpt format)
		local $part = {
				'number' => "$+{num}",
				'device' => "$+{dev}",
				'type' => "$+{type}",
				'start' => "$+{start}",
				'end' => "$+{end}",
				'blocks' => "$+{blocks}",
				'index' => scalar(@{$disk->{'parts'}}),
				'dtable' => $disk->{'table'},
			 	'edittype' => 2, };
		$part->{'desc'} = &partition_description($part->{'device'});
		$part->{'size'} = ($part->{'end'} - $part->{'start'} + 1) *
				  $disk->{'cylsize'};
		push(@{$disk->{'parts'}}, $part);
		}
	elsif (/^\s*(\d+)\s+(\d+)cyl\s+(\d+)cyl\s+(\d+)cyl\s+(primary|logical|extended)\s*(\S*)\s*(\S*)/) {
		# Partition within the current disk from parted (msdos format)
		local $part = { 'number' => $1,
				'device' => $disk->{'prefix'}.$1,
				'type' => $6 || 'ext2',
				'start' => $2+1,
				'end' => $3+1,
				'blocks' => $4 * $disk->{'cylsize'},
				'extended' => $5 eq 'extended' ? 1 : 0,
				'raid' => $7 eq 'raid' ? 1 : 0,
				'index' => scalar(@{$disk->{'parts'}}),
				'edittype' => 0, };
		$part->{'type'} = 'ext2' if ($part->{'type'} =~ /^ext/);
		$part->{'type'} = 'raid' if ($part->{'type'} eq 'ext2' &&
					     $part->{'raid'});
		$part->{'desc'} = &partition_description($part->{'device'});
		$part->{'size'} = ($part->{'end'} - $part->{'start'} + 1) *
				  $disk->{'cylsize'};
		push(@{$disk->{'parts'}}, $part);
		}
	elsif (/^\s*(\d+)\s+(\d+)cyl\s+(\d+)cyl\s+(\d+)cyl\s(.*)/) {
		# Partition within the current disk from parted (gpt format)
		local $part = { 'number' => $1,
				'device' => $disk->{'prefix'}.$1,
				'start' => $2+1,
				'end' => $3+1,
				'blocks' => $4 * $disk->{'cylsize'},
				'extended' => 0,
				'index' => scalar(@{$disk->{'parts'}}),
				'edittype' => 0, };

		# Work out partition type, name and flags
		local $rest = $5;
		$rest =~ s/^\s+//;
		$rest =~ s/,//g;	# Remove commas in flags list
		local @rest = split(/\s+/, $rest);

		# If first word is a known partition type, assume it is the type
		if (@rest && &conv_type($rest[0])) {
			$part->{'type'} = shift(@rest);
			}

		# Remove flag words from the end
		local %flags;
		while(@rest && $rest[$#rest] =~ /boot|lba|root|swap|hidden|raid|LVM/i) {
			$flags{lc(pop(@rest))} = 1;
			}

		# Anything left in the middle should be the name
		if (@rest) {
			$part->{'name'} = $rest[0];
			}
		if ($flags{'raid'}) {
			# RAID flag is set
			$part->{'raid'} = 1;
			}
		$part->{'type'} = 'ext2' if (!$part->{'type'} ||
					     $part->{'type'} =~ /^ext/);
		$part->{'type'} = 'raid' if ($part->{'type'} =~ /^ext/ &&
					     $part->{'raid'});
		$part->{'desc'} = &partition_description($part->{'device'});
		$part->{'size'} = ($part->{'end'} - $part->{'start'} + 1) *
				  $disk->{'cylsize'};
		push(@{$disk->{'parts'}}, $part);
		}
	elsif (/Partition\s+Table:\s+(\S+)/) {
		# Parted partition table type (from parted)
		$disk->{'table'} = $1;
		}
	elsif (/Disklabel\s+type:\s+(\S+)/) {
		# Parted partition table type (from fdisk)
		$disk->{'table'} = $1;
		}
	}
close(FDISK);

# Check /proc/ide for IDE disk models
foreach $d (@disks) {
	if ($d->{'type'} eq 'ide') {
		local $short = $d->{'short'};
		$d->{'model'} = &read_file_contents("/proc/ide/$short/model");
		$d->{'model'} =~ s/\r|\n//g;
		$d->{'media'} = &read_file_contents("/proc/ide/$short/media");
		$d->{'media'} =~ s/\r|\n//g;
		if ($d->{'short'} =~ /^vd/ && !$d->{'model'}) {
			# Fake up model for KVM VirtIO disks
			$d->{'model'} = "KVM VirtIO";
			}
		}
	}

# Fill in SCSI information
foreach $d (@disks) {
	if ($d->{'type'} eq 'scsi') {
		local $s = $d->{'scsi'};
		local $sysdir = "/sys/block/$d->{'short'}/device";
		if (-d $sysdir) {
			# From kernel 2.6.30+ sys directory
			$d->{'model'} = &read_file_contents("$sysdir/vendor").
					" ".
					&read_file_contents("$sysdir/model");
			$d->{'model'} =~ s/\r|\n//g;
			$d->{'media'} = &read_file_contents("$sysdir/media");
			$d->{'media'} =~ s/\r|\n//g;
			}
		elsif ($dscsi_mode) {
			# From other scsi files
			$d->{'model'} = "$dscsi[$s]->{'make'} $dscsi[$s]->{'model'}";
			$d->{'controller'} = $dscsi[$s]->{'host'};
			$d->{'scsiid'} = $dscsi[$s]->{'target'};
			}
		else {
			# From /proc/scsi/scsi lines
			if ($pscsi[$s] =~ /Vendor:\s+(\S+).*Model:\s+(.*)\s+Rev:/i) {
				$d->{'model'} = "$1 $2";
				}
			if ($pscsi[$s] =~ /Host:\s+scsi(\d+).*Id:\s+(\d+)/i) {
				$d->{'controller'} = int($1);
				$d->{'scsiid'} = int($2);
				}
			}
		if ($d->{'model'} =~ /ATA/) {
			# Fake SCSI disk, actually IDE
			$d->{'scsi'} = 0;
			$d->{'desc'} =~ s/SCSI/SATA/g;
			foreach my $p (@{$d->{'parts'}}) {
				$p->{'desc'} =~ s/SCSI/SATA/g;
				}
			}
		}
	}

@list_disks_partitions_cache = @disks;
return @disks;
}

# partition_description(device)
# Converts a device path like /dev/hda1 into a human-readable name
sub partition_description
{
my ($device) = @_;
return $device =~ /(s|h|xv|v)d([a-z]+)(\d+)$/ ?
	 &text('select_part', $1 eq 's' ? 'SCSI' :
			      $1 eq 'xv' ? 'Xen' :
			      $1 eq 'v' ? 'VirtIO' : 'IDE', uc($2), "$3") :
       $device =~ /mmcblk(\d+)p(\d+)$/ ?
	 &text('select_part', 'SD-Card', "$1", "$2") :
       $device =~ /scsi\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/part(\d+)/ ?
	 &text('select_spart', "$1", "$2", "$3", "$4", "$5") :
       $device =~ /ide\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/part(\d+)/ ?
	 &text('select_snewide', "$1", "$2", "$3", "$4", "$5") :
       $device =~ /rd\/c(\d+)d(\d+)p(\d+)$/ ? 
	 &text('select_mpart', "$1", "$2", "$3") :
       $device =~ /ida\/c(\d+)d(\d+)p(\d+)$/ ? 
	 &text('select_cpart', "$1", "$2", "$3") :
       $device =~ /cciss\/c(\d+)d(\d+)p(\d+)$/ ? 
	 &text('select_smartpart', "$1", "$2", "$3") :
       $device =~ /ataraid\/disc(\d+)\/part(\d+)$/ ?
	 &text('select_ppart', "$1", "$2") :
       $device =~ /nvme(\d+)n(\d+)p(\d+)$/ ?
	 &text('select_nvmepart', "$1", "$2", "$3") :
	 "???";
}

# hbt_to_device(host, bus, target)
# Converts an IDE device specified as a host, bus and target to an hdX device
sub hbt_to_device
{
local ($host, $bus, $target) = @_;
local $num = $host*4 + $bus*2 + $target;
return &number_to_device("hd", $num);
}

# number_to_device(suffix, number)
sub number_to_device
{
local ($suffix, $num) = @_;
if ($num < 26) {
	# Just a single letter
	return $suffix.(('a' .. 'z')[$num]);
	}
else {
	# Two-letter format
	local $first = int($num / 26);
	local $second = $num % 26;
	return $suffix.(('a' .. 'z')[$first]).(('a' .. 'z')[$second]);
	}
}

# change_type(disk, partition, type)
# Changes the type of an existing partition
sub change_type
{
my ($disk, $part, $type) = @_;
&open_fdisk($disk);
&wprint("t\n");
local $rv = &wait_for($fh, 'Partition.*:', 'Selected partition');
&wprint("$part\n") if ($rv == 0);
&wait_for($fh, 'Hex.*:');
&wprint("$type\n");
&wait_for($fh, 'Command.*:');
&wprint("w\n"); sleep(1);
&close_fdisk();
undef(@list_disks_partitions_cache);
}

# delete_partition(disk, partition)
# Delete an existing partition
sub delete_partition
{
my ($disk, $part) = @_;
if ($has_parted) {
	# Using parted
	my $cmd = "parted -s ".$disk." rm ".$part;
	my $out = &backquote_logged("$cmd </dev/null 2>&1");
	if ($?) {
		&error("$cmd failed : $out");
		}
	}
else {
	# Using fdisk
	&open_fdisk($disk);
	&wprint("d\n");
	local $rv = &wait_for($fh, 'Partition.*:', 'Selected partition');
	&wprint("$part\n") if ($rv == 0);
	&wait_for($fh, 'Command.*:');
	&wprint("w\n");
	&wait_for($fh, 'Syncing');
	sleep(3);
	&close_fdisk();
	}
undef(@list_disks_partitions_cache);
}

# create_partition(disk, partition, start, end, type)
# Create a new partition with the given extent and type
sub create_partition
{
my ($disk, $part, $start, $end, $type) = @_;
if ($has_parted) {
	# Using parted
	my $pe = $part > 4 ? "logical" : "primary";
	my $cmd;
	if ($type eq "raid") {
		$cmd = "parted -s ".$disk." unit cyl mkpart ".$pe." ".
		       "ext2 ".($start-1)." ".$end;
		$cmd .= " ; parted -s ".$disk." set $part raid on";
		}
	elsif ($type && $type ne 'ext2') {
		$cmd = "parted -s ".$disk." unit cyl mkpart ".$pe." ".
		       $type." ".($start-1)." ".$end;
		}
	else {
		$cmd = "parted -s ".$disk." unit cyl mkpart ".$pe." ".
		       ($start-1)." ".$end;
		}
	my $out = &backquote_logged("$cmd </dev/null 2>&1");
	if ($?) {
		&error("$cmd failed : $out");
		}
	}
else {
	# Using fdisk
	&open_fdisk($disk);
	&wprint("n\n");
	local $wf = &wait_for($fh, 'primary.*\r?\n', 'First.*:');
	if ($part > 4) {
		&wprint("l\n");
		}
	else {
		&wprint("p\n");
		local $wf2 = &wait_for($fh, 'Partition.*:',
					    'Selected partition');
		&wprint("$part\n") if ($wf2 == 0);
		}
	&wait_for($fh, 'First.*:') if ($wf != 1);
	&wprint("$start\n");
	$wf = &wait_for($fh, 'Last.*:', 'First.*:');
	$wf < 0 && &error("End of input waiting for first cylinder response");
	$wf == 1 && &error("First cylinder is invalid : $wait_for_input");
	&wprint("$end\n");
	$wf = &wait_for($fh, 'Command.*:', 'Last.*:');
	$wf < 0 && &error("End of input waiting for last cylinder response");
	$wf == 1 && &error("Last cylinder is invalid : $wait_for_input");

	&wprint("t\n");
	local $rv = &wait_for($fh, 'Partition.*:', 'Selected partition');
	&wprint("$part\n") if ($rv == 0);
	&wait_for($fh, 'Hex.*:');
	&wprint("$type\n");
	$wf = &wait_for($fh, 'Command.*:', 'Hex.*:');
	$wf < 0 && &error("End of input waiting for partition type response");
	$wf == 1 && &error("Partition type is invalid : $wait_for_input");
	&wprint("w\n");
	&wait_for($fh, 'Syncing'); sleep(3);
	&close_fdisk();
	}
undef(@list_disks_partitions_cache);
}

# create_extended(disk, partition, start, end)
# Create a new extended partition
sub create_extended
{
my ($disk, $part, $start, $end) = @_;
if ($has_parted) {
	# Create using parted
	my $cmd = "parted -s ".$disk." unit cyl mkpart extended ".
		  ($start-1)." ".$end;
	my $out = &backquote_logged("$cmd </dev/null 2>&1");
	if ($?) {
		&error("$cmd failed : $out");
		}
	}
else {
	# Use classic fdisk
	&open_fdisk($disk);
	&wprint("n\n");
	&wait_for($fh, 'primary.*\r?\n');
	&wprint("e\n");
	&wait_for($fh, 'Partition.*:');
	&wprint("$part\n");
	&wait_for($fh, 'First.*:');
	&wprint("$start\n");
	&wait_for($fh, 'Last.*:');
	&wprint("$end\n");
	&wait_for($fh, 'Command.*:');

	&wprint("w\n");
	&wait_for($fh, 'Syncing');
	sleep(3);
	&close_fdisk();
	}
undef(@list_disks_partitions_cache);
}

# list_tags()
# Returns a list of known partition tag numbers
sub list_tags
{
my ($fdisk_gpt) = @_;
if ($fdisk_gpt eq 'gpt') {
	# fdisk gpt types
	return sort { $a cmp $b } (keys %fdisk_gpt_tags);
	}
elsif ($has_parted) {
	# Parted types
	return sort { $a cmp $b } (keys %parted_tags);
	}
else {
	# Classic fdisk types
	return sort { hex($a) <=> hex($b) } (keys %tags);
	}
}

# tag_name(tag)
# Returns a human-readable version of a tag
sub tag_name
{
return $tags{$_[0]} || $parted_tags{$_[0]} || $fdisk_gpt_tags{$_[0]} || $hidden_tags{$_[0]};
}

sub default_tag
{
return $has_parted ? 'ext2' : '83';
}

# conv_type(tag)
# Given a partition tag, returns the filesystem type (assuming it is supported)
sub conv_type
{
my ($tag) = @_;
my @rv;
if ($has_parted) {
	# Use parted type names
	if ($tag eq "fat16") {
		@rv = ( "msdos" );
		}
	elsif ($tag eq "fat32") {
		@rv = ( "vfat" );
		}
	elsif ($tag =~ /^ext/ || $tag eq "raid") {
		@rv = ( "ext3", "ext4", "ext2", "xfs", "reiserfs", "btrfs" );
		}
	elsif ($tag eq "hfs" || $tag eq "HFS") {
		@rv = ( "hfs" );
		}
	elsif ($tag eq "linux-swap") {
		@rv = ( "swap" );
		}
	elsif ($tag eq "NTFS") {
		@rv = ( "ntfs" );
		}
	elsif ($tag eq "reiserfs") {
		@rv = "reiserfs";
		}
	elsif ($tag eq "ufs") {
		@rv = ( "ufs" );
		}
	else {
		return ( );
		}
	}
else {
	# Use fdisk type IDs
	if ($tag eq "4" || $tag eq "6" || $tag eq "1" || $tag eq "e") {
		@rv = ( "msdos" );
		}
	elsif ($tag eq "b" || $tag eq "c") {
		@rv = ( "vfat" );
		}
	elsif ($tag eq "83") {
		@rv = ( "ext3", "ext4", "ext2", "xfs", "reiserfs", "btrfs" );
		}
	elsif ($tag eq "82") {
		@rv = ( "swap" );
		}
	elsif ($tag eq "81") {
		@rv = ( "minix" );
		}
	else {
		return ( );
		}
	}
local %supp = map { $_, 1 } &mount::list_fstypes();
@rv = grep { $supp{$_} } @rv;
return wantarray ? @rv : $rv[0];
}

# fstype_name(type)
# Returns a readable name for a filesystem type
sub fstype_name
{
return $text{"fs_".$_[0]};
}

sub mkfs_options
{
if ($_[0] eq "msdos" || $_[0] eq "vfat") {
	&opt_input("msdos_ff", "", 1);
	print &ui_table_row($text{'msdos_F'},
	     &ui_select("msdos_F", undef,
			[ [ undef, $text{'default'} ],
			  [ 12 ], [ 16 ], [ 32 ],
			  [ "*", $text{'msdos_F_other'} ] ])." ".
	     &ui_textbox("msdos_F_other", undef, 4));
	&opt_input("msdos_i", "", 1);
	&opt_input("msdos_n", "", 0);
	&opt_input("msdos_r", "", 1);
	&opt_input("msdos_s", "sectors", 0);
	print &ui_table_row($text{'msdos_c'},
		&ui_yesno_radio("msdos_c", 0));
	}
elsif ($_[0] eq "minix") {
	&opt_input("minix_n", "", 1);
	&opt_input("minix_i", "", 0);
	&opt_input("minix_b", "", 1);
	print &ui_table_row($text{'minix_c'},
		&ui_yesno_radio("minix_c", 0));
	}
elsif ($_[0] eq "reiserfs") {
	print &ui_table_row($text{'reiserfs_force'},
		&ui_yesno_radio("reiserfs_f", 0));

	print &ui_table_row($text{'reiserfs_hash'},
		&ui_select("reiserfs_h", "",
			   [ [ "", $text{'default'} ],
			     [ "rupasov", "tea" ] ]));
	}
elsif ($_[0] =~ /^ext\d+$/) {
	&opt_input("ext2_b", $text{'bytes'}, 1);
	&opt_input("ext2_f", $text{'bytes'}, 0);
	&opt_input("ext2_i", "", 1);
	&opt_input("ext2_m", "%", 0);
	&opt_input("ext3_j", "MB", 1);
	print &ui_table_row($text{'ext2_c'},
		&ui_yesno_radio("ext2_c", 0));
	}
elsif ($_[0] eq "xfs") {
	print &ui_table_row($text{'xfs_force'},
		&ui_yesno_radio("xfs_f", 0));
	&opt_input("xfs_b", $text{'bytes'}, 0);
	}
elsif ($_[0] eq "jfs") {
	&opt_input("jfs_s", $text{'megabytes'}, 1);
	print &ui_table_row($text{'jfs_c'},
		&ui_yesno_radio("jfs_c", 0));
	}
elsif ($_[0] eq "fatx") {
	# Has no options!
	print &ui_table_row(undef, $text{'fatx_none'}, 4);
	}
elsif ($_[0] eq "btrfs") {
	&opt_input("btrfs_l", $text{'bytes'}, 0);
	&opt_input("btrfs_n", $text{'bytes'}, 0);
	&opt_input("btrfs_s", $text{'bytes'}, 0);
	}
}

# mkfs_parse(type, device)
# Returns a command to build a new filesystem of the given type on the
# given device. Options are taken from %in.
sub mkfs_parse
{
local($cmd);
if ($_[0] eq "msdos" || $_[0] eq "vfat") {
	$cmd = "mkfs -t $_[0]";
	$cmd .= &opt_check("msdos_ff", '[1-2]', "-f");
	if ($in{'msdos_F'} eq '*') {
		$in{'msdos_F_other'} =~ /^\d+$/ ||
			&error(&text('opt_error', $in{'msdos_F_other'},
						  $text{'msdos_F'}));
		$cmd .= " -F ".$in{'msdos_F_other'};
		}
	elsif ($in{'msdos_F'}) {
		$cmd .= " -F ".$in{'msdos_F'};
		}
	$cmd .= &opt_check("msdos_i", '[0-9a-f]{8}', "-i");
	$cmd .= &opt_check("msdos_n", '\S{1,11}', "-n");
	$cmd .= &opt_check("msdos_r", '\d+', "-r");
	$cmd .= &opt_check("msdos_s", '\d+', "-s");
	$cmd .= $in{'msdos_c'} ? " -c" : "";
	$cmd .= " $_[1]";
	}
elsif ($_[0] eq "minix") {
	local(@plist, $disk, $part, $i, @pinfo);
	$cmd = "mkfs -t minix";
	$cmd .= &opt_check("minix_n", '14|30', "-n ");
	$cmd .= &opt_check("minix_i", '\d+', "-i ");
	$cmd .= $in{'minix_c'} ? " -c" : "";
	$cmd .= &opt_check("minix_b", '\d+', " ");
	$cmd .= " $_[1]";
	}
elsif ($_[0] eq "reiserfs") {
	$cmd = "yes | mkreiserfs";
	$cmd .= " -f" if ($in{'reiserfs_f'});
	$cmd .= " -h $in{'reiserfs_h'}" if ($in{'reiserfs_h'});
	$cmd .= " $_[1]";
	}
elsif ($_[0] =~ /^ext\d+$/) {
	if (&has_command("mkfs.$_[0]")) {
		$cmd = "mkfs -t $_[0]";
		$cmd .= &opt_check("ext3_j", '\d+', "-j");
		}
	elsif ($_[0] eq "ext3" && &has_command("mke3fs")) {
		$cmd = "mke3fs";
		$cmd .= &opt_check("ext3_j", '\d+', "-j");
		}
	elsif ($_[0] eq "ext4" && &has_command("mke4fs")) {
		$cmd = "mke4fs";
		$cmd .= &opt_check("ext3_j", '\d+', "-j");
		}
	else {
		$cmd = "mkfs.ext2 -j";
		if (!$in{'ext3_j_def'}) {
			$in{'ext3_j'} =~ /^\d+$/ ||
				&error(&text('opt_error', $in{'ext3_j'},
					     $text{'ext3_j'}));
			$cmd .= " -J size=$in{'ext3_j'}";
			}
		}
	$cmd .= &opt_check("ext2_b", '\d+', "-b");
	$cmd .= &opt_check("ext2_f", '\d+', "-f");
	$cmd .= &opt_check("ext2_i", '\d{4,}', "-i");
	$cmd .= &opt_check("ext2_m", '\d+', "-m");
	$cmd .= $in{'ext2_c'} ? " -c" : "";
	$cmd .= " -q";
	$cmd .= " $_[1]";
	}
elsif ($_[0] eq "xfs") {
	$cmd = "mkfs -t $_[0]";
	$cmd .= " -f" if ($in{'xfs_f'});
	$cmd .= " -b size=$in{'xfs_b'}" if (!$in{'xfs_b_def'});
	$cmd .= " $_[1]";
	}
elsif ($_[0] eq "jfs") {
	$cmd = "mkfs -t $_[0] -q";
	$cmd .= &opt_check("jfs_s", '\d+', "-s");
	$cmd .= " -c" if ($in{'jfs_c'});
	$cmd .= " $_[1]";
	}
elsif ($_[0] eq "fatx") {
	$cmd = "mkfs -t $_[0] $_[1]";
	}
elsif ($_[0] eq "btrfs") {
	$cmd = "mkfs -t $_[0]";
	$cmd .= " -l $in{'btrfs_l'}" if (!$in{'btrfs_l_def'});
	$cmd .= " -n $in{'btrfs_n'}" if (!$in{'btrfs_n_def'});
	$cmd .= " -s $in{'btrfs_s'}" if (!$in{'btrfs_s_def'});
	$cmd .= " $_[1]";
	}
if (&has_command("partprobe")) {
	$cmd = "partprobe ; $cmd";
	}
return $cmd;
}

# can_tune(type)
# Returns 1 if this filesystem type can be tuned
sub can_tune
{
return $_[0] =~ /^ext\d+$/;
}

# tunefs_options(type)
# Output HTML for tuning options for some filesystem type
sub tunefs_options
{
if ($_[0] =~ /^ext\d+$/) {
	# Gaps between checks
	&opt_input("tunefs_c", "", 1);

	# Action on error
	print &ui_table_row($text{'tunefs_e'},
		&ui_radio("tunefs_e_def", 1,
			[ [ 1, $text{'opt_default'} ],
			  [ 0, &ui_select("tunefs_e", undef,
				[ [ "continue", $text{'tunefs_continue'} ],
				  [ "remount-ro", $text{'tunefs_remount'} ],
				  [ "panic", $text{'tunefs_panic'} ] ]) ] ]));

	# Reserved user
	print &ui_table_row($text{'tunefs_u'},
		&ui_opt_textbox("tunefs_u", undef, 13, $text{'opt_default'})." ".
		&user_chooser_button("tunefs_u", 0));

	# Reserved group
	print &ui_table_row($text{'tunefs_g'},
		&ui_opt_textbox("tunefs_g", undef, 13, $text{'opt_default'})." ".
		&group_chooser_button("tunefs_g", 0));

	# Reserved blocks
	&opt_input("tunefs_m", "%", 1);

	# Time between checks
	$tsel = &ui_select("tunefs_i_unit", undef,
			   [ [ "d", $text{'tunefs_days'} ],
			     [ "w", $text{'tunefs_weeks'} ],
			     [ "m", $text{'tunefs_months'} ] ]);
	&opt_input("tunefs_i", $tsel, 0);
	}
}

# tunefs_parse(type, device)
# Returns the tuning command based on user inputs
sub tunefs_parse
{
if ($_[0] =~ /^ext\d+$/) {
	$cmd = "tune2fs";
	$cmd .= &opt_check("tunefs_c", '\d+', "-c");
	$cmd .= $in{'tunefs_e_def'} ? "" : " -e$in{'tunefs_e'}";
	$cmd .= $in{'tunefs_u_def'} ? "" : " -u".getpwnam($in{'tunefs_u'});
	$cmd .= $in{'tunefs_g_def'} ? "" : " -g".getgrnam($in{'tunefs_g'});
	$cmd .= &opt_check("tunefs_m",'\d+',"-m");
	$cmd .= &opt_check("tunefs_i", '\d+', "-i").
		($in{'tunefs_i_def'} ? "" : $in{'tunefs_i_unit'});
	$cmd .= " $_[1]";
	}
return $cmd;
}

# need_reboot(disk)
# Returns 1 if a reboot is needed after changing the partitions on some disk
sub need_reboot
{
local $un = `uname -r`;
return $un =~ /^2\.0\./ || $un =~ /^1\./ || $un =~ /^0\./;
}

# device_status(device)
# Returns an array of  directory, type, mounted, module
sub device_status
{
@mounted = &foreign_call("mount", "list_mounted") if (!@mounted);
@mounts = &foreign_call("mount", "list_mounts") if (!@mounts);
local $label = &get_label($_[0]);
local $volid = &get_volid($_[0]);

local ($mounted) = grep { &same_file($_->[1], $_[0]) ||
			  $_->[1] eq "LABEL=$label" ||
			  $_->[1] eq "UUID=$volid" } @mounted;
local ($mount) = grep { &same_file($_->[1], $_[0]) ||
			$_->[1] eq "LABEL=$label" ||
			$_->[1] eq "UUID=$volid" } @mounts;
if ($mounted) { return ($mounted->[0], $mounted->[2], 1,
			&indexof($mount, @mounts),
			&indexof($mounted, @mounted)); }
elsif ($mount) { return ($mount->[0], $mount->[2], 0,
			 &indexof($mount, @mounts)); }
if ($raid_module) {
	my $raidconf = &foreign_call("raid", "get_raidtab") if (!$raidconf);
	foreach $c (@$raidconf) {
		foreach $d (&raid::find_value('device', $c->{'members'})) {
			return ( $c->{'value'}, "raid", 1, "raid" )
				if ($d eq $_[0]);
			}
		}
	}
if ($lvm_module) {
	if (!scalar(@physical_volumes)) {
		@physical_volumes = ();
		foreach $vg (&foreign_call("lvm", "list_volume_groups")) {
			push(@physical_volumes,
				&foreign_call("lvm", "list_physical_volumes",
						     $vg->{'name'}));
			}
		}
	foreach my $pv (@physical_volumes) {
		return ( $pv->{'vg'}, "lvm", 1, "lvm")
			if ($pv->{'device'} eq $_[0]);
		}
	}
if ($iscsi_server_module) {
	my $iscsiconf = &iscsi_server::get_iscsi_config();
	foreach my $c (@$iscsiconf) {
		if ($c->{'type'} eq 'extent' && $c->{'device'} eq $_[0]) {
			return ( $c->{'type'}.$c->{'num'}, "iscsi", 1,
				 "iscsi-server");
			}
		}
	}
if ($iscsi_target_module) {
	my $iscsiconf = &iscsi_target::get_iscsi_config();
	foreach my $t (&iscsi_target::find($iscsiconf, "Target")) {
		foreach my $l (&iscsi_target::find($t->{'members'}, "Lun")) {
			if ($l->{'value'} =~ /Path=([^, ]+)/ && $1 eq $_[0]) {
				return ( $t->{'value'}, "iscsi", 1,
					 "iscsi-target");
				}
			}
		}
	}
return ();
}

# device_status_link(directory, type, mounted, module)
# Converts the list returned by device_status to a link
sub device_status_link
{
my @stat = @_;
my $stat = "";
my $statdesc = $stat[0] =~ /^swap/ ? "<i>$text{'disk_vm'}</i>"
				   : "<tt>$stat[0]</tt>";
my $ret = $main::initial_module_name;
if ($ret !~ /fdisk$/) {
	$ret = $module_name;
	}
if ($stat[1] eq 'raid') {
	$stat = $statdesc;
	}
elsif ($stat[1] eq 'lvm') {
	if (&foreign_available("lvm")) {
		$stat = "<a href='../lvm/'>".
			"LVM VG $statdesc</a>";
		}
	else {
		$stat = "LVM VG $statdesc";
		}
	}
elsif ($stat[1] eq 'iscsi') {
	$stat = &text('disk_iscsi', $stat[0]);
	if (&foreign_available("iscsi-server")) {
		$stat = "<a href='../$stat[3]/'>$stat</a>";
		}
	}
elsif ($stat[0] && !&foreign_available("mount")) {
	$stat = $statdesc;
	}
elsif ($stat[0] && $stat[3] == -1) {
	$stat = "<a href='../mount/edit_mount.cgi?".
		"index=$stat[4]&temp=1&return=/$ret/'>".
		"$statdesc</a>";
	}
elsif ($stat[0]) {
	$stat = "<a href='../mount/edit_mount.cgi?".
		"index=$stat[3]&return=/$ret/'>".
		"$statdesc</a>";
	}
return $stat;
}

# can_fsck(type)
# Returns 1 if some filesystem type can fsck'd
sub can_fsck
{
return ($_[0] =~ /^ext\d+$/ && &has_command("fsck.$_[0]") ||
	$_[0] eq "minix" && &has_command("fsck.minix"));
}

# fsck_command(type, device)
# Returns the fsck command to unconditionally check a filesystem
sub fsck_command
{
if ($_[0] =~ /^ext\d+$/) {
	return "fsck -t $_[0] -p $_[1]";
	}
elsif ($_[0] eq "minix") {
	return "fsck -t minix -a $_[1]";
	}
}

# fsck_error(code)
# Returns a description of an exit code from fsck
sub fsck_error
{
return $text{"fsck_err$_[0]"} ? $text{"fsck_err$_[0]"}
			      : &text("fsck_unknown", $_[0]);
}

# partition_select(name, value, mode, [&found], [disk_regexp])
# Returns HTML for selecting a disk or partition
# mode 0 = floppies and disk partitions
#      1 = disks
#      2 = floppies and disks and disk partitions
#      3 = disk partitions
sub partition_select
{
local ($name, $value, $mode, $found, $diskre) = @_;
local $rv = "<select name=$_[0]>\n";
local @opts;
if (($mode == 0 || $mode == 2) &&
    (-r "/dev/fd0" || $value =~ /^\/dev\/fd[01]$/)) {
	push(@opts, [ '/dev/fd0', &text('select_fd', 0) ])
		if (!$diskre || '/dev/fd0' =~ /$diskre/);
	push(@opts, [ '/dev/fd1', &text('select_fd', 1) ])
		if (!$diskre || '/dev/fd1' =~ /$diskre/);
	${$found}++ if ($found && $value =~ /^\/dev\/fd[01]$/);
	}
local @dlist = &list_disks_partitions();
foreach my $d (@dlist) {
	local $dev = $d->{'device'};
	next if ($diskre && $dev !~ /$_[4]/);
	if ($mode == 1 || $mode == 2) {
		local $name = $d->{'desc'};
		$name .= " ($d->{'model'})" if ($d->{'model'});
		push(@opts, [ $dev, $name ]);
		${$found}++ if ($found && $dev eq $_[1]);
		}
	if ($mode == 0 || $mode == 2 || $mode == 3) {
		foreach $p (@{$d->{'parts'}}) {
			next if ($p->{'extended'});
			local $name = $p->{'desc'};
			$name .= " (".&tag_name($p->{'type'}).")"
				if (&tag_name($p->{'type'}));
			push(@opts, [ $p->{'device'}, $name ]);
			${$found}++ if ($found && $value eq $p->{'device'});
			}
		}
	}
return &ui_select($name, $value, \@opts, 1, 0, $value && !$found);
}

# label_select(name, value, &found)
# Returns HTML for selecting a filesystem label
sub label_select
{
local ($name, $value, $found) = @_;
local @opts;
local @dlist = &list_disks_partitions();
local $any;
foreach my $d (@dlist) {
	local $dev = $d->{'device'};
	foreach $p (@{$d->{'parts'}}) {
		next if ($p->{'type'} ne '83' &&
			 $p->{'type'} !~ /^ext/);
		local $label = &get_label($p->{'device'});
		next if (!$label);
		push(@opts, [ $label, $label." (".$p->{'desc'}.")" ]);
		${$found}++ if ($value eq $label && $found);
		$any++;
		}
	}
if (@opts) {
	return &ui_select($name, $value, \@opts, 1, 0, $value && !$found);
	}
else {
	return undef;
	}
}

# volid_select(name, value, &found)
# Returns HTML for selecting a filesystem UUID
sub volid_select
{
local ($name, $value, $found) = @_;
local @dlist = &list_disks_partitions();
local @opts;
foreach my $d (@dlist) {
	local $dev = $d->{'device'};
	foreach $p (@{$d->{'parts'}}) {
		next if ($p->{'type'} ne '83' && $p->{'type'} ne '82' &&
			 $p->{'type'} ne 'b' && $p->{'type'} ne 'c' &&
			 $p->{'type'} !~ /^(ext|xfs)/);
		local $volid = &get_volid($p->{'device'});
		next if (!$volid);
		push(@opts, [ $volid, "$volid ($p->{'desc'})" ]);
		${$found}++ if ($value eq $volid && $found);
		}
	}
if (@opts) {
	return &ui_select($name, $value, \@opts, 1, 0, $value && !$found);
	}
else {
	return undef;
	}
}

#############################################################################
# Internal functions
#############################################################################
sub open_fdisk
{
local $fpath = &check_fdisk();
my $cylarg;
if ($fpath =~ /\/fdisk/) {
	my $out = &backquote_command("$fpath -h 2>&1 </dev/null");
	if ($out =~ /-u\s+<size>/) {
		$cylarg = "-u=cylinders";
		}
	}
($fh, $fpid) = &foreign_call("proc", "pty_process_exec",
			     join(" ", $fpath, $cylarg, @_));
}

sub open_sfdisk
{
local $sfpath = &has_command("sfdisk");
($fh, $fpid) = &foreign_call("proc", "pty_process_exec", join(" ",$sfpath, @_));
}

sub check_fdisk
{
local $fpath = &has_command("fdisk");
&error(&text('open_error', "<tt>fdisk</tt>")) if (!$fpath);
return $fpath;
}

sub close_fdisk
{
close($fh); kill('TERM', $fpid);
}

sub wprint
{
syswrite($fh, $_[0], length($_[0]));
}

sub opt_input
{
print &ui_table_row($text{$_[0]},
	&ui_opt_textbox($_[0], undef, 6, $text{'opt_default'})." ".$_[1]);
}

sub opt_check
{
if ($in{"$_[0]_def"}) { return ""; }
elsif ($in{$_[0]} !~ /^$_[1]$/) {
	&error(&text('opt_error', $in{$_[0]}, $text{$_[0]}));
	}
else { return " $_[2] $in{$_[0]}"; }
}

%tags = ('0', 'Empty',
	 '1', 'FAT12',
	 '2', 'XENIX root',
	 '3', 'XENIX usr',
	 '4', 'FAT16 <32M',
	 '6', 'FAT16',
	 '7', 'NTFS',
	 '8', 'AIX',
	 '9', 'AIX bootable',
	 'a', 'OS/2 boot manager',
	 'b', 'Windows FAT32',
	 'c', 'Windows FAT32 LBA',
	 'e', 'Windows FAT16 LBA',
	'10', 'OPUS',
	'11', 'Hidden FAT12',
	'12', 'Compaq diagnostic',
	'14', 'Hidden FAT16 < 32M',
	'16', 'Hidden FAT16',
	'17', 'Hidden NTFS',
	'18', 'AST Windows swapfile',
	'1b', 'Hidden Windows FAT (1b)',
	'1c', 'Hidden Windows FAT (1c)',
	'1e', 'Hidden Windows FAT (1e)',
	'24', 'NEC DOS',
	'3c', 'PartitionMagic recovery',
	'40', 'Venix 80286',
	'41', 'PPC PReP boot',
	'42', 'SFS',
	'4d', 'QNX 4.x',
	'4e', 'QNX 4.x 2nd partition',
	'4f', 'QNX 4.x 3rd partition',
	'50', 'OnTrack DM',
	'51', 'OnTrack DM6 Aux1',
	'52', 'CP/M',
	'53', 'OnTrack DM6 Aux3',
	'54', 'OnTrack DM6',
	'55', 'EZ-Drive',
	'56', 'Golden Bow',
	'5c', 'Priam Edisk',
	'61', 'SpeedStor',
	'63', 'GNU HURD or SysV',
	'64', 'Novell Netware 286',
	'65', 'Novell Netware 386',
	'70', 'DiskSecure Multi-Boot',
	'75', 'PC/IX',
	'80', 'Old Minix',
	'81', 'Minix / Old Linux / Solaris',
	'82', 'Linux swap',
	'83', 'Linux',
	'84', 'OS/2 hidden C: drive',
	'85', 'Linux extended',
	'86', 'NTFS volume set (86)',
	'87', 'NTFS volume set (87)',
	'8e', 'Linux LVM',
	'93', 'Amoeba',
	'94', 'Amoeba BBT',
	'a0', 'IBM Thinkpad hibernation',
	'a5', 'BSD/386',
	'a6', 'OpenBSD',
	'a7', 'NeXTSTEP',
	'b7', 'BSDI filesystem',
	'b8', 'BSDI swap',
	'c1', 'DRDOS/sec FAT12',
	'c4', 'DRDOS/sec FAT16 <32M',
	'c6', 'DRDOS/sec FAT16',
	'c7', 'Syrinx',
	'db', 'CP/M / CTOS',
	'e1', 'DOS access',
	'e3', 'DOS read-only',
	'e4', 'SpeedStor',
	'eb', 'BeOS',
	'ee', 'GPT',
	'f1', 'SpeedStor',
	'f4', 'SpeedStor large partition',
	'f2', 'DOS secondary',
	'fd', 'Linux RAID',
	'fe', 'LANstep',
	'ff', 'BBT'
	);

%hidden_tags = (
	 '5', 'Extended',
	 'f', 'Windows extended LBA',
	);

%fdisk_gpt_tags = (
	'', 'None',
	'EFI System', 'EFI system partition',
	'BIOS boot', 'BIOS boot partition',
	'Linux filesystem', 'Linux filesystem',
	'Linux LVM', 'Linux LVM',
	);

%parted_tags = (
	'', 'None',
	'fat16', 'Windows FAT16',
	'fat32', 'Windows FAT32',
	'ext2', 'Linux EXT',
	'xfs', 'Linux XFS',
	'raid', 'Linux RAID',
	'HFS', 'MacOS HFS',
	'linux-swap', 'Linux Swap',
	'NTFS', 'Windows NTFS',
	'reiserfs', 'ReiserFS',
	'ufs', 'FreeBSD UFS',
	);
	
@space_type = ( '1', '4', '5', '6', 'b', 'c', 'e', '83' );

# can_edit_disk(device)
sub can_edit_disk
{
my ($device) = @_;
$device =~ s/\d+$//;
foreach (split(/\s+/, $access{'disks'})) {
        return 1 if ($_ eq "*" || $_ eq $device);
        }
return 0;
}

# disk_space(device, [mountpoint])
# Returns the amount of total and free space for some filesystem, or an
# empty array if not appropriate.
sub disk_space
{
local $w = $_[1] || $_[0];
local $out = `df -k '$w'`;
if ($out =~ /Mounted on\s*\n\s*\S+\s+(\S+)\s+\S+\s+(\S+)/i) {
	return ($1, $2);
	}
elsif ($out =~ /Mounted on\s*\n\S+\s*\n\s+(\S+)\s+\S+\s+(\S+)/i) {
	return ($1, $2);
	}
else {
	return ( );
	}
}

# supported_filesystems()
# Returns a list of filesystem types that can have mkfs_options called on them
sub supported_filesystems
{
local @fstypes = ( "ext2" );
push(@fstypes, "ext3") if (&has_command("mkfs.ext3") ||
			   &has_command("mke3fs") ||
			   `mkfs.ext2 -h 2>&1` =~ /\[-j\]/);
push(@fstypes, "ext4") if (&has_command("mkfs.ext4") ||
			   &has_command("mke4fs"));
push(@fstypes, "reiserfs") if (&has_command("mkreiserfs"));
push(@fstypes, "xfs") if (&has_command("mkfs.xfs"));
push(@fstypes, "jfs") if (&has_command("mkfs.jfs"));
push(@fstypes, "fatx") if (&has_command("mkfs.fatx"));
push(@fstypes, "btrfs") if (&has_command("mkfs.btrfs"));
push(@fstypes, "msdos");
push(@fstypes, "vfat");
push(@fstypes, "minix");
return @fstypes;
}

# get_label(device, [type])
# Returns the XFS or EXT label for some device's filesystem
sub get_label
{
local $label;
if ($has_e2label) {
	$label = `e2label $_[0] 2>&1`;
	chop($label);
	}
if (($? || $label !~ /\S/) && $has_xfs_db) {
	$label = undef;
	local $out = &backquote_with_timeout("xfs_db -x -p xfs_admin -c label -r $_[0] 2>&1", 5);
	$label = $1 if ($out =~ /label\s*=\s*"(.*)"/ &&
			$1 ne '(null)');
	}
if (($? || $label !~ /\S/) && $has_reiserfstune) {
	$label = undef;
	local $out = &backquote_command("reiserfstune $_[0]");
	if ($out =~ /LABEL:\s*(\S+)/) {
		$label = $1;
		}
	}
return $? || $label !~ /\S/ ? undef : $label;
}

# get_volid(device)
# Returns the UUID for some device's filesystem
sub get_volid
{
local ($device) = @_;
local $uuid;
if (-d $uuid_directory) {
	# Use UUID mapping directory
	opendir(DIR, $uuid_directory);
	foreach my $f (readdir(DIR)) {
		local $linkdest = &simplify_path(
			&resolve_links("$uuid_directory/$f"));
		if ($linkdest eq $device) {
			$uuid = $f;
			last;
			}
		}
	closedir(DIR);
	}
elsif ($has_volid) {
	# Use vol_id command
	local $out = &backquote_command(
			"vol_id ".quotemeta($device)." 2>&1", 1);
	if ($out =~ /ID_FS_UUID=(\S+)/) {
		$uuid = $1;
		}
	}
return $uuid;
}

# set_label(device, label, [type])
# Tries to set the label for some device's filesystem
sub set_label
{
if ($has_e2label && ($_[2] =~ /^ext[23]$/ || !$_[2])) {
	&system_logged("e2label '$_[0]' '$_[1]' >/dev/null 2>&1");
	return 1 if (!$?);
	}
if ($has_xfs_db && ($_[2] eq "xfs" || !$_[2])) {
	&system_logged("xfs_db -x -p xfs_admin -c \"label $_[1]\" $_[0] >/dev/null 2>&1");
	return 1 if (!$?);
	}
return 0;
}

# set_name(&disk, &partition, name)
# Sets the name of a partition, for partition types that support it
sub set_name
{
my ($dinfo, $pinfo, $name) = @_;
my $cmd = "parted -s ".$dinfo->{'device'}." name ".$pinfo->{'number'}." ";
if ($name) {
	$cmd .= quotemeta($name);
	}
else {
	$cmd .= " '\"\"'";
	}
my $out = &backquote_logged("$cmd </dev/null 2>&1");
if ($?) {
	&error("$cmd failed : $out");
	}
}

# set_partition_table(device, table-type)
# Wipe and re-create the partition table on some disk
sub set_partition_table
{
my ($disk, $table) = @_;
my $cmd = "parted -s ".$disk." mktable ".$table;
my $out = &backquote_logged("$cmd </dev/null 2>&1");
if ($?) {
	&error("$cmd failed : $out");
	}
}

# supports_label(&partition)
# Returns 1 if the label can be set on a partition
sub supports_label
{
my ($part) = @_;
return $part->{'type'} eq '83' || $part->{'type'} eq 'ext2';
}

# supports_name(&disk)
# Returns 1 if the name can be set on a disk's partitions
sub supports_name
{
my ($disk) = @_;
return $disk->{'table'} eq 'gpt';
}

# supports_hdparm(&disk)
sub supports_hdparm
{
local ($d) = @_;
return $d->{'type'} eq 'ide' || $d->{'type'} eq 'scsi' && $d->{'model'} =~ /ATA/;
}

# supports_relabel(&disk)
# Return 1 if a disk can have it's partition table re-written
sub supports_relabel
{
return $has_parted ? 1 : 0;
}

# supports_smart(&disk)
sub supports_smart
{
return &foreign_installed("smart-status") &&
       &foreign_available("smart-status");
}

# supports_extended(&disk)
# Return 1 if some disk can support extended partitions
sub supports_extended
{
my ($disk) = @_;
return $disk->{'label'} eq 'msdos' ? 1 : 0;
}

# list_table_types(&disk)
# Returns the list of supported partition table types for a disk
sub list_table_types
{
if ($has_parted) {
	return ( 'msdos', 'gpt', 'bsd', 'dvh', 'loop', 'mac', 'pc98', 'sun' );
	}
else {
	return ( 'msdos' );
	}
}

# get_parted_version()
# Returns the version number of parted that is installed
sub get_parted_version
{
my $out = &backquote_command("parted -v 2>&1 </dev/null");
return $out =~ /parted.*\s([0-9\.]+)/i ? $1 : undef;
}

# identify_disk(&disk)
# Blinks the activity LED of the drive sixty times
sub identify_disk
{
local ($d) = @_;
$count = 1;
while ($count <= 60) {
        &system_logged("dd if=".quotemeta($d->{'device'}).
		       " of=/dev/null bs=10M count=1");
	sleep(1);
	print "$count ";
	$count ++;
	}
}

1;

Filemanager

Name Type Size Permission Actions
help Folder 0755
images Folder 0755
lang Folder 0755
CHANGELOG File 1.92 KB 0644
acl_security.pl File 958 B 0755
apply_hdparm.cgi File 1.17 KB 0755
blink.cgi File 384 B 0755
cgi_args.pl File 483 B 0755
config File 13 B 0644
config.info File 144 B 0644
config.info.bg File 235 B 0644
config.info.ca File 167 B 0644
config.info.de File 148 B 0644
config.info.fr File 167 B 0644
config.info.nl File 138 B 0644
config.info.no File 151 B 0644
defaultacl File 26 B 0644
edit_disk.cgi File 4.91 KB 0755
edit_hdparm.cgi File 4.36 KB 0755
edit_part.cgi File 8.26 KB 0755
edit_relabel.cgi File 1.32 KB 0755
fdisk-lib.pl File 48.16 KB 0755
feedback_files.pl File 115 B 0755
fsck.cgi File 604 B 0755
fsck_form.cgi File 700 B 0755
index.cgi File 2.41 KB 0755
log_parser.pl File 1.53 KB 0755
make_tags.pl File 210 B 0755
mkfs.cgi File 820 B 0755
mkfs_form.cgi File 832 B 0755
module.info File 238 B 0644
module.info.af File 0 B 0644
module.info.af.auto File 153 B 0644
module.info.ar File 0 B 0644
module.info.ar.auto File 209 B 0644
module.info.be File 0 B 0644
module.info.be.auto File 249 B 0644
module.info.bg File 0 B 0644
module.info.bg.auto File 236 B 0644
module.info.ca File 119 B 0644
module.info.ca.auto File 29 B 0644
module.info.cs File 40 B 0644
module.info.cs.auto File 130 B 0644
module.info.da File 0 B 0644
module.info.da.auto File 149 B 0644
module.info.de File 131 B 0644
module.info.de.auto File 26 B 0644
module.info.el File 0 B 0644
module.info.el.auto File 299 B 0644
module.info.es File 38 B 0644
module.info.es.auto File 112 B 0644
module.info.eu File 0 B 0644
module.info.eu.auto File 146 B 0644
module.info.fa File 0 B 0644
module.info.fa.auto File 233 B 0644
module.info.fi File 0 B 0644
module.info.fi.auto File 149 B 0644
module.info.fr File 35 B 0644
module.info.fr.auto File 133 B 0644
module.info.he File 0 B 0644
module.info.he.auto File 188 B 0644
module.info.hr File 0 B 0644
module.info.hr.auto File 160 B 0644
module.info.hu File 79 B 0644
module.info.hu.auto File 26 B 0644
module.info.it File 0 B 0644
module.info.it.auto File 152 B 0644
module.info.ja File 58 B 0644
module.info.ja.auto File 179 B 0644
module.info.ko File 36 B 0644
module.info.ko.auto File 137 B 0644
module.info.lt File 0 B 0644
module.info.lt.auto File 163 B 0644
module.info.lv File 0 B 0644
module.info.lv.auto File 167 B 0644
module.info.ms File 120 B 0644
module.info.ms.auto File 23 B 0644
module.info.mt File 0 B 0644
module.info.mt.auto File 153 B 0644
module.info.nl File 39 B 0644
module.info.nl.auto File 111 B 0644
module.info.no File 38 B 0644
module.info.no.auto File 112 B 0644
module.info.pl File 38 B 0644
module.info.pl.auto File 120 B 0644
module.info.pt File 37 B 0644
module.info.pt.auto File 116 B 0644
module.info.pt_BR File 0 B 0644
module.info.pt_BR.auto File 162 B 0644
module.info.ro File 0 B 0644
module.info.ro.auto File 157 B 0644
module.info.ru File 60 B 0644
module.info.ru.auto File 185 B 0644
module.info.sk File 0 B 0644
module.info.sk.auto File 166 B 0644
module.info.sl File 0 B 0644
module.info.sl.auto File 155 B 0644
module.info.sv File 28 B 0644
module.info.sv.auto File 116 B 0644
module.info.th File 0 B 0644
module.info.th.auto File 303 B 0644
module.info.tr File 34 B 0644
module.info.tr.auto File 123 B 0644
module.info.uk File 0 B 0644
module.info.uk.auto File 245 B 0644
module.info.ur File 0 B 0644
module.info.ur.auto File 266 B 0644
module.info.vi File 0 B 0644
module.info.vi.auto File 187 B 0644
module.info.zh File 27 B 0644
module.info.zh.auto File 104 B 0644
module.info.zh_TW File 33 B 0644
module.info.zh_TW.auto File 110 B 0644
range.js File 4.7 KB 0644
reboot.cgi File 261 B 0755
relabel.cgi File 417 B 0755
save_part.cgi File 3.75 KB 0755
slider.js File 14.48 KB 0644
timer.js File 2.86 KB 0644
tunefs.cgi File 689 B 0755
tunefs_form.cgi File 722 B 0755
winclassic.css File 1.72 KB 0644