mirror of
https://github.com/webmin/webmin.git
synced 2026-02-03 14:13:29 +00:00
Add files via upload
gpart edition
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -19,8 +19,10 @@ $slice || &error($text{'slice_egone'});
|
||||
|
||||
# Validate inputs, starting with slice number
|
||||
my $part = { };
|
||||
$in{'letter'} =~ /^[a-d]$/i || &error($text{'npart_eletter'});
|
||||
$in{'letter'} =~ /^[a-h]$/i || &error($text{'npart_eletter'});
|
||||
$in{'letter'} = lc($in{'letter'});
|
||||
# Partition 'c' is reserved in BSD disklabels (represents the whole slice)
|
||||
$in{'letter'} ne 'c' || &error($text{'npart_ereserved'});
|
||||
my ($clash) = grep { $_->{'letter'} eq $in{'letter'} } @{$slice->{'parts'}};
|
||||
$clash && &error(&text('npart_eclash', $in{'letter'}));
|
||||
$part->{'letter'} = $in{'letter'};
|
||||
@@ -30,7 +32,7 @@ $in{'start'} =~ /^\d+$/ || &error($text{'nslice_estart'});
|
||||
$in{'end'} =~ /^\d+$/ || &error($text{'nslice_eend'});
|
||||
$in{'start'} < $in{'end'} || &error($text{'npart_erange'});
|
||||
$part->{'startblock'} = $in{'start'};
|
||||
$part->{'blocks'} = $in{'end'} - $in{'start'};
|
||||
$part->{'blocks'} = $in{'end'} - $in{'start'} + 1;
|
||||
|
||||
# Slice type
|
||||
$part->{'type'} = $in{'type'};
|
||||
@@ -39,7 +41,8 @@ $part->{'type'} = $in{'type'};
|
||||
&ui_print_header($slice->{'desc'}, $text{'npart_title'}, "");
|
||||
|
||||
print &text('npart_creating', $in{'letter'}, $slice->{'desc'}),"<p>\n";
|
||||
my $err = &save_partition($disk, $slice, $part);
|
||||
# Actually create the partition inside the slice (initialize BSD label if needed)
|
||||
my $err = &create_partition($disk, $slice, $part);
|
||||
if ($err) {
|
||||
print &text('npart_failed', $err),"<p>\n";
|
||||
}
|
||||
|
||||
@@ -1,65 +1,109 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Actually create a new slice
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
&ReadParse();
|
||||
&error_setup($text{'nslice_err'});
|
||||
|
||||
# Get the disk
|
||||
my @disks = &list_disks_partitions();
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
|
||||
ReadParse();
|
||||
error_setup($text{'nslice_err'});
|
||||
# Get the disk using first() for an early exit on match
|
||||
my @disks = list_disks_partitions();
|
||||
my $disk;
|
||||
foreach my $d (@disks) {
|
||||
if ($d->{'device'} eq $in{'device'}) {
|
||||
$disk = $d;
|
||||
last;
|
||||
}
|
||||
}
|
||||
# Validate device parameter to prevent path traversal and command injection
|
||||
$disk or error($text{'disk_egone'});
|
||||
# Prefer GPART total blocks for bounds
|
||||
(my $base_dev = $in{'device'}) =~ s{^/dev/}{};
|
||||
my $ds = get_disk_structure($base_dev);
|
||||
my $disk_blocks = ($ds && $ds->{'total_blocks'}) ? $ds->{'total_blocks'} : ($disk->{'blocks'} || 0);
|
||||
# Validate inputs, starting with slice number
|
||||
my $slice = { };
|
||||
$in{'number'} =~ /^\d+$/ || &error($text{'nslice_enumber'});
|
||||
my ($clash) = grep { $_->{'number'} == $in{'number'} } @{$disk->{'slices'}};
|
||||
$clash && &error(&text('nslice_eclash', $in{'number'}));
|
||||
my $slice = {};
|
||||
$in{'number'} =~ /^\d+$/ or error($text{'nslice_enumber'});
|
||||
# Check for clash using first() with a loop exiting on first match
|
||||
my $clash;
|
||||
foreach my $s (@{$disk->{'slices'}}) {
|
||||
if ($s->{'number'} == $in{'number'}) {
|
||||
$clash = $s;
|
||||
last;
|
||||
}
|
||||
}
|
||||
$slice->{'number'} = $in{'number'};
|
||||
|
||||
# Start and end blocks
|
||||
$in{'start'} =~ /^\d+$/ || &error($text{'nslice_estart'});
|
||||
$in{'end'} =~ /^\d+$/ || &error($text{'nslice_eend'});
|
||||
$in{'start'} < $in{'end'} || &error($text{'nslice_erange'});
|
||||
$in{'start'} =~ /^\d+$/ or error($text{'nslice_estart'});
|
||||
$in{'end'} =~ /^\d+$/ or error($text{'nslice_eend'});
|
||||
($in{'start'} < $in{'end'}) or error($text{'nslice_erange'});
|
||||
# total_blocks is the block *after* the last valid block, so end must be < total_blocks
|
||||
($in{'end'} < $disk_blocks) or error(text('nslice_emax', $disk_blocks - 1));
|
||||
|
||||
# Ensure the new slice does not overlap existing slices
|
||||
foreach my $s (@{ $disk->{'slices'} }) {
|
||||
my $s_start = $s->{'startblock'};
|
||||
my $s_end = $s->{'startblock'} + $s->{'blocks'} - 1;
|
||||
if (!($in{'end'} < $s_start || $in{'start'} > $s_end)) {
|
||||
error("Requested slice range overlaps with existing slice #".$s->{'number'});
|
||||
}
|
||||
}
|
||||
|
||||
$slice->{'startblock'} = $in{'start'};
|
||||
$slice->{'blocks'} = $in{'end'} - $in{'start'};
|
||||
|
||||
# Slice type
|
||||
$slice->{'blocks'} = $in{'end'} - $in{'start'} + 1;
|
||||
# Slice type
|
||||
$in{'type'} =~ /^[a-zA-Z0-9_-]+$/ or error($text{'nslice_etype'});
|
||||
length($in{'type'}) <= 20 or error($text{'nslice_etype'});
|
||||
$slice->{'type'} = $in{'type'};
|
||||
|
||||
# Do the creation
|
||||
&ui_print_header($disk->{'desc'}, $text{'nslice_title'}, "");
|
||||
|
||||
print &text('nslice_creating', $in{'number'}, $disk->{'desc'}),"<p>\n";
|
||||
my $err = &create_slice($disk, $slice);
|
||||
ui_print_header($disk->{'desc'}, $text{'nslice_title'}, "");
|
||||
print text('nslice_creating', $in{'number'}, $disk->{'desc'}), "<p>\n";
|
||||
my $err = create_slice($disk, $slice);
|
||||
if ($err) {
|
||||
print &text('nslice_failed', $err),"<p>\n";
|
||||
}
|
||||
else {
|
||||
print &text('nslice_done'),"<p>\n";
|
||||
}
|
||||
|
||||
print text('nslice_failed', $err), "<p>\n";
|
||||
} else {
|
||||
print text('nslice_done'), "<p>\n";
|
||||
# Auto-label the new partition provider with its name if scheme is GPT or BSD
|
||||
my $base = $disk->{'device'}; $base =~ s{^/dev/}{};
|
||||
my $ds = get_disk_structure($base);
|
||||
if ($ds && $ds->{'scheme'}) {
|
||||
# Determine provider and label text
|
||||
my $label_text = slice_name($slice); # e.g., da8s2 or da0p2
|
||||
if ($ds->{'scheme'} =~ /GPT/i) {
|
||||
my $idx = $slice->{'number'};
|
||||
if ($idx) {
|
||||
my $cmd2 = "gpart modify -i $idx -l " . quote_path($label_text) . " $base";
|
||||
my $out2 = `$cmd2 2>&1`;
|
||||
# If it fails, ignore silently
|
||||
}
|
||||
} else {
|
||||
# On MBR, if BSD label exists we can set label once created; ignore for now
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$err && $in{'makepart'}) {
|
||||
# Also create a partition
|
||||
print &text('nslice_parting', $in{'number'}, $disk->{'desc'}),"<p>\n";
|
||||
my $err = &initialize_slice($disk, $slice);
|
||||
if ($err) {
|
||||
print &text('nslice_pfailed', $err),"<p>\n";
|
||||
}
|
||||
else {
|
||||
print &text('nslice_pdone'),"<p>\n";
|
||||
}
|
||||
}
|
||||
|
||||
# Also create a partition (initialize slice label)
|
||||
my $part_err = initialize_slice($disk, $slice);
|
||||
if ($part_err) {
|
||||
print text('nslice_pfailed', $part_err), "<p>\n";
|
||||
} else {
|
||||
print text('nslice_pdone'), "<p>\n";
|
||||
}
|
||||
}
|
||||
if (!$err) {
|
||||
&webmin_log("create", "slice", $slice->{'device'}, $slice);
|
||||
}
|
||||
|
||||
&ui_print_footer("edit_disk.cgi?device=$in{'device'}",
|
||||
$text{'disk_return'});
|
||||
|
||||
# Auto-label GPT partitions with their device name (e.g., da8p2)
|
||||
my $base = $disk->{'device'}; $base =~ s{^/dev/}{};
|
||||
my $ds = get_disk_structure($base);
|
||||
if ($ds && $ds->{'scheme'} && $ds->{'scheme'} =~ /GPT/i) {
|
||||
my $slice_devname = $slice->{'device'};
|
||||
$slice_devname =~ s{^/dev/}{}; # e.g., da8p2
|
||||
my $idx = $slice->{'number'};
|
||||
if ($idx && $slice_devname) {
|
||||
my $label_cmd = "gpart modify -i $idx -l " . quote_path($slice_devname) . " $base 2>&1";
|
||||
my $label_out = `$label_cmd`;
|
||||
# Ignore errors - labeling is optional
|
||||
}
|
||||
}
|
||||
webmin_log("create", "slice", $slice->{'device'}, $slice);
|
||||
}
|
||||
ui_print_footer("edit_disk.cgi?device=$in{'device'}", $text{'disk_return'});
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Show details of a disk, and slices on it
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
@@ -8,93 +7,334 @@ no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
&ReadParse();
|
||||
my $extwidth = 300;
|
||||
|
||||
my $extwidth = 100;
|
||||
# Get the disk
|
||||
my @disks = &list_disks_partitions();
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
|
||||
# Cache commonly used values
|
||||
my $device = $disk->{'device'};
|
||||
my $device_url = &urlize($device);
|
||||
my $desc = $disk->{'desc'};
|
||||
# Prefer total blocks from gpart header when available
|
||||
my $base_device = $disk->{'device'}; $base_device =~ s{^/dev/}{};
|
||||
my $disk_structure = &get_disk_structure($base_device);
|
||||
my $disk_blocks = ($disk_structure && $disk_structure->{'total_blocks'}) ? $disk_structure->{'total_blocks'} : ($disk->{'blocks'} || 1000000);
|
||||
# Precompute a scale factor for extent image widths
|
||||
my $scale = $extwidth / ($disk_blocks || 1);
|
||||
&ui_print_header($disk->{'desc'}, $text{'disk_title'}, "");
|
||||
|
||||
# Show disk details
|
||||
my @info = ( );
|
||||
push(@info, &text('disk_dsize', &nice_size($disk->{'size'})));
|
||||
# Debug toggle bar
|
||||
print "<div class='debug-toggle' style='margin-bottom: 15px; text-align: right;'>";
|
||||
if ($in{'debug'}) {
|
||||
print "<a href='edit_disk.cgi?device=$device_url' class='btn btn-default'><i class='fa fa-bug'></i> $text{'disk_hide_debug'}</a>";
|
||||
} else {
|
||||
print "<a href='edit_disk.cgi?device=$device_url&debug=1' class='btn btn-default'><i class='fa fa-bug'></i> $text{'disk_show_debug'}</a>";
|
||||
}
|
||||
print "</div>";
|
||||
# Get detailed disk information from geom and disk structure from gpart show (cache disk_structure entries)
|
||||
my $geom_info = &get_detailed_disk_info($device);
|
||||
my $entries = $disk_structure && $disk_structure->{'entries'} ? $disk_structure->{'entries'} : [];
|
||||
print &ui_table_start($text{'disk_details'}, "width=100%", 2);
|
||||
# Prefer mediasize (bytes) for accurate size; fallback to stat-based size
|
||||
my $disk_bytes = ($disk_structure && $disk_structure->{'mediasize'}) ? $disk_structure->{'mediasize'} : $disk->{'size'};
|
||||
print &ui_table_row($text{'disk_dsize'}, &safe_nice_size($disk_bytes));
|
||||
if ($disk->{'model'}) {
|
||||
push(@info, &text('disk_model', $disk->{'model'}));
|
||||
print &ui_table_row($text{'disk_model'}, $disk->{'model'});
|
||||
}
|
||||
print &ui_table_row($text{'disk_device'}, "<tt>$disk->{'device'}</tt>");
|
||||
# Get disk scheme
|
||||
print &ui_table_row($text{'disk_scheme'}, $disk_structure ? $disk_structure->{'scheme'} : $text{'disk_unknown'});
|
||||
# GEOM details
|
||||
if ($geom_info) {
|
||||
print &ui_table_hr();
|
||||
print &ui_table_row($text{'disk_geom_header'}, "<b>$text{'disk_geom_details'}</b>", 2);
|
||||
if ($geom_info->{'mediasize'}) {
|
||||
print &ui_table_row($text{'disk_mediasize'}, $geom_info->{'mediasize'});
|
||||
}
|
||||
if ($geom_info->{'sectorsize'}) {
|
||||
print &ui_table_row($text{'disk_sectorsize'}, $geom_info->{'sectorsize'} . " " . $text{'disk_bytes'});
|
||||
}
|
||||
if ($geom_info->{'stripesize'}) {
|
||||
print &ui_table_row($text{'disk_stripesize'}, $geom_info->{'stripesize'} . " " . $text{'disk_bytes'});
|
||||
}
|
||||
if ($geom_info->{'stripeoffset'}) {
|
||||
print &ui_table_row($text{'disk_stripeoffset'}, $geom_info->{'stripeoffset'} . " " . $text{'disk_bytes'});
|
||||
}
|
||||
if ($geom_info->{'mode'}) {
|
||||
print &ui_table_row($text{'disk_mode'}, $geom_info->{'mode'});
|
||||
}
|
||||
if ($geom_info->{'rotationrate'}) {
|
||||
if ($geom_info->{'rotationrate'} eq "0") {
|
||||
print &ui_table_row($text{'disk_rotationrate'}, $text{'disk_ssd'});
|
||||
} else {
|
||||
print &ui_table_row($text{'disk_rotationrate'}, $geom_info->{'rotationrate'} . " " . $text{'disk_rpm'});
|
||||
}
|
||||
push(@info, &text('disk_cylinders', $disk->{'cylinders'}));
|
||||
push(@info, &text('disk_blocks', $disk->{'blocks'}));
|
||||
push(@info, &text('disk_device', "<tt>$disk->{'device'}</tt>"));
|
||||
print &ui_links_row(\@info),"<p>\n";
|
||||
}
|
||||
if ($geom_info->{'ident'}) {
|
||||
print &ui_table_row($text{'disk_ident'}, $geom_info->{'ident'});
|
||||
}
|
||||
if ($geom_info->{'lunid'}) {
|
||||
print &ui_table_row($text{'disk_lunid'}, $geom_info->{'lunid'});
|
||||
}
|
||||
if ($geom_info->{'descr'}) {
|
||||
print &ui_table_row($text{'disk_descr'}, $geom_info->{'descr'});
|
||||
}
|
||||
}
|
||||
# Advanced information (cylinders, blocks)
|
||||
print &ui_table_hr();
|
||||
print &ui_table_row($text{'disk_advanced_header'}, "<b>$text{'disk_advanced_details'}</b>", 2);
|
||||
if ($disk->{'cylinders'}) {
|
||||
print &ui_table_row($text{'disk_cylinders'}, $disk->{'cylinders'});
|
||||
}
|
||||
print &ui_table_row($text{'disk_blocks'}, $disk->{'blocks'});
|
||||
print &ui_table_end();
|
||||
# Debug: print raw outputs if debug mode is enabled
|
||||
if ($in{'debug'}) {
|
||||
print "<div class='debug-section'>";
|
||||
# Debug: gpart show output
|
||||
my $cmd = "gpart show -l $base_device 2>&1";
|
||||
my $out = &backquote_command($cmd);
|
||||
print "<div class='panel panel-default'>";
|
||||
print "<div class='panel-heading'><h3 class='panel-title'>$text{'disk_debug_gpart'}</h3></div>";
|
||||
print "<div class='panel-body'>";
|
||||
print "<pre>Command: $cmd\nOutput:\n$out\n</pre>";
|
||||
print "</div></div>";
|
||||
|
||||
# Debug: disk structure
|
||||
print "<div class='panel panel-default'>";
|
||||
print "<div class='panel-heading'><h3 class='panel-title'>$text{'disk_debug_structure'}</h3></div>";
|
||||
print "<div class='panel-body'>";
|
||||
print "<pre>Disk Structure:\n";
|
||||
foreach my $key (sort keys %$disk_structure) {
|
||||
if ($key eq 'entries') {
|
||||
print "entries: [\n";
|
||||
foreach my $entry (@{$disk_structure->{'entries'}}) {
|
||||
print " {\n";
|
||||
foreach my $k (sort keys %$entry) {
|
||||
print " $k: $entry->{$k}\n";
|
||||
}
|
||||
print " },\n";
|
||||
}
|
||||
print "]\n";
|
||||
} else {
|
||||
print "$key: $disk_structure->{$key}\n";
|
||||
}
|
||||
}
|
||||
print "</pre>";
|
||||
print "</div></div>";
|
||||
|
||||
# Debug: Raw GEOM output
|
||||
print "<div class='panel panel-default'>";
|
||||
print "<div class='panel-heading'><h3 class='panel-title'>$text{'disk_debug_geom'}</h3></div>";
|
||||
print "<div class='panel-body'>";
|
||||
print "<pre>Raw GEOM output:\n";
|
||||
print &html_escape(&backquote_command("geom disk list " . "e_path($device) . " 2>/dev/null"));
|
||||
print "</pre>";
|
||||
print "</div></div>";
|
||||
print "</div>";
|
||||
}
|
||||
# Build partition details from disk_structure (no separate gpart list call)
|
||||
my %part_details = ();
|
||||
if ($disk_structure && $disk_structure->{'partitions'}) {
|
||||
%part_details = %{ $disk_structure->{'partitions'} };
|
||||
}
|
||||
# Ensure we have names/labels for any entries missing from partitions map
|
||||
if ($disk_structure && $disk_structure->{'entries'}) {
|
||||
foreach my $entry (@{$disk_structure->{'entries'}}) {
|
||||
next unless ($entry->{'type'} eq 'partition' && $entry->{'index'});
|
||||
my $part_num = $entry->{'index'};
|
||||
$part_details{$part_num} ||= {};
|
||||
$part_details{$part_num}->{'name'} ||= $base_device . (($disk_structure->{'scheme'} eq 'GPT') ? "p$part_num" : "s$part_num");
|
||||
if ($entry->{'label'} && $entry->{'label'} ne '(null)') {
|
||||
$part_details{$part_num}->{'label'} ||= $entry->{'label'};
|
||||
}
|
||||
$part_details{$part_num}->{'type'} ||= $entry->{'part_type'} || 'unknown';
|
||||
}
|
||||
}
|
||||
# Build ZFS devices cache
|
||||
my ($zfs_pools, $zfs_devices) = &build_zfs_devices_cache();
|
||||
# Debug ZFS pools if debug mode is enabled
|
||||
if ($in{'debug'}) {
|
||||
print "<div class='debug-section'>";
|
||||
print "<div class='panel panel-default'>";
|
||||
print "<div class='panel-heading'><h3 class='panel-title'>$text{'disk_debug_zfs'}</h3></div>";
|
||||
print "<div class='panel-body'>";
|
||||
print "<pre>";
|
||||
my $cmd = "zpool status 2>&1";
|
||||
my $out = &backquote_command($cmd);
|
||||
print "Command: $cmd\nOutput:\n$out\n";
|
||||
print "</pre>";
|
||||
print "</div></div>";
|
||||
print "</div>";
|
||||
}
|
||||
|
||||
# Debug: Print partition details mapping if debug enabled
|
||||
if ($in{'debug'}) {
|
||||
print "<div class='debug-section'>";
|
||||
print "<div class='panel panel-default'>";
|
||||
print "<div class='panel-heading'><h3 class='panel-title'>$text{'disk_debug_part_details'}</h3></div>";
|
||||
print "<div class='panel-body'>";
|
||||
print "<pre>Partition Details Mapping:\n";
|
||||
foreach my $pnum (sort { $a <=> $b } keys %part_details) {
|
||||
print " $pnum: {\n";
|
||||
foreach my $k (sort keys %{$part_details{$pnum}}) {
|
||||
print " $k: $part_details{$pnum}->{$k}\n";
|
||||
}
|
||||
print " },\n";
|
||||
}
|
||||
print "</pre>";
|
||||
print "</div></div>";
|
||||
print "</div>";
|
||||
}
|
||||
# Get sector size
|
||||
my $sectorsize = $disk_structure->{'sectorsize'} || &get_disk_sectorsize($device) || 512;
|
||||
my $sectorsize_text = $sectorsize ? "$sectorsize" : "512";
|
||||
# Show partitions table
|
||||
my @links = ( "<a href='slice_form.cgi?device=".&urlize($disk->{'device'}).
|
||||
"&new=1'>".$text{'disk_add'}."</a>" );
|
||||
if (@{$disk->{'slices'}}) {
|
||||
print &ui_links_row(\@links);
|
||||
print &ui_columns_start([
|
||||
$text{'disk_no'},
|
||||
$text{'disk_type'},
|
||||
$text{'disk_extent'},
|
||||
$text{'disk_size'},
|
||||
$text{'disk_start'},
|
||||
$text{'disk_end'},
|
||||
$text{'disk_use'},
|
||||
]);
|
||||
foreach my $p (@{$disk->{'slices'}}) {
|
||||
# Create images for the extent
|
||||
my $ext = "";
|
||||
$ext .= sprintf "<img src=images/gap.gif height=10 width=%d>",
|
||||
$extwidth*($p->{'startblock'} - 1) /
|
||||
$disk->{'blocks'};
|
||||
$ext .= sprintf "<img src=images/%s.gif height=10 width=%d>",
|
||||
$p->{'extended'} ? "ext" : "use",
|
||||
$extwidth*($p->{'blocks'}) /
|
||||
$disk->{'blocks'};
|
||||
$ext .= sprintf "<img src=images/gap.gif height=10 width=%d>",
|
||||
$extwidth*($disk->{'blocks'} - $p->{'startblock'} -
|
||||
$p->{'blocks'}) / $disk->{'blocks'};
|
||||
|
||||
# Work out use
|
||||
my @st = &fdisk::device_status($p->{'device'});
|
||||
my $use = &fdisk::device_status_link(@st);
|
||||
my $n = scalar(@{$p->{'parts'}});
|
||||
|
||||
# Add row for the slice
|
||||
my $url = "edit_slice.cgi?device=".&urlize($disk->{'device'}).
|
||||
"&slice=".$p->{'number'};
|
||||
my $nlink = "<a href='$url'>$p->{'number'}</a>";
|
||||
$nlink = "<b>$nlink</b>" if ($p->{'active'});
|
||||
print &ui_columns_row([
|
||||
$nlink,
|
||||
"<a href='$url'>".&fdisk::tag_name($p->{'type'})."</a>",
|
||||
$ext,
|
||||
&nice_size($p->{'size'}),
|
||||
$p->{'startblock'},
|
||||
$p->{'startblock'} + $p->{'blocks'} - 1,
|
||||
$use ? $use :
|
||||
$n ? &text('disk_scount', $n) : "",
|
||||
]);
|
||||
}
|
||||
print &ui_columns_end();
|
||||
}
|
||||
else {
|
||||
print "<b>$text{'disk_none'}</b><p>\n";
|
||||
}
|
||||
my @links = ( "<a href='slice_form.cgi?device=$device_url&new=1'>".$text{'disk_add'}."</a>" );
|
||||
if (@$entries) {
|
||||
print &ui_links_row(\@links);
|
||||
print &ui_columns_start([
|
||||
$text{'disk_no'}, # Row number
|
||||
$text{'disk_partno'}, # Part. No.
|
||||
$text{'disk_partname'}, # Part. Name
|
||||
$text{'disk_partlabel'}, # Part. Label
|
||||
$text{'disk_subpart'}, # Sub-part.
|
||||
$text{'disk_extent'}, # Extent
|
||||
$text{'disk_start'}, # Startblock
|
||||
$text{'disk_end'}, # Endblock
|
||||
$text{'disk_size'}, # Size
|
||||
$text{'disk_format'}, # Format type
|
||||
$text{'disk_use'}, # Used by
|
||||
$text{'disk_role'}, # Role Type
|
||||
]);
|
||||
my $row_number = 1;
|
||||
foreach my $entry (@$entries) {
|
||||
my @cols = ();
|
||||
push(@cols, $row_number++);
|
||||
if ($entry->{'type'} eq 'free') {
|
||||
my $start = $entry->{'start'};
|
||||
my $end = $entry->{'start'} + $entry->{'size'} - 1;
|
||||
my $create_url = "slice_form.cgi?device=$device_url&new=1&start=$start&end=$end";
|
||||
push(@cols, "<a href='$create_url' style='color: green;'>".$text{'disk_free'}."</a>");
|
||||
push(@cols, "-");
|
||||
push(@cols, "-");
|
||||
push(@cols, "-");
|
||||
my $ext = "";
|
||||
$ext .= sprintf "<img src='images/gap.gif' height='10' width='%d'>", $scale * ($entry->{'start'} - 1);
|
||||
$ext .= sprintf "<img src='images/gap.gif' height='10' width='%d' style='background-color: #8f8;'>", $scale * ($entry->{'size'});
|
||||
$ext .= sprintf "<img src='images/gap.gif' height='10' width='%d'>", $scale * ($disk_blocks - $entry->{'start'} - $entry->{'size'});
|
||||
push(@cols, $ext);
|
||||
push(@cols, $start);
|
||||
push(@cols, $end);
|
||||
push(@cols, $entry->{'size_human'});
|
||||
push(@cols, $text{'disk_free_space'});
|
||||
push(@cols, $text{'disk_available'});
|
||||
push(@cols, "-");
|
||||
} else {
|
||||
my $part_num = $entry->{'index'};
|
||||
my $ext = "";
|
||||
$ext .= sprintf "<img src='images/gap.gif' height='10' width='%d'>", $scale * ($entry->{'start'} - 1);
|
||||
$ext .= sprintf "<img src='images/use.gif' height='10' width='%d'>", $scale * ($entry->{'size'});
|
||||
$ext .= sprintf "<img src='images/gap.gif' height='10' width='%d'>", $scale * ($disk_blocks - $entry->{'start'} - $entry->{'size'});
|
||||
my $url = "edit_slice.cgi?device=$device_url&slice=".&urlize($part_num);
|
||||
push(@cols, "<a href='$url'>".&html_escape($part_num)."</a>");
|
||||
|
||||
my $part_info = $part_details{$part_num};
|
||||
my $part_name = $part_info ? $part_info->{'name'} : "-";
|
||||
push(@cols, $part_name);
|
||||
my $part_label = $part_info ? $part_info->{'label'} : ($entry->{'label'} eq "(null)" ? "-" : $entry->{'label'});
|
||||
push(@cols, $part_label);
|
||||
|
||||
# Find sub-partitions if available
|
||||
my ($slice) = grep { $_->{'number'} eq $part_num } @{$disk->{'slices'} || []};
|
||||
my $sub_part_info = ($slice && scalar(@{$slice->{'parts'}||[]}) > 0) ?
|
||||
join(", ", map { $_->{'letter'} } @{$slice->{'parts'}}) : "-";
|
||||
push(@cols, $sub_part_info);
|
||||
|
||||
push(@cols, $ext);
|
||||
push(@cols, $entry->{'start'});
|
||||
push(@cols, $entry->{'start'} + $entry->{'size'} - 1);
|
||||
push(@cols, $entry->{'size_human'});
|
||||
|
||||
# Classify format/use/role via library helper
|
||||
my ($format_type, $usage, $role) = classify_partition_row(
|
||||
base_device => $base_device,
|
||||
scheme => ($disk_structure->{'scheme'} || ''),
|
||||
part_num => $part_num,
|
||||
part_name => $part_name,
|
||||
part_label => $part_label,
|
||||
entry_part_type => ($part_info ? $part_info->{'type'} : $entry->{'part_type'}),
|
||||
entry_rawtype => ($part_info ? $part_info->{'rawtype'} : undef),
|
||||
size_human => $entry->{'size_human'},
|
||||
size_blocks => $entry->{'size'},
|
||||
zfs_devices => $zfs_devices,
|
||||
);
|
||||
push(@cols, $format_type || '-');
|
||||
push(@cols, $usage || $text{'part_nouse'});
|
||||
push(@cols, $role || '-');
|
||||
}
|
||||
print &ui_columns_row(\@cols);
|
||||
}
|
||||
print &ui_columns_end();
|
||||
} else {
|
||||
if (@{$disk->{'slices'}||[]}) {
|
||||
print &ui_links_row(\@links);
|
||||
print &ui_columns_start([
|
||||
$text{'disk_no'},
|
||||
$text{'disk_type'},
|
||||
$text{'disk_extent'},
|
||||
$text{'disk_start'},
|
||||
$text{'disk_end'},
|
||||
$text{'disk_use'},
|
||||
]);
|
||||
foreach my $s (@{$disk->{'slices'}}) {
|
||||
my @cols = ();
|
||||
my $ext = "";
|
||||
$ext .= sprintf "<img src='images/gap.gif' height='10' width='%d'>", $scale * ($s->{'startblock'} - 1);
|
||||
$ext .= sprintf "<img src='images/%s.gif' height='10' width='%d'>", ($s->{'extended'} ? "ext" : "use"), $scale * ($s->{'blocks'});
|
||||
$ext .= sprintf "<img src='images/gap.gif' height='10' width='%d'>", $scale * ($disk_blocks - $s->{'startblock'} - $s->{'blocks'});
|
||||
my $url = "edit_slice.cgi?device=$device_url&slice=".&urlize($s->{'number'});
|
||||
push(@cols, "<a href='$url'>".&html_escape($s->{'number'})."</a>");
|
||||
push(@cols, &get_type_description($s->{'type'}) || $s->{'type'});
|
||||
push(@cols, $ext);
|
||||
push(@cols, $s->{'startblock'});
|
||||
push(@cols, $s->{'startblock'} + $s->{'blocks'} - 1);
|
||||
my @st = &fdisk::device_status($s->{'device'});
|
||||
my $use = &fdisk::device_status_link(@st);
|
||||
push(@cols, $use || $text{'part_nouse'});
|
||||
print &ui_columns_row(\@cols);
|
||||
}
|
||||
print &ui_columns_end();
|
||||
} else {
|
||||
print "<p>$text{'disk_none'}</p>\n";
|
||||
}
|
||||
}
|
||||
print &ui_links_row(\@links);
|
||||
|
||||
print &ui_hr();
|
||||
print &ui_buttons_start();
|
||||
|
||||
if (&foreign_installed("smart-status")) {
|
||||
print &ui_buttons_row(
|
||||
"../smart-status/index.cgi",
|
||||
$text{'disk_smart'},
|
||||
$text{'disk_smartdesc'},
|
||||
&ui_hidden("drive", $disk->{'device'}.":"));
|
||||
}
|
||||
|
||||
print &ui_buttons_end();
|
||||
|
||||
&ui_print_footer("", $text{'index_return'});
|
||||
# Show SMART status link if available
|
||||
if (&has_command("smartctl")) {
|
||||
print &ui_hr();
|
||||
print &ui_buttons_start();
|
||||
print &ui_buttons_row("smart.cgi", $text{'disk_smart'}, $text{'disk_smartdesc'}, &ui_hidden("device", $device));
|
||||
print &ui_buttons_end();
|
||||
}
|
||||
# Debug: ZFS cache detail
|
||||
if ($in{'debug'}) {
|
||||
print "<div class='debug-section'>";
|
||||
print "<div class='panel panel-default'>";
|
||||
print "<div class='panel-heading'><h3 class='panel-title'>$text{'disk_debug_zfs_cache'}</h3></div>";
|
||||
print "<div class='panel-body'>";
|
||||
print "<pre>Pools: " . join(", ", keys %$zfs_pools) . "\n\nDevices:\n";
|
||||
foreach my $device_id (sort keys %$zfs_devices) {
|
||||
next if $device_id =~ /^_debug_/;
|
||||
my $device_info = $zfs_devices->{$device_id};
|
||||
print "$device_id => Pool: $device_info->{'pool'}, Type: $device_info->{'vdev_type'}, Mirrored: " .
|
||||
($device_info->{'is_mirrored'} ? "Yes" : "No") . ", RAIDZ: " .
|
||||
($device_info->{'is_raidz'} ? "Yes (Level: $device_info->{'raidz_level'})" : "No") .
|
||||
", Single: " . ($device_info->{'is_single'} ? "Yes" : "No") .
|
||||
", Striped: " . ($device_info->{'is_striped'} ? "Yes" : "No") . "\n";
|
||||
}
|
||||
print "</pre>";
|
||||
print "</div></div>";
|
||||
print "</div>";
|
||||
}
|
||||
&ui_print_footer("", $text{'disk_return'});
|
||||
|
||||
@@ -1,87 +1,126 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Show details of a partition, with buttons to create a filesystem
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
&ReadParse();
|
||||
|
||||
# Get the disk and slice
|
||||
my @disks = &list_disks_partitions();
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{$disk->{'slices'}};
|
||||
$slice || &error($text{'slice_egone'});
|
||||
my ($part) = grep { $_->{'letter'} eq $in{'part'} } @{$slice->{'parts'}};
|
||||
$part || &error($text{'part_egone'});
|
||||
|
||||
&ui_print_header($part->{'desc'}, $text{'part_title'}, "");
|
||||
# Load required libraries
|
||||
require "./bsdfdisk-lib.pl";
|
||||
our ( %in, %text, $module_name );
|
||||
ReadParse();
|
||||
# Cache input parameters to avoid repeated hash lookups
|
||||
my $device = $in{'device'};
|
||||
my $slice_num = $in{'slice'};
|
||||
my $part_letter = $in{'part'};
|
||||
|
||||
# Get the disk and slice using first() to stop at the first matching element
|
||||
my @disks = list_disks_partitions();
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/ or error($text{'disk_edevice'});
|
||||
$in{'device'} !~ /\.\./ or error($text{'disk_edevice'});
|
||||
my $disk;
|
||||
foreach my $d (@disks) {
|
||||
if ($d->{'device'} eq $device) {
|
||||
$disk = $d;
|
||||
last;
|
||||
}
|
||||
}
|
||||
$disk or error($text{'disk_egone'});
|
||||
my $slice;
|
||||
foreach my $s (@{$disk->{'slices'}}) {
|
||||
if ($s->{'number'} eq $slice_num) {
|
||||
$slice = $s;
|
||||
last;
|
||||
}
|
||||
}
|
||||
$slice or error($text{'slice_egone'});
|
||||
my $part;
|
||||
foreach my $p (@{$slice->{'parts'}}) {
|
||||
if ($p->{'letter'} eq $part_letter) { $part = $p; last; }
|
||||
}
|
||||
$part or error($text{'part_egone'});
|
||||
ui_print_header($part->{'desc'}, $text{'part_title'}, "");
|
||||
# Check if this is a boot partition
|
||||
my $is_boot = is_boot_partition($part);
|
||||
if ($is_boot) {
|
||||
print ui_alert_box($text{'part_bootdesc'}, 'info');
|
||||
}
|
||||
# Show current details
|
||||
my @st = &fdisk::device_status($part->{'device'});
|
||||
my $use = &fdisk::device_status_link(@st);
|
||||
my $canedit = !@st || !$st[2];
|
||||
my $hiddens = &ui_hidden("device", $in{'device'})."\n".
|
||||
&ui_hidden("slice", $in{'slice'})."\n".
|
||||
&ui_hidden("part", $in{'part'})."\n";
|
||||
my $zfs_info = get_all_zfs_info();
|
||||
my @st = fdisk::device_status($part->{'device'});
|
||||
# calculate $use from either ZFS info or from a status link
|
||||
my $device_path = $part->{'device'};
|
||||
my $use = $zfs_info->{ $device_path } || fdisk::device_status_link(@st);
|
||||
my $canedit = (!@st && !$zfs_info->{ $device_path } && !$is_boot);
|
||||
# Prepare hidden fields once
|
||||
my $hiddens = ui_hidden("device", $device) . "\n" .
|
||||
ui_hidden("slice", $slice_num) . "\n" .
|
||||
ui_hidden("part", $part_letter) . "\n";
|
||||
if ($canedit) {
|
||||
print &ui_form_start("save_part.cgi", "post");
|
||||
print $hiddens;
|
||||
}
|
||||
print &ui_table_start($text{'part_header'}, undef, 2);
|
||||
|
||||
print &ui_table_row($text{'part_device'},
|
||||
"<tt>$part->{'device'}</tt>");
|
||||
|
||||
print &ui_table_row($text{'part_size'},
|
||||
&nice_size($part->{'size'}));
|
||||
|
||||
print &ui_table_row($text{'part_start'},
|
||||
$part->{'startblock'});
|
||||
|
||||
print &ui_table_row($text{'part_end'},
|
||||
$part->{'startblock'} + $part->{'blocks'} - 1);
|
||||
|
||||
print ui_form_start("save_part.cgi", "post"), $hiddens;
|
||||
}
|
||||
print ui_table_start($text{'part_header'}, undef, 2);
|
||||
print ui_table_row($text{'part_device'}, "<tt>$part->{'device'}</tt>");
|
||||
my $part_bytes = bytes_from_blocks($part->{'device'}, $part->{'blocks'});
|
||||
print ui_table_row($text{'part_size'}, $part_bytes ? safe_nice_size($part_bytes) : '-');
|
||||
print ui_table_row($text{'part_start'}, $part->{'startblock'});
|
||||
print ui_table_row($text{'part_end'}, $part->{'startblock'} + $part->{'blocks'} - 1);
|
||||
my $disk_geom = get_detailed_disk_info($disk->{'device'});
|
||||
my $stripesize = ($disk_geom && $disk_geom->{'stripesize'}) ? $disk_geom->{'stripesize'} : '-';
|
||||
print ui_table_row($text{'disk_stripesize'}, $stripesize);
|
||||
if ($canedit) {
|
||||
print &ui_table_row($text{'part_type'},
|
||||
&ui_select("type", $part->{'type'},
|
||||
[ &list_partition_types() ], 1, 0, 1));
|
||||
}
|
||||
else {
|
||||
print &ui_table_row($text{'part_type'},
|
||||
$part->{'type'});
|
||||
}
|
||||
|
||||
print &ui_table_row($text{'part_use'},
|
||||
!@st ? $text{'part_nouse'} :
|
||||
$st[2] ? &text('part_inuse', $use) :
|
||||
&text('part_foruse', $use));
|
||||
|
||||
print &ui_table_end();
|
||||
# BSD disklabel partitions only support FreeBSD types
|
||||
print ui_table_row($text{'part_type'},
|
||||
ui_select("type", $part->{'type'}, [ list_partition_types('BSD') ], 1, 0, 1));
|
||||
} else {
|
||||
print ui_table_row($text{'part_type'}, get_format_type($part));
|
||||
}
|
||||
my $use_text = ((!@st && !$zfs_info->{ $part->{'device'} })
|
||||
? $text{'part_nouse'}
|
||||
: (($st[2] || $zfs_info->{ $part->{'device'} })
|
||||
? text('part_inuse', $use)
|
||||
: text('part_foruse', $use)));
|
||||
print ui_table_row($text{'part_use'}, $use_text);
|
||||
# Add a row for the partition role
|
||||
print ui_table_row($text{'part_role'}, get_partition_role($part));
|
||||
print ui_table_end();
|
||||
if ($canedit) {
|
||||
print &ui_form_end([ [ undef, $text{'save'} ] ]);
|
||||
}
|
||||
print ui_form_end([[ undef, $text{'save'} ]]);
|
||||
}
|
||||
|
||||
# Show newfs and mount buttons
|
||||
# Existing partitions on this slice
|
||||
if (@{ $slice->{'parts'} || [] }) {
|
||||
my $zfs = get_all_zfs_info();
|
||||
print ui_hr();
|
||||
print ui_columns_start([
|
||||
$text{'slice_letter'}, $text{'slice_type'}, $text{'slice_start'}, $text{'slice_end'}, $text{'slice_size'}, $text{'slice_use'}, $text{'slice_role'}
|
||||
], $text{'epart_existing'});
|
||||
foreach my $p (sort { $a->{'startblock'} <=> $b->{'startblock'} } @{ $slice->{'parts'} }) {
|
||||
my $ptype = get_type_description($p->{'type'}) || $p->{'type'};
|
||||
my @stp = fdisk::device_status($p->{'device'});
|
||||
my $usep = $zfs->{$p->{'device'}} || fdisk::device_status_link(@stp) || $text{'part_nouse'};
|
||||
my $rolep = get_partition_role($p);
|
||||
my $pb2 = bytes_from_blocks($p->{'device'}, $p->{'blocks'});
|
||||
print ui_columns_row([
|
||||
uc($p->{'letter'}), $ptype, $p->{'startblock'}, $p->{'startblock'} + $p->{'blocks'} - 1, ($pb2 ? safe_nice_size($pb2) : '-'), $usep, $rolep
|
||||
]);
|
||||
}
|
||||
print ui_columns_end();
|
||||
}
|
||||
|
||||
# Show newfs and mount buttons if editing is allowed
|
||||
if ($canedit) {
|
||||
print &ui_hr();
|
||||
|
||||
print &ui_buttons_start();
|
||||
|
||||
&show_filesystem_buttons($hiddens, \@st, $part);
|
||||
|
||||
print &ui_buttons_row(
|
||||
"delete_part.cgi", $text{'part_delete'},
|
||||
$text{'part_deletedesc'}, $hiddens);
|
||||
|
||||
print &ui_buttons_end();
|
||||
}
|
||||
else {
|
||||
print "<b>$text{'part_cannotedit'}</b><p>\n";
|
||||
}
|
||||
|
||||
&ui_print_footer("edit_slice.cgi?device=$in{'device'}&slice=$in{'slice'}",
|
||||
$text{'slice_return'});
|
||||
print ui_hr();
|
||||
print ui_buttons_start();
|
||||
show_filesystem_buttons($hiddens, \@st, $part);
|
||||
print ui_buttons_row("delete_part.cgi", $text{'part_delete'}, $text{'part_deletedesc'}, $hiddens);
|
||||
print ui_buttons_end();
|
||||
} else {
|
||||
print ($is_boot) ? "<b>$text{'part_bootcannotedit'}</b><p>\n"
|
||||
: "<b>$text{'part_cannotedit'}</b><p>\n";
|
||||
}
|
||||
# SMART button (physical device)
|
||||
if (&has_command("smartctl")) {
|
||||
print ui_hr();
|
||||
print ui_buttons_start();
|
||||
print ui_buttons_row("smart.cgi", $text{'disk_smart'}, $text{'disk_smartdesc'},
|
||||
ui_hidden("device", $disk->{'device'}));
|
||||
print ui_buttons_end();
|
||||
}
|
||||
ui_print_footer("edit_slice.cgi?device=$device&slice=$slice_num", $text{'slice_return'});
|
||||
@@ -1,146 +1,178 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Show details of a slice, and partitions on it
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
&ReadParse();
|
||||
ReadParse();
|
||||
my $extwidth = 300;
|
||||
|
||||
# Get the disk and slice
|
||||
my @disks = &list_disks_partitions();
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{$disk->{'slices'}};
|
||||
$slice || &error($text{'slice_egone'});
|
||||
|
||||
&ui_print_header($slice->{'desc'}, $text{'slice_title'}, "");
|
||||
|
||||
my @disks = list_disks_partitions();
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks or error($text{'disk_egone'});
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{$disk->{'slices'}} or error($text{'slice_egone'});
|
||||
ui_print_header($slice->{'desc'}, $text{'slice_title'}, "");
|
||||
# Show slice details
|
||||
my @st = &fdisk::device_status($slice->{'device'});
|
||||
my $use = &fdisk::device_status_link(@st);
|
||||
my $canedit = !@st || !$st[2];
|
||||
my $hiddens = &ui_hidden("device", $in{'device'})."\n".
|
||||
&ui_hidden("slice", $in{'slice'})."\n";
|
||||
print &ui_form_start("save_slice.cgi");
|
||||
my $zfs_info = get_all_zfs_info();
|
||||
my ($zfs_pools, $zfs_devices) = build_zfs_devices_cache();
|
||||
# Cache slice device status
|
||||
my @slice_status = fdisk::device_status($slice->{'device'});
|
||||
my $slice_use = $zfs_info->{$slice->{'device'}} ? $zfs_info->{$slice->{'device'}} : fdisk::device_status_link(@slice_status);
|
||||
my $canedit = (! @slice_status || !$slice_status[2]);
|
||||
# Prepare hidden fields
|
||||
my $hiddens = ui_hidden("device", $in{'device'}) . "\n" . ui_hidden("slice", $in{'slice'}) . "\n";
|
||||
# Derive disk scheme for classifier
|
||||
my $base_device = $disk->{'device'}; $base_device =~ s{^/dev/}{};
|
||||
my $disk_structure = get_disk_structure($base_device);
|
||||
# Check if this is a boot slice
|
||||
my $is_boot = is_boot_partition($slice);
|
||||
print ui_alert_box($text{'slice_bootdesc'}, 'info') if $is_boot;
|
||||
print ui_form_start("save_slice.cgi");
|
||||
print $hiddens;
|
||||
print &ui_table_start($text{'slice_header'}, undef, 2);
|
||||
|
||||
print &ui_table_row($text{'part_device'},
|
||||
"<tt>$slice->{'device'}</tt>");
|
||||
|
||||
print &ui_table_row($text{'slice_ssize'},
|
||||
&nice_size($slice->{'size'}));
|
||||
|
||||
print &ui_table_row($text{'slice_sstart'},
|
||||
$slice->{'startblock'});
|
||||
|
||||
print &ui_table_row($text{'slice_send'},
|
||||
$slice->{'startblock'} + $slice->{'blocks'} - 1);
|
||||
|
||||
print &ui_table_row($text{'slice_stype'},
|
||||
&ui_select("type", $slice->{'type'},
|
||||
[ sort { $a->[1] cmp $b->[1] }
|
||||
map { [ $_, &fdisk::tag_name($_) ] }
|
||||
&fdisk::list_tags() ]));
|
||||
|
||||
print &ui_table_row($text{'slice_sactive'},
|
||||
$slice->{'active'} ? $text{'yes'} :
|
||||
&ui_yesno_radio("active", $slice->{'active'}));
|
||||
|
||||
print &ui_table_row($text{'slice_suse'},
|
||||
!@st ? $text{'part_nouse'} :
|
||||
$st[2] ? &text('part_inuse', $use) :
|
||||
&text('part_foruse', $use));
|
||||
|
||||
print &ui_table_end();
|
||||
print &ui_form_end([ [ undef, $text{'save'} ] ]);
|
||||
|
||||
print &ui_hr();
|
||||
|
||||
# Show partitions table
|
||||
my @links = ( "<a href='part_form.cgi?device=".&urlize($disk->{'device'}).
|
||||
"&slice=$in{'slice'}'>".$text{'slice_add'}."</a>" );
|
||||
if (@{$slice->{'parts'}}) {
|
||||
print &ui_links_row(\@links);
|
||||
print &ui_columns_start([
|
||||
$text{'slice_letter'},
|
||||
$text{'slice_type'},
|
||||
$text{'slice_extent'},
|
||||
$text{'slice_size'},
|
||||
$text{'slice_start'},
|
||||
$text{'slice_end'},
|
||||
$text{'slice_use'},
|
||||
]);
|
||||
foreach my $p (@{$slice->{'parts'}}) {
|
||||
# Create images for the extent
|
||||
my $ext = "";
|
||||
$ext .= sprintf "<img src=images/gap.gif height=10 width=%d>",
|
||||
$extwidth*($p->{'startblock'} - 1) /
|
||||
$slice->{'blocks'};
|
||||
$ext .= sprintf "<img src=images/%s.gif height=10 width=%d>",
|
||||
$p->{'extended'} ? "ext" : "use",
|
||||
$extwidth*($p->{'blocks'}) /
|
||||
$slice->{'blocks'};
|
||||
$ext .= sprintf "<img src=images/gap.gif height=10 width=%d>",
|
||||
$extwidth*($slice->{'blocks'} - $p->{'startblock'} -
|
||||
$p->{'blocks'}) / $slice->{'blocks'};
|
||||
|
||||
# Work out use
|
||||
my @st = &fdisk::device_status($p->{'device'});
|
||||
my $use = &fdisk::device_status_link(@st);
|
||||
|
||||
# Add row for the partition
|
||||
my $url = "edit_part.cgi?device=".&urlize($disk->{'device'}).
|
||||
"&slice=".$slice->{'number'}."&part=".$p->{'letter'};
|
||||
print &ui_columns_row([
|
||||
"<a href='$url'>".uc($p->{'letter'})."</a>",
|
||||
"<a href='$url'>$p->{'type'}</a>",
|
||||
$ext,
|
||||
&nice_size($p->{'size'}),
|
||||
$p->{'startblock'},
|
||||
$p->{'startblock'} + $p->{'blocks'} - 1,
|
||||
$use,
|
||||
]);
|
||||
}
|
||||
print &ui_columns_end();
|
||||
print &ui_links_row(\@links);
|
||||
}
|
||||
print ui_table_start($text{'slice_header'}, undef, 2);
|
||||
print ui_table_row($text{'part_device'}, "<tt>$slice->{'device'}</tt>");
|
||||
my $slice_bytes = bytes_from_blocks($slice->{'device'}, $slice->{'blocks'});
|
||||
print ui_table_row($text{'slice_ssize'}, $slice_bytes ? safe_nice_size($slice_bytes) : '-');
|
||||
print ui_table_row($text{'slice_sstart'}, $slice->{'startblock'});
|
||||
print ui_table_row($text{'slice_send'}, $slice->{'startblock'} + $slice->{'blocks'} - 1);
|
||||
# Slice type selector (GPT vs legacy)
|
||||
if (is_using_gpart()) {
|
||||
my $scheme = ($disk_structure && $disk_structure->{'scheme'}) ? $disk_structure->{'scheme'} : 'GPT';
|
||||
my @opts = list_partition_types($scheme);
|
||||
# Default sensibly per scheme
|
||||
my $default_type = ($scheme =~ /GPT/i) ? 'freebsd-zfs' : 'freebsd';
|
||||
print ui_table_row($text{'slice_stype'}, ui_select("type", $slice->{'type'} || $default_type, \@opts));
|
||||
}
|
||||
else {
|
||||
# No partitions yet
|
||||
if (@st) {
|
||||
# And directly in use, so none can be created
|
||||
print "<b>$text{'slice_none2'}</b><p>\n";
|
||||
}
|
||||
else {
|
||||
# Show link to add first partition
|
||||
print "<b>$text{'slice_none'}</b><p>\n";
|
||||
print &ui_links_row(\@links);
|
||||
}
|
||||
}
|
||||
# Pre-cache tag options for the slice type select (legacy fdisk)
|
||||
my @tags = fdisk::list_tags();
|
||||
my @tag_options = map { [ $_, fdisk::tag_name($_) ] } @tags;
|
||||
@tag_options = sort { $a->[1] cmp $b->[1] } @tag_options;
|
||||
print ui_table_row($text{'slice_stype'}, ui_select("type", $slice->{'type'}, \@tag_options));
|
||||
}
|
||||
# Active slice - only applicable for legacy MBR. For GPT/UEFI and for EFI/freebsd-boot types, the active flag is irrelevant.
|
||||
my $is_gpt = is_using_gpart() && ($disk_structure && $disk_structure->{'scheme'} && $disk_structure->{'scheme'} =~ /GPT/i);
|
||||
if (!$is_gpt && ($slice->{'type'} !~ /^(?:efi|freebsd-boot)$/i)) {
|
||||
my $active_default = $slice->{'active'} ? 1 : 0;
|
||||
print ui_table_row($text{'slice_sactive'}, ui_yesno_radio("active", $active_default));
|
||||
} else {
|
||||
# Do not offer the control; display 'No' since active is not used here
|
||||
print ui_table_row($text{'slice_sactive'}, $text{'no'});
|
||||
}
|
||||
print ui_table_row($text{'slice_suse'},
|
||||
(!$slice_use || $slice_use eq $text{'part_nouse'})
|
||||
? $text{'part_nouse'}
|
||||
: ($slice_status[2] ? text('part_inuse', $slice_use) : text('part_foruse', $slice_use)));
|
||||
# Add a row for the slice role
|
||||
print ui_table_row($text{'slice_role'}, get_partition_role($slice));
|
||||
print ui_table_end();
|
||||
print ui_form_end([ [ undef, $text{'save'} ] ]);
|
||||
print ui_hr();
|
||||
# Show partitions table (only for MBR slices that support BSD disklabel)
|
||||
my $can_have_parts = 0;
|
||||
if (!is_using_gpart()) {
|
||||
# Legacy MBR with BSD disklabel
|
||||
$can_have_parts = 1;
|
||||
} elsif ($disk_structure && $disk_structure->{'scheme'} && $disk_structure->{'scheme'} !~ /GPT/i) {
|
||||
# MBR-style slice
|
||||
$can_have_parts = 1;
|
||||
}
|
||||
my @links = $can_have_parts ? ( "<a href='part_form.cgi?device=" . urlize($disk->{'device'}) . "&slice=$in{'slice'}'>" . $text{'slice_add'} . "</a>" ) : ();
|
||||
if (@{$slice->{'parts'}}) {
|
||||
print ui_links_row(\@links) if @links;
|
||||
print ui_columns_start([
|
||||
$text{'slice_letter'},
|
||||
$text{'slice_type'},
|
||||
$text{'slice_extent'},
|
||||
$text{'slice_size'},
|
||||
$text{'slice_start'},
|
||||
$text{'slice_end'},
|
||||
$text{'disk_stripesize'},
|
||||
$text{'slice_use'},
|
||||
$text{'slice_role'},
|
||||
]);
|
||||
|
||||
# Pre-calculate scaling factor for the partition extent images
|
||||
my $scale = $extwidth / $slice->{'blocks'};
|
||||
|
||||
foreach my $p (@{$slice->{'parts'}}) {
|
||||
# Create images representing the partition extent
|
||||
my $gap_before = sprintf("<img src=images/gap.gif height=10 width=%d>", int($scale * ($p->{'startblock'} - 1)));
|
||||
my $img_type = $p->{'extended'} ? "ext" : "use";
|
||||
my $partition_img = sprintf("<img src=images/%s.gif height=10 width=%d>", $img_type, int($scale * $p->{'blocks'}));
|
||||
my $gap_after = sprintf("<img src=images/gap.gif height=10 width=%d>", int($scale * ($slice->{'blocks'} - $p->{'startblock'} - $p->{'blocks'})));
|
||||
my $ext = $gap_before . $partition_img . $gap_after;
|
||||
|
||||
# Cache partition device status information
|
||||
my @part_status = fdisk::device_status($p->{'device'});
|
||||
my $part_use = $zfs_info->{$p->{'device'}} || fdisk::device_status_link(@part_status);
|
||||
# Prefer GEOM details for stripesize
|
||||
my $ginfo = get_detailed_disk_info($p->{'device'});
|
||||
my $stripesize = ($ginfo && $ginfo->{'stripesize'}) ? $ginfo->{'stripesize'} : '-';
|
||||
|
||||
# Classify format/use/role via library helper
|
||||
(my $pn = $p->{'device'}) =~ s{^/dev/}{};
|
||||
my ($fmt, $use_txt, $role_txt) = classify_partition_row(
|
||||
base_device => $base_device,
|
||||
scheme => ($disk_structure->{'scheme'} || ''),
|
||||
part_name => $pn,
|
||||
entry_part_type => $p->{'type'},
|
||||
zfs_devices => $zfs_devices,
|
||||
);
|
||||
$use_txt ||= $part_use;
|
||||
$role_txt ||= get_partition_role($p);
|
||||
|
||||
# Build edit URL
|
||||
my $url = "edit_part.cgi?device=" . urlize($disk->{'device'}) . "&slice=" . $slice->{'number'} . "&part=" . $p->{'letter'};
|
||||
my $psz_b = bytes_from_blocks($p->{'device'}, $p->{'blocks'});
|
||||
print ui_columns_row([
|
||||
"<a href='$url'>" . uc($p->{'letter'}) . "</a>",
|
||||
"<a href='$url'>" . ($fmt || get_format_type($p)) . "</a>",
|
||||
$ext,
|
||||
($psz_b ? safe_nice_size($psz_b) : '-'),
|
||||
$p->{'startblock'},
|
||||
$p->{'startblock'} + $p->{'blocks'} - 1,
|
||||
$stripesize,
|
||||
$use_txt,
|
||||
$role_txt,
|
||||
]);
|
||||
}
|
||||
print ui_columns_end();
|
||||
print ui_links_row(\@links) if @links;
|
||||
} else {
|
||||
# GPT partitions do not have sub-partitions
|
||||
if (!$can_have_parts) {
|
||||
# No message needed for GPT; partitions are top-level
|
||||
}
|
||||
# If slice is in use by a filesystem OR it is a boot slice, do not allow creating partitions
|
||||
elsif (@slice_status || $zfs_info->{$slice->{'device'}} || $is_boot) {
|
||||
print "<b>$text{'slice_none2'}</b><p>\n";
|
||||
} else {
|
||||
print "<b>$text{'slice_none'}</b><p>\n";
|
||||
print ui_links_row(\@links) if @links;
|
||||
}
|
||||
}
|
||||
if ($canedit && !$is_boot) { # Do not allow editing boot slices
|
||||
print ui_hr();
|
||||
print ui_buttons_start();
|
||||
if (!@{$slice->{'parts'}}) {
|
||||
show_filesystem_buttons($hiddens, \@slice_status, $slice);
|
||||
}
|
||||
print ui_buttons_row(
|
||||
'delete_slice.cgi',
|
||||
$text{'slice_delete'},
|
||||
$text{'slice_deletedesc'},
|
||||
ui_hidden("device", $in{'device'}) . "\n" . ui_hidden("slice", $in{'slice'})
|
||||
);
|
||||
print ui_buttons_end();
|
||||
}
|
||||
# SMART button (physical device)
|
||||
if (&has_command("smartctl")) {
|
||||
print ui_hr();
|
||||
print ui_buttons_start();
|
||||
print ui_buttons_row("smart.cgi", $text{'disk_smart'}, $text{'disk_smartdesc'},
|
||||
ui_hidden("device", $disk->{'device'}));
|
||||
print ui_buttons_end();
|
||||
}
|
||||
|
||||
if ($canedit) {
|
||||
print &ui_hr();
|
||||
print &ui_buttons_start();
|
||||
|
||||
if (!@{$slice->{'parts'}}) {
|
||||
&show_filesystem_buttons($hiddens, \@st, $slice);
|
||||
}
|
||||
|
||||
# Button to delete slice
|
||||
print &ui_buttons_row(
|
||||
'delete_slice.cgi',
|
||||
$text{'slice_delete'},
|
||||
$text{'slice_deletedesc'},
|
||||
&ui_hidden("device", $in{'device'})."\n".
|
||||
&ui_hidden("slice", $in{'slice'}));
|
||||
|
||||
print &ui_buttons_end();
|
||||
}
|
||||
|
||||
|
||||
&ui_print_footer("edit_disk.cgi?device=$in{'device'}",
|
||||
$text{'disk_return'});
|
||||
ui_print_footer("edit_disk.cgi?device=$in{'device'}", $text{'disk_return'});
|
||||
@@ -27,28 +27,54 @@ else {
|
||||
$object = $slice;
|
||||
}
|
||||
|
||||
# Safety checks: do not run fsck on boot partitions or in-use devices
|
||||
if (is_boot_partition($object)) {
|
||||
&error($in{'part'} ne '' ? $text{'part_eboot'} : $text{'slice_eboot'});
|
||||
}
|
||||
my @st_obj = &fdisk::device_status($object->{'device'});
|
||||
my $use_obj = &fdisk::device_status_link(@st_obj);
|
||||
if (@st_obj && $st_obj[2]) {
|
||||
&error(&text('part_esave', $use_obj));
|
||||
}
|
||||
|
||||
&ui_print_unbuffered_header($object->{'desc'}, $text{'fsck_title'}, "");
|
||||
|
||||
# Do the creation
|
||||
print &text('fsck_checking', "<tt>$object->{'device'}</tt>"),"<br>\n";
|
||||
print "<pre>\n";
|
||||
my $cmd = &get_check_filesystem_command($disk, $slice, $part);
|
||||
&additional_log('exec', undef, $cmd);
|
||||
my $fh = "CMD";
|
||||
&open_execute_command($fh, $cmd, 2);
|
||||
while(<$fh>) {
|
||||
print &html_escape($_);
|
||||
}
|
||||
close($fh);
|
||||
print "</pre>";
|
||||
if ($?) {
|
||||
print $text{'fsck_failed'},"<p>\n";
|
||||
}
|
||||
else {
|
||||
print $text{'fsck_done'},"<p>\n";
|
||||
}
|
||||
# If device is ZFS, do not run fsck; show zpool status instead
|
||||
my $zmap = get_all_zfs_info();
|
||||
if ($zmap->{$object->{'device'}}) {
|
||||
my $pool = $zmap->{$object->{'device'}}; $pool =~ s/^.*?\b([A-Za-z0-9_\-]+)\b.*$/$1/;
|
||||
print &text('fsck_checking', "<tt>$object->{'device'}</tt>"),"<br>\n";
|
||||
print "<pre>\n";
|
||||
my $cmd = "zpool status 2>&1";
|
||||
&additional_log('exec', undef, $cmd);
|
||||
print &html_escape(&backquote_command($cmd));
|
||||
print "</pre>";
|
||||
print $text{'fsck_done'},"<p>\n";
|
||||
} else {
|
||||
# Do the creation
|
||||
print &text('fsck_checking', "<tt>$object->{'device'}</tt>"),"<br>\n";
|
||||
print "<pre>\n";
|
||||
my $cmd = &get_check_filesystem_command($disk, $slice, $part);
|
||||
&additional_log('exec', undef, $cmd);
|
||||
my $fh;
|
||||
&open_execute_command($fh, $cmd, 2);
|
||||
if ($fh) {
|
||||
while (my $line = <$fh>) {
|
||||
$line =~ s/[^\x09\x0A\x0D\x20-\x7E]//g;
|
||||
print &html_escape($line);
|
||||
}
|
||||
close($fh);
|
||||
}
|
||||
print "</pre>";
|
||||
if ($?) {
|
||||
print $text{'fsck_failed'},"<p>\n";
|
||||
}
|
||||
else {
|
||||
print $text{'fsck_done'},"<p>\n";
|
||||
}
|
||||
}
|
||||
&webmin_log("fsck", $in{'part'} ne '' ? "part" : "object",
|
||||
$object->{'device'}, $object);
|
||||
$object->{'device'}, $object);
|
||||
|
||||
if ($in{'part'} ne '') {
|
||||
&ui_print_footer("edit_part.cgi?device=$in{'device'}&".
|
||||
@@ -59,4 +85,4 @@ else {
|
||||
&ui_print_footer("edit_slice.cgi?device=$in{'device'}&".
|
||||
"slice=$in{'slice'}",
|
||||
$text{'slice_return'});
|
||||
}
|
||||
}
|
||||
BIN
bsdfdisk/images/free.gif
Normal file
BIN
bsdfdisk/images/free.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 B |
@@ -1,41 +1,57 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Show a list of disks
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, %config, $module_name);
|
||||
|
||||
&ui_print_header(undef, $text{'index_title'}, "", "intro", 1, 1, 0,
|
||||
&help_search_link("fdisk", "man"));
|
||||
|
||||
my $err = &check_fdisk();
|
||||
# Check prerequisites first
|
||||
my $err = check_fdisk();
|
||||
if ($err) {
|
||||
&ui_print_endpage(&text('index_problem', $err));
|
||||
}
|
||||
ui_print_header(undef, $text{'index_title'}, "", "intro", 1, 1, 0);
|
||||
print "<b>$text{'index_problem'}</b><br>\n$err\n";
|
||||
ui_print_footer("/", $text{'index_return'});
|
||||
exit;
|
||||
}
|
||||
|
||||
# Print header with help link
|
||||
ui_print_header(undef, $text{'index_title'}, "", "intro", 1, 1, 0,
|
||||
help_search_link("fdisk", "man"));
|
||||
|
||||
# List and sort disks by device name
|
||||
my @disks = list_disks_partitions();
|
||||
@disks = sort { ($a->{'device'}//'') cmp ($b->{'device'}//'') } @disks;
|
||||
|
||||
my @disks = &list_disks_partitions();
|
||||
@disks = sort { $a->{'device'} cmp $b->{'device'} } @disks;
|
||||
if (@disks) {
|
||||
print &ui_columns_start([ $text{'index_dname'},
|
||||
$text{'index_dsize'},
|
||||
$text{'index_dmodel'},
|
||||
$text{'index_dparts'} ]);
|
||||
foreach my $d (@disks) {
|
||||
print &ui_columns_row([
|
||||
"<a href='edit_disk.cgi?device=".&urlize($d->{'device'}).
|
||||
"'>".&partition_description($d->{'device'})."</a>",
|
||||
&nice_size($d->{'size'}),
|
||||
$d->{'model'},
|
||||
scalar(@{$d->{'slices'}}),
|
||||
]);
|
||||
}
|
||||
print &ui_columns_end();
|
||||
}
|
||||
else {
|
||||
print "<b>$text{'index_none'}</b> <p>\n";
|
||||
}
|
||||
print ui_columns_start([
|
||||
$text{'index_dname'},
|
||||
$text{'index_dsize'},
|
||||
$text{'index_dmodel'},
|
||||
$text{'index_dparts'}
|
||||
]);
|
||||
|
||||
&ui_print_footer("/", $text{'index'});
|
||||
foreach my $d (@disks) {
|
||||
my $device = $d->{'device'} // '';
|
||||
my $disk_name = $device; $disk_name =~ s{^/dev/}{};
|
||||
# Prefer mediasize from gpart list (bytes); fallback to diskinfo size
|
||||
my $base = $device; $base =~ s{^/dev/}{};
|
||||
my $ds = get_disk_structure($base);
|
||||
my $bytes = $ds && $ds->{'mediasize'} ? $ds->{'mediasize'} : $d->{'size'};
|
||||
my $size_display = defined $bytes ? safe_nice_size($bytes) : '-';
|
||||
my $model = $d->{'model'} // '-';
|
||||
my $url_device = urlize($device);
|
||||
my $slices_cnt = scalar(@{ $d->{'slices'} || [] });
|
||||
|
||||
print ui_columns_row([
|
||||
"<a href='edit_disk.cgi?device=$url_device'>$disk_name</a>",
|
||||
$size_display,
|
||||
$model, # Now correctly populated from bsdfdisk-lib.pl
|
||||
$slices_cnt,
|
||||
]);
|
||||
}
|
||||
print ui_columns_end();
|
||||
}
|
||||
else {
|
||||
print "<b>$text{'index_none'}</b><p>\n";
|
||||
}
|
||||
|
||||
ui_print_footer("/", $text{'index_return'});
|
||||
179
bsdfdisk/lang/en
179
bsdfdisk/lang/en
@@ -1,42 +1,92 @@
|
||||
index_title=Partitions on Local Disks
|
||||
index_ecmd=The required command $1 is missing
|
||||
index_problem=This module cannot be used : $1
|
||||
index_none=No disks were found on this system!
|
||||
index_ecmd=Missing required command $1
|
||||
index_problem=Cannot use this module : $1
|
||||
index_none=No disks found on this system!
|
||||
index_dname=Disk name
|
||||
index_dsize=Total size
|
||||
index_dmodel=Make and model
|
||||
index_dparts=Slices
|
||||
index_return=list of disks
|
||||
index_format=Format
|
||||
index_efdisk=You must have $1 or $2 installed to use this module
|
||||
disk_edevice=Invalid device parameter
|
||||
nslice_etype=Invalid slice type
|
||||
|
||||
# Disk overview
|
||||
disk_title=Edit Disk
|
||||
disk_egone=Disk no longer exists!
|
||||
disk_no=Slice
|
||||
disk_type=Type
|
||||
disk_extent=Extent
|
||||
disk_start=Start block
|
||||
disk_end=End block
|
||||
disk_use=Used by
|
||||
disk_scount=$1 partitions
|
||||
disk_parts=Partitions
|
||||
disk_free=Free space
|
||||
disk_vm=Virtual memory
|
||||
disk_iscsi=iSCSI shared device $1
|
||||
disk_none=This disk has no slices yet.
|
||||
disk_size=Size
|
||||
disk_dsize=<b>Disk size:</b> $1
|
||||
disk_model=<b>Make and model:</b> $1
|
||||
disk_cylinders=<b>Cylinders:</b> $1
|
||||
disk_blocks=<b>Blocks:</b> $1
|
||||
disk_device=<b>Device file:</b> $1
|
||||
disk_dsize=Disk size
|
||||
disk_model=Make and model
|
||||
disk_cylinders=Cylinders
|
||||
disk_blocks=Blocks
|
||||
disk_device=Device file
|
||||
disk_return=disk details and list of slices
|
||||
disk_add=Create a new slice.
|
||||
disk_smart=Show SMART Status
|
||||
disk_smartdesc=Show the current status of this drive as detected by SMART, and check it for disk errors.
|
||||
disk_no=No.
|
||||
disk_partno=Part. No.
|
||||
disk_partname=Part. Name
|
||||
disk_partlabel=Part. Label
|
||||
disk_subpart=Sub-part.
|
||||
disk_size=Size
|
||||
disk_free_space=Free Space
|
||||
disk_available=Available
|
||||
disk_format=Format
|
||||
disk_use=Used by
|
||||
disk_role=Role Type
|
||||
|
||||
select_device=$1 device $2
|
||||
select_slice=$1 device $2 slice $3
|
||||
select_part=$1 device $2 slice $3 partition $4
|
||||
# Debug and scheme
|
||||
disk_show_debug=Show raw debug information
|
||||
disk_hide_debug=Hide debug information
|
||||
disk_scheme=Partition Scheme
|
||||
disk_unknown=Unknown
|
||||
disk_sectorsize=Sector Size
|
||||
disk_bytes=bytes
|
||||
|
||||
# GEOM information
|
||||
disk_geom_header=GEOM Information
|
||||
disk_geom_details=GEOM Disk Details
|
||||
disk_mediasize=Media Size
|
||||
disk_stripesize=Stripe Size
|
||||
disk_stripeoffset=Stripe Offset
|
||||
disk_mode=Mode
|
||||
disk_rotationrate=Rotation Rate
|
||||
disk_ident=Identifier
|
||||
disk_lunid=LUN ID
|
||||
disk_descr=Description
|
||||
disk_rpm=RPM
|
||||
disk_ssd=SSD (Solid State Drive)
|
||||
|
||||
# Debug panels
|
||||
disk_debug_gpart=GPART Output
|
||||
disk_debug_structure=Parsed Disk Structure
|
||||
disk_debug_geom=GEOM Disk List
|
||||
disk_debug_zfs=ZFS Status
|
||||
disk_debug_zfs_cache=ZFS Devices Cache
|
||||
|
||||
# Roles and usage
|
||||
disk_inzfs=In ZFS pool
|
||||
disk_zfs_log=ZFS Log Device
|
||||
disk_zfs_cache=ZFS Cache Device
|
||||
disk_zfs_spare=ZFS Spare Device
|
||||
disk_zfs_mirror=ZFS Mirror
|
||||
disk_zfs_stripe=ZFS Stripe
|
||||
disk_zfs_single=ZFS Single Device
|
||||
disk_zfs_data=ZFS Data Device
|
||||
disk_boot=Boot Partition
|
||||
disk_boot_role=System Boot
|
||||
disk_swap=Swap
|
||||
disk_swap_role=Virtual Memory
|
||||
|
||||
# Slice pages
|
||||
slice_title=Edit Slice
|
||||
slice_egone=Selected slice does not exist!
|
||||
slice_ssize=Slice size
|
||||
@@ -56,55 +106,70 @@ slice_use=Used by
|
||||
slice_none=This slice has no partitions yet.
|
||||
slice_none2=This slice has no partitions, and none can be created as it is in use as a filesystem.
|
||||
slice_delete=Delete Slice
|
||||
slice_deletedesc=Delete this slice and all partitions and filesystems within it. Any data on those filesystem will be almost certainly unrecoverable.
|
||||
slice_deletedesc=Delete this slice and all partitions and filesystems within it. Any data on that filesystem will be almost certainly unrecoverable.
|
||||
slice_return=slice details and list of partitions
|
||||
slice_err=Failed to modify slice
|
||||
slice_header=Slice details
|
||||
slice_suse=Directly used by
|
||||
slice_adddesc=Create a new partition within this slice.
|
||||
slice_cannotedit=This slice cannot be modified as it is currently in use.
|
||||
slice_eboot=Cannot delete this slice as it contains boot partitions
|
||||
slice_bootdesc=This slice contains bootloader code or kernel files needed to start the system
|
||||
slice_role=Role
|
||||
|
||||
# Delete slice dialogs
|
||||
dslice_title=Delete Slice
|
||||
dslice_rusure=Are you sure you want to delete the slice $1? Any partitions and filesystems within it will also be deleted.
|
||||
dslice_rusure=Are you sure you want to delete the slice $1 ? Any partitions and filesystems within it will also be deleted.
|
||||
dslice_warn=Warning - this slice is currently used by : $1
|
||||
dslice_confirm=Delete Now
|
||||
dslice_deleting=Deleting slice $1 ..
|
||||
dslice_failed=.. deletion failed : $1
|
||||
dslice_done=.. done
|
||||
|
||||
# Create slice
|
||||
nslice_title=Create Slice
|
||||
nslice_header=New slice details
|
||||
nslice_number=Slice number
|
||||
nslice_autonext=Will auto-select next index
|
||||
nslice_diskblocks=Disk size in blocks
|
||||
nslice_start=Starting block
|
||||
nslice_end=Ending block
|
||||
nslice_type=New slice type
|
||||
nslice_makepart=Create default partition?
|
||||
nslice_err=Failed to create slice
|
||||
nslice_enumber=Missing or non-numeric slice number
|
||||
nslice_enumber=Slice number must be a number
|
||||
nslice_eclash=A slice with number $1 already exists
|
||||
nslice_estart=Starting block must be a number
|
||||
nslice_eend=Ending block must be a number
|
||||
nslice_erange=Starting block must be lower than the ending block
|
||||
nslice_emax=Ending block cannot be larger than the disk size of $1 blocks
|
||||
nslice_erange=Starting block must be before ending block
|
||||
nslice_emax=Ending block cannot be larger than disk size of $1 blocks
|
||||
nslice_creating=Creating slice $1 on $2 ..
|
||||
nslice_failed=.. slice creation failed : $1
|
||||
nslice_failed=.. creation failed : $1
|
||||
nslice_done=.. slice added
|
||||
nslice_parting=Create default partitions in slice $1 on $2 ..
|
||||
nslice_pfailed=.. partition creation failed : $1
|
||||
nslice_pdone=.. partition added
|
||||
nslice_existing_header=Existing slices on this disk
|
||||
nslice_existing_parts=Existing partitions on this disk
|
||||
nslice_enospace=No space on device left to create a slice!
|
||||
epart_existing=Existing partitions on this slice
|
||||
|
||||
# Create partition
|
||||
npart_title=Create Partition
|
||||
npart_header=New partition details
|
||||
npart_letter=Partition letter
|
||||
npart_diskblocks=Slice size in blocks
|
||||
npart_slicerel=(slice-relative)
|
||||
npart_creserved=partition 'c' is reserved
|
||||
npart_type=Partition type
|
||||
npart_err=Failed to create partition
|
||||
npart_eletter=Partition number must be a letter from A to D
|
||||
npart_eletter=Partition letter must be a-h (excluding c)
|
||||
npart_ereserved=Partition 'c' is reserved for the whole slice in BSD disklabels
|
||||
npart_eclash=A partition with letter $1 already exists
|
||||
npart_emax=Ending block cannot be larger than the slice size of $1 blocks
|
||||
npart_emax=Ending block cannot be larger than slice size of $1 blocks
|
||||
npart_erange=Start block must be less than end block
|
||||
npart_creating=Creating partition $1 on $2 ..
|
||||
npart_failed=.. partition creation failed : $1
|
||||
npart_done=.. partition added
|
||||
|
||||
# Edit partition
|
||||
part_title=Edit Partition
|
||||
part_egone=Partition no longer exists!
|
||||
part_header=Partition details
|
||||
@@ -128,18 +193,29 @@ part_err=Failed to save partition
|
||||
part_esave=Currently in use by $1
|
||||
part_newmount=Mount Partition On:
|
||||
part_newmount2=Mount Partition
|
||||
part_mountmsg=Mount this device on new directory on your system, so that it can be used to store files. A filesystem must have been already created on the device.
|
||||
part_mountmsg2=Mount this device as virtual memory on your system, to increase the amount of memory available.
|
||||
part_mountmsg=Mount this device on a new directory on your system, so that it can be used to store files. A filesystem must already be created on the device.
|
||||
part_mountmsg2=Mount this device as virtual memory on your system to increase the amount of available memory.
|
||||
part_cannotedit=This partition cannot be modified as it is currently in use.
|
||||
part_boot=Boot Partition
|
||||
part_eboot=Cannot delete boot partitions as this may render the system unbootable
|
||||
part_bootdesc=This partition contains bootloader code or kernel files needed to start the system
|
||||
part_zfslog=ZFS Log Device
|
||||
part_zfsdata=ZFS Data Device
|
||||
part_mounted=Mounted on $1
|
||||
part_unused=Not in use
|
||||
part_bootcannotedit=This partition cannot be modified as it is a boot partition. Changing it could render the system unbootable.
|
||||
part_role=Role
|
||||
|
||||
# Delete partition dialogs
|
||||
dpart_title=Delete Partition
|
||||
dpart_rusure=Are you sure you want to delete the partition $1? Any filesystems within it will also be deleted.
|
||||
dpart_rusure=Are you sure you want to delete the partition $1 ? Any filesystems in it will also be deleted.
|
||||
dpart_warn=Warning - this partition is currently used by $1
|
||||
dpart_confirm=Delete Now
|
||||
dpart_deleting=Deleting partition $1 ..
|
||||
dpart_failed=.. deletion failed : $1
|
||||
dpart_done=.. done
|
||||
|
||||
# New filesystem
|
||||
newfs_title=Create Filesystem
|
||||
newfs_header=New filesystem details
|
||||
newfs_free=Space to reserve for root
|
||||
@@ -147,27 +223,36 @@ newfs_deffree=Default (8%)
|
||||
newfs_trim=Enable TRIM mode for SSDs
|
||||
newfs_label=Filesystem label
|
||||
newfs_none=None
|
||||
newfs_create=Create Now
|
||||
newfs_err=Failed to create filesystem
|
||||
newfs_efree=Space to reserve for root must be a percentage
|
||||
newfs_elabel=Missing or invalid label
|
||||
newfs_efree=Missing or invalid percentage of free space
|
||||
newfs_elabel=Missing or invalid filesystem label
|
||||
newfs_creating=Creating filesystem on $1 ..
|
||||
newfs_failed=.. creation failed!
|
||||
newfs_done=.. created successfully
|
||||
newfs_failed=.. creation failed : $1
|
||||
newfs_done=.. filesystem created
|
||||
|
||||
# FS check
|
||||
fsck_title=Check Filesystem
|
||||
fsck_header=Filesystem check options
|
||||
fsck_repair=Repair mode
|
||||
fsck_fix=Only fix safe errors
|
||||
fsck_fix2=Try to fix all errors
|
||||
fsck_err=Failed to check filesystem
|
||||
fsck_checking=Checking filesystem on $1 ..
|
||||
fsck_exec=Executing command $1 ..
|
||||
fsck_failed=.. check failed!
|
||||
fsck_done=.. check completed with no errors found
|
||||
fsck_done=.. check complete
|
||||
fsck_checking=Checking filesystem on $1 ..
|
||||
|
||||
log_create_slice=Created slice $1
|
||||
log_delete_slice=Deleted slice $1
|
||||
log_modify_slice=Modified slice $1
|
||||
log_create_part=Created partition $1
|
||||
log_delete_part=Deleted partition $1
|
||||
log_modify_part=Modified partition $1
|
||||
log_newfs_part=Created filesystem on partition $1
|
||||
log_fsck_part=Checked filesystem on partition $1
|
||||
# Logging
|
||||
action_create_slice=Created slice $1
|
||||
action_delete_slice=Deleted slice $1
|
||||
action_modify_slice=Modified slice $1
|
||||
action_create_part=Created partition $1
|
||||
action_delete_part=Deleted partition $1
|
||||
action_modify_part=Modified partition $1
|
||||
action_create_fs=Created filesystem on $1
|
||||
action_check_fs=Checked filesystem on $1
|
||||
|
||||
__norefs=1
|
||||
# Generic
|
||||
save=Save
|
||||
yes=Yes
|
||||
no=No
|
||||
|
||||
@@ -27,13 +27,25 @@ else {
|
||||
$object = $slice;
|
||||
}
|
||||
|
||||
# Safety checks: do not run newfs on boot partitions or in-use devices
|
||||
if (is_boot_partition($object)) {
|
||||
&error($in{'part'} ne '' ? $text{'part_eboot'} : $text{'slice_eboot'});
|
||||
}
|
||||
my @st_obj = &fdisk::device_status($object->{'device'});
|
||||
my $use_obj = &fdisk::device_status_link(@st_obj);
|
||||
if (@st_obj && $st_obj[2]) {
|
||||
&error(&text('part_esave', $use_obj));
|
||||
}
|
||||
|
||||
# Validate inputs
|
||||
my $newfs = { };
|
||||
$in{'free_def'} || $in{'free'} =~ /^\d+$/ && $in{'free'} <= 100 ||
|
||||
$in{'free_def'} ||
|
||||
($in{'free'} =~ /^\d+$/ && $in{'free'} >= 0 && $in{'free'} <= 100) ||
|
||||
&error($text{'newfs_efree'});
|
||||
$newfs->{'free'} = $in{'free_def'} ? undef : $in{'free'};
|
||||
$newfs->{'trim'} = $in{'trim'};
|
||||
$in{'label_def'} || $in{'label'} =~ /^\S+$/ ||
|
||||
$in{'label_def'} ||
|
||||
length($in{'label'}) > 0 ||
|
||||
&error($text{'newfs_elabel'});
|
||||
$newfs->{'label'} = $in{'label_def'} ? undef : $in{'label'};
|
||||
|
||||
@@ -44,12 +56,14 @@ print &text('newfs_creating', "<tt>$object->{'device'}</tt>"),"<br>\n";
|
||||
print "<pre>\n";
|
||||
my $cmd = &get_create_filesystem_command($disk, $slice, $part, $newfs);
|
||||
&additional_log('exec', undef, $cmd);
|
||||
my $fh = "CMD";
|
||||
my $fh;
|
||||
&open_execute_command($fh, $cmd, 2);
|
||||
while(<$fh>) {
|
||||
print &html_escape($_);
|
||||
}
|
||||
close($fh);
|
||||
if ($fh) {
|
||||
while(<$fh>) {
|
||||
print &html_escape($_);
|
||||
}
|
||||
close($fh);
|
||||
}
|
||||
print "</pre>";
|
||||
if ($?) {
|
||||
print $text{'newfs_failed'},"<p>\n";
|
||||
@@ -58,6 +72,18 @@ else {
|
||||
print $text{'newfs_done'},"<p>\n";
|
||||
&webmin_log("newfs", $in{'part'} ne '' ? "part" : "object",
|
||||
$object->{'device'}, $object);
|
||||
# If a label was provided, set the partition label (GPT slice or BSD sub-partition)
|
||||
if (!$in{'label_def'} && defined $in{'label'} && length $in{'label'}) {
|
||||
my $errlbl = set_partition_label(
|
||||
disk => $disk,
|
||||
slice => $slice,
|
||||
part => ($in{'part'} ne '' ? $part : undef),
|
||||
label => $in{'label'}
|
||||
);
|
||||
if ($errlbl) {
|
||||
print "Warning: failed to set partition label: \n" . &html_escape($errlbl) . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($in{'part'} ne '') {
|
||||
@@ -69,4 +95,4 @@ else {
|
||||
&ui_print_footer("edit_slice.cgi?device=$in{'device'}&".
|
||||
"slice=$in{'slice'}",
|
||||
$text{'slice_return'});
|
||||
}
|
||||
}
|
||||
@@ -10,21 +10,27 @@ our (%in, %text, $module_name);
|
||||
&ReadParse();
|
||||
|
||||
# Get the disk and slice
|
||||
my @disks = &list_disks_partitions();
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{$disk->{'slices'}};
|
||||
$slice || &error($text{'slice_egone'});
|
||||
my $object;
|
||||
if ($in{'part'} ne '') {
|
||||
my ($part) = grep { $_->{'letter'} eq $in{'part'} }
|
||||
@{$slice->{'parts'}};
|
||||
$part || &error($text{'part_egone'});
|
||||
$object = $part;
|
||||
}
|
||||
else {
|
||||
$object = $slice;
|
||||
}
|
||||
# Validate input parameters to prevent command injection
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/ or &error("Invalid device name");
|
||||
$in{'device'} !~ /\.\./ or &error("Invalid device name");
|
||||
$in{'slice'} =~ /^\d+$/ or &error("Invalid slice number") if $in{'slice'};
|
||||
$in{'part'} =~ /^[a-z]$/ or &error("Invalid partition letter") if $in{'part'};
|
||||
my @disks = &list_disks_partitions();
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{$disk->{'slices'}};
|
||||
$slice || &error($text{'slice_egone'});
|
||||
my $object;
|
||||
if ($in{'part'} ne '') {
|
||||
$in{'part'} =~ /^[a-z]$/ or &error("Invalid partition letter");
|
||||
my ($part) = grep { $_->{'letter'} eq $in{'part'} }
|
||||
@{$slice->{'parts'}};
|
||||
$part || &error($text{'part_egone'});
|
||||
$object = $part;
|
||||
}
|
||||
else {
|
||||
$object = $slice;
|
||||
}
|
||||
|
||||
&ui_print_header($object->{'desc'}, $text{'newfs_title'}, "");
|
||||
|
||||
@@ -38,24 +44,24 @@ print &ui_table_row($text{'part_device'},
|
||||
"<tt>$object->{'device'}</tt>");
|
||||
|
||||
print &ui_table_row($text{'newfs_free'},
|
||||
&ui_opt_textbox("free", undef, 4, $text{'newfs_deffree'})."%");
|
||||
&ui_opt_textbox("free", undef, 4, $text{'newfs_deffree'}) . "%");
|
||||
|
||||
print &ui_table_row($text{'newfs_trim'},
|
||||
&ui_yesno_radio("trim", 0));
|
||||
&ui_yesno_radio("trim", 0));
|
||||
|
||||
print &ui_table_row($text{'newfs_label'},
|
||||
&ui_opt_textbox("label", undef, 20, $text{'newfs_none'}));
|
||||
&ui_opt_textbox("label", undef, 20, $text{'newfs_none'}));
|
||||
|
||||
print &ui_table_end();
|
||||
print &ui_form_end([ [ undef, $text{'newfs_create'} ] ]);
|
||||
print &ui_form_end([ [ undef, $text{'save'} ] ]);
|
||||
|
||||
if ($in{'part'} ne '') {
|
||||
&ui_print_footer("edit_part.cgi?device=$in{'device'}&".
|
||||
"slice=$in{'slice'}&part=$in{'part'}",
|
||||
$text{'part_return'});
|
||||
}
|
||||
&ui_print_footer("edit_part.cgi?device=$in{'device'}&" .
|
||||
"slice=$in{'slice'}&part=$in{'part'}",
|
||||
$text{'part_return'});
|
||||
}
|
||||
else {
|
||||
&ui_print_footer("edit_slice.cgi?device=$in{'device'}&".
|
||||
"slice=$in{'slice'}",
|
||||
$text{'slice_return'});
|
||||
}
|
||||
&ui_print_footer("edit_slice.cgi?device=$in{'device'}&" .
|
||||
"slice=$in{'slice'}",
|
||||
$text{'slice_return'});
|
||||
}
|
||||
@@ -23,37 +23,65 @@ print &ui_hidden("device", $in{'device'});
|
||||
print &ui_hidden("slice", $in{'slice'});
|
||||
print &ui_table_start($text{'npart_header'}, undef, 2);
|
||||
|
||||
# Partition number (first free)
|
||||
# Partition number (first free, skipping 'c' which is reserved for the whole slice)
|
||||
my %used = map { $_->{'letter'}, $_ } @{$slice->{'parts'}};
|
||||
$used{'c'} = 1; # Reserve 'c' for the whole slice (BSD convention)
|
||||
my $l = 'a';
|
||||
while($used{$l}) {
|
||||
$l++;
|
||||
}
|
||||
print &ui_table_row($text{'npart_letter'},
|
||||
&ui_textbox("letter", $l, 4));
|
||||
&ui_textbox("letter", $l, 4) . " <i>(" . $text{'npart_creserved'} . ")</i>");
|
||||
|
||||
# Slice size in blocks
|
||||
print &ui_table_row($text{'npart_diskblocks'},
|
||||
$slice->{'blocks'});
|
||||
|
||||
# Start and end blocks (defaults to last part)
|
||||
my ($start, $end) = (0, $slice->{'blocks'});
|
||||
foreach my $p (sort { $a->{'startblock'} cmp $b->{'startblock'} }
|
||||
@{$slice->{'parts'}}) {
|
||||
$start = $p->{'startblock'} + $p->{'blocks'} + 1;
|
||||
}
|
||||
print &ui_table_row($text{'nslice_start'},
|
||||
&ui_textbox("start", $start, 10));
|
||||
print &ui_table_row($text{'nslice_end'},
|
||||
&ui_textbox("end", $end, 10));
|
||||
|
||||
# Start and end blocks for BSD partitions are SLICE-RELATIVE (not disk-absolute)
|
||||
# Start at 0 (or after last partition), end at slice size - 1
|
||||
my ($start, $end) = (0, $slice->{'blocks'} - 1);
|
||||
foreach my $p (sort { $a->{'startblock'} <=> $b->{'startblock'} }
|
||||
@{$slice->{'parts'}}) {
|
||||
# Partitions are already stored as slice-relative
|
||||
$start = $p->{'startblock'} + $p->{'blocks'};
|
||||
}
|
||||
if (defined $in{'start'} && $in{'start'} =~ /^\d+$/) { $start = $in{'start'}; }
|
||||
if (defined $in{'end'} && $in{'end'} =~ /^\d+$/) { $end = $in{'end'}; }
|
||||
print &ui_table_row($text{'nslice_start'} . " " . $text{'npart_slicerel'},
|
||||
&ui_textbox("start", $start, 10));
|
||||
print &ui_table_row($text{'nslice_end'} . " " . $text{'npart_slicerel'},
|
||||
&ui_textbox("end", $end, 10));
|
||||
|
||||
# Partition type
|
||||
# For BSD-on-MBR inner label partitions, offer FreeBSD partition types
|
||||
my $scheme = 'BSD';
|
||||
my $default_ptype = 'freebsd-ufs';
|
||||
print &ui_table_row($text{'npart_type'},
|
||||
&ui_select("type", '4.2BSD',
|
||||
[ &list_partition_types() ]));
|
||||
|
||||
&ui_select("type", $default_ptype,
|
||||
[ list_partition_types($scheme) ]));
|
||||
|
||||
print &ui_table_end();
|
||||
print &ui_form_end([ [ undef, $text{'create'} ] ]);
|
||||
print &ui_form_end([ [ undef, $text{'save'} ] ]);
|
||||
|
||||
# Existing partitions summary
|
||||
if (@{$slice->{'parts'}||[]}) {
|
||||
my $zfs = get_all_zfs_info();
|
||||
print &ui_hr();
|
||||
print &ui_columns_start([
|
||||
$text{'slice_letter'}, $text{'slice_type'}, $text{'slice_start'}, $text{'slice_end'}, $text{'slice_size'}, $text{'slice_use'}, $text{'slice_role'}
|
||||
], $text{'epart_existing'});
|
||||
foreach my $p (sort { $a->{'startblock'} <=> $b->{'startblock'} } @{$slice->{'parts'}}) {
|
||||
my $ptype = get_type_description($p->{'type'}) || $p->{'type'};
|
||||
my @stp = fdisk::device_status($p->{'device'});
|
||||
my $usep = $zfs->{$p->{'device'}} || fdisk::device_status_link(@stp) || $text{'part_nouse'};
|
||||
my $rolep = get_partition_role($p);
|
||||
my $pb = bytes_from_blocks($p->{'device'}, $p->{'blocks'});
|
||||
print &ui_columns_row([
|
||||
uc($p->{'letter'}), $ptype, $p->{'startblock'}, $p->{'startblock'} + $p->{'blocks'} - 1, ($pb ? safe_nice_size($pb) : '-'), $usep, $rolep
|
||||
]);
|
||||
}
|
||||
print &ui_columns_end();
|
||||
}
|
||||
|
||||
&ui_print_footer("edit_slice.cgi?device=$in{'device'}&slice=$in{'slice'}",
|
||||
$text{'slice_return'});
|
||||
$text{'slice_return'});
|
||||
@@ -20,9 +20,22 @@ $slice || &error($text{'slice_egone'});
|
||||
# Apply changes
|
||||
my $oldslice = { %$slice };
|
||||
$slice->{'type'} = $in{'type'};
|
||||
if (!$slice->{'active'}) {
|
||||
$slice->{'active'} = $in{'active'};
|
||||
}
|
||||
$slice->{'active'} = $in{'active'} if (defined $in{'active'});
|
||||
|
||||
# Apply active flag for MBR disks via gpart set/unset when it changed
|
||||
my $base = $disk->{'device'}; $base =~ s{^/dev/}{};
|
||||
my $ds = get_disk_structure($base);
|
||||
if (is_using_gpart() && $ds && $ds->{'scheme'} && $ds->{'scheme'} !~ /GPT/i) {
|
||||
my $idx = slice_number($slice);
|
||||
if (defined $oldslice->{'active'} && defined $slice->{'active'} && $oldslice->{'active'} != $slice->{'active'}) {
|
||||
my $cmd = $slice->{'active'} ? "gpart set -a active -i $idx $base" : "gpart unset -a active -i $idx $base";
|
||||
my $out = `$cmd 2>&1`;
|
||||
if ($? != 0) {
|
||||
&error("Failed to change active flag: $out");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $err = &modify_slice($disk, $oldslice, $slice);
|
||||
&error($err) if ($err);
|
||||
|
||||
|
||||
@@ -16,6 +16,29 @@ $disk || &error($text{'disk_egone'});
|
||||
|
||||
&ui_print_header($disk->{'desc'}, $text{'nslice_title'}, "");
|
||||
|
||||
# Determine scheme for read-only behavior and note
|
||||
my $base_device = $disk->{'device'}; $base_device =~ s{^/dev/}{};
|
||||
my $disk_structure = get_disk_structure($base_device);
|
||||
my $is_gpt = (is_using_gpart() && $disk_structure && ($disk_structure->{'scheme'}||'') =~ /GPT/i);
|
||||
|
||||
# Check if there is any free space on the device
|
||||
my $has_free_space = 0;
|
||||
if ($disk_structure && $disk_structure->{'entries'}) {
|
||||
foreach my $entry (@{$disk_structure->{'entries'}}) {
|
||||
if ($entry->{'type'} eq 'free' && $entry->{'size'} > 0) {
|
||||
$has_free_space = 1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# If no free space, show error and return
|
||||
if (!$has_free_space) {
|
||||
print "<p><b>$text{'nslice_enospace'}</b></p>\n";
|
||||
&ui_print_footer("edit_disk.cgi?device=$in{'device'}", $text{'disk_return'});
|
||||
exit;
|
||||
}
|
||||
|
||||
print &ui_form_start("create_slice.cgi", "post");
|
||||
print &ui_hidden("device", $in{'device'});
|
||||
print &ui_table_start($text{'nslice_header'}, undef, 2);
|
||||
@@ -26,37 +49,86 @@ my $n = 1;
|
||||
while($used{$n}) {
|
||||
$n++;
|
||||
}
|
||||
print &ui_table_row($text{'nslice_number'},
|
||||
&ui_textbox("number", $n, 6));
|
||||
|
||||
# Disk size in blocks
|
||||
print &ui_table_row($text{'nslice_diskblocks'},
|
||||
$disk->{'blocks'});
|
||||
|
||||
# Start and end blocks (defaults to last slice+1)
|
||||
my ($start, $end) = (63, $disk->{'blocks'});
|
||||
foreach my $s (sort { $a->{'startblock'} cmp $b->{'startblock'} }
|
||||
my $num_field = $is_gpt
|
||||
? "<input type='text' name='number' value='".&html_escape($n)."' size='6' readonly> <span style='color:#666;font-style:italic'>".$text{'nslice_autonext'}."</span>"
|
||||
: &ui_textbox("number", $n, 6);
|
||||
print &ui_table_row($text{'nslice_number'}, $num_field);
|
||||
|
||||
# Disk size in blocks (prefer GPART total blocks)
|
||||
my $disk_blocks = ($disk_structure && $disk_structure->{'total_blocks'}) ? $disk_structure->{'total_blocks'} : ($disk->{'blocks'} || 0);
|
||||
print &ui_table_row($text{'nslice_diskblocks'}, $disk_blocks);
|
||||
|
||||
# Start and end blocks (defaults to last slice+1). Allow prefill from query.
|
||||
my ($start, $end) = (2048, $disk_blocks > 0 ? $disk_blocks - 1 : 0);
|
||||
foreach my $s (sort { $a->{'startblock'} <=> $b->{'startblock'} }
|
||||
@{$disk->{'slices'}}) {
|
||||
$start = $s->{'startblock'} + $s->{'blocks'} + 1;
|
||||
}
|
||||
$start = $s->{'startblock'} + $s->{'blocks'}; # leave 1 block (512B) gap
|
||||
}
|
||||
if (defined $in{'start'} && $in{'start'} =~ /^\d+$/) { $start = $in{'start'}; }
|
||||
if (defined $in{'end'} && $in{'end'} =~ /^\d+$/) { $end = $in{'end'}; }
|
||||
print &ui_table_row($text{'nslice_start'},
|
||||
&ui_textbox("start", $start, 10));
|
||||
&ui_textbox("start", $start, 10));
|
||||
print &ui_table_row($text{'nslice_end'},
|
||||
&ui_textbox("end", $end, 10));
|
||||
|
||||
&ui_textbox("end", $end, 10));
|
||||
|
||||
# Slice type
|
||||
print &ui_table_row($text{'nslice_type'},
|
||||
&ui_select("type", 'a5',
|
||||
[ sort { $a->[1] cmp $b->[1] }
|
||||
map { [ $_, &fdisk::tag_name($_) ] }
|
||||
&fdisk::list_tags() ]));
|
||||
|
||||
# Also create partition?
|
||||
print &ui_table_row($text{'nslice_makepart'},
|
||||
&ui_yesno_radio("makepart", 1));
|
||||
if (is_using_gpart()) {
|
||||
my $scheme = ($disk_structure && $disk_structure->{'scheme'}) ? $disk_structure->{'scheme'} : 'GPT';
|
||||
my $default_stype = ($scheme =~ /GPT/i) ? 'freebsd-zfs' : 'freebsd';
|
||||
print &ui_table_row($text{'nslice_type'},
|
||||
&ui_select("type", $default_stype,
|
||||
[ list_partition_types($scheme) ]));
|
||||
}
|
||||
else {
|
||||
print &ui_table_row($text{'nslice_type'},
|
||||
&ui_select("type", 'a5',
|
||||
[ sort { $a->[1] cmp $b->[1] }
|
||||
map { [ $_, &fdisk::tag_name($_) ] }
|
||||
&fdisk::list_tags() ]));
|
||||
}
|
||||
|
||||
# Also create partition? (only for MBR slices with BSD disklabel support)
|
||||
if (!$is_gpt) {
|
||||
print &ui_table_row($text{'slice_add'},
|
||||
&ui_yesno_radio("makepart", 1));
|
||||
}
|
||||
|
||||
print &ui_table_end();
|
||||
print &ui_form_end([ [ undef, $text{'create'} ] ]);
|
||||
print &ui_form_end([ [ undef, $text{'save'} ] ]);
|
||||
|
||||
# Existing slices summary
|
||||
print &ui_hr();
|
||||
print &ui_columns_start([$text{'disk_no'}, $text{'disk_type'}, $text{'disk_start'}, $text{'disk_end'}, $text{'disk_size'}], $text{'nslice_existing_header'});
|
||||
foreach my $s (sort { $a->{'number'} <=> $b->{'number'} } @{$disk->{'slices'}}) {
|
||||
my $stype = get_type_description($s->{'type'}) || $s->{'type'};
|
||||
my $szb = bytes_from_blocks($s->{'device'}, $s->{'blocks'});
|
||||
my $sz = defined $szb ? safe_nice_size($szb) : '-';
|
||||
print &ui_columns_row([
|
||||
$s->{'number'},
|
||||
$stype,
|
||||
$s->{'startblock'},
|
||||
$s->{'startblock'} + $s->{'blocks'} - 1,
|
||||
$sz,
|
||||
]);
|
||||
}
|
||||
print &ui_columns_end();
|
||||
|
||||
# Existing partitions summary
|
||||
my @parts_rows;
|
||||
foreach my $s (sort { $a->{'number'} <=> $b->{'number'} } @{$disk->{'slices'}}) {
|
||||
next unless @{$s->{'parts'}||[]};
|
||||
foreach my $p (@{$s->{'parts'}}) {
|
||||
my $ptype = get_type_description($p->{'type'}) || $p->{'type'};
|
||||
my $pb = bytes_from_blocks($p->{'device'}, $p->{'blocks'});
|
||||
my $psz = defined $pb ? safe_nice_size($pb) : '-';
|
||||
push @parts_rows, [ $s->{'number'}, uc($p->{'letter'}), $ptype, $p->{'startblock'}, $p->{'startblock'} + $p->{'blocks'} - 1, $psz ];
|
||||
}
|
||||
}
|
||||
if (@parts_rows) {
|
||||
print &ui_columns_start(['Slice', $text{'slice_letter'}, $text{'slice_type'}, $text{'slice_start'}, $text{'slice_end'}, $text{'slice_size'}], $text{'nslice_existing_parts'});
|
||||
foreach my $row (@parts_rows) { print &ui_columns_row($row); }
|
||||
print &ui_columns_end();
|
||||
}
|
||||
|
||||
&ui_print_footer("edit_disk.cgi?device=$in{'device'}",
|
||||
$text{'disk_return'});
|
||||
$text{'disk_return'});
|
||||
31
bsdfdisk/smart.cgi
Normal file
31
bsdfdisk/smart.cgi
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Show SMART status for a given device
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
&ReadParse();
|
||||
|
||||
# Validate device param
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/ or &error($text{'disk_edevice'} || 'Invalid device');
|
||||
$in{'device'} !~ /\.\./ or &error($text{'disk_edevice'} || 'Invalid device');
|
||||
|
||||
# Check smartctl availability
|
||||
&has_command('smartctl') or &error($text{'index_ecmd'} ? &text('index_ecmd','smartctl') : 'smartctl not available');
|
||||
|
||||
my $device = $in{'device'};
|
||||
my $dev_html = &html_escape($device);
|
||||
|
||||
&ui_print_header($dev_html, $text{'disk_smart'} || 'SMART Status', "");
|
||||
|
||||
print "<div class='panel panel-default'>\n";
|
||||
print "<div class='panel-heading'><h3 class='panel-title'>SMART status for <tt>$dev_html</tt></h3></div>\n";
|
||||
print "<div class='panel-body'>\n";
|
||||
my $cmd = "smartctl -a " . "e_path($device) . " 2>&1";
|
||||
my $out = &backquote_command($cmd);
|
||||
print "<pre>" . &html_escape("Command: $cmd\n\n$out") . "</pre>\n";
|
||||
print "</div></div>\n";
|
||||
|
||||
&ui_print_footer("edit_disk.cgi?device=".&urlize($device), $text{'disk_return'});
|
||||
Reference in New Issue
Block a user