mirror of
https://github.com/webmin/webmin.git
synced 2026-02-09 08:42:20 +00:00
286 lines
9.9 KiB
Perl
Executable File
286 lines
9.9 KiB
Perl
Executable File
#!/usr/local/bin/perl
|
|
use strict;
|
|
use warnings;
|
|
no warnings 'redefine';
|
|
no warnings 'uninitialized';
|
|
require './bsdfdisk-lib.pl';
|
|
our ( %in, %text, $module_name );
|
|
ReadParse();
|
|
my $extwidth = 300;
|
|
|
|
# Get the disk and slice
|
|
my @disks = list_disks_partitions();
|
|
|
|
# Validate input parameters to prevent command injection
|
|
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/ or error( $text{'disk_edevice'} );
|
|
$in{'device'} !~ /\.\./ or error( $text{'disk_edevice'} );
|
|
$in{'slice'} =~ /^\d+$/ or error( $text{'slice_egone'} );
|
|
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'} );
|
|
|
|
my $url_device = &urlize( $in{'device'} );
|
|
my $url_slice = &urlize( $in{'slice'} );
|
|
ui_print_header( $slice->{'desc'}, $text{'slice_title'}, "" );
|
|
|
|
# Show slice details
|
|
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);
|
|
|
|
# Device label (GPT label or glabel)
|
|
my $slice_label = get_device_label_name(
|
|
disk => $disk,
|
|
slice => $slice,
|
|
disk_structure => $disk_structure
|
|
);
|
|
|
|
# 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;
|
|
my $confirm_msg = $text{'confirm_overwrite'}
|
|
|| 'You will destroy/overwrite existing data structures. Continue?';
|
|
my $confirm_js = $confirm_msg;
|
|
$confirm_js =~ s/\\/\\\\/g;
|
|
$confirm_js =~ s/'/\\'/g;
|
|
$confirm_js =~ s/\r?\n/\\n/g;
|
|
print ui_form_start( "save_slice.cgi", "post", undef,
|
|
"onsubmit=\"return confirm('$confirm_js')\"" );
|
|
print $hiddens;
|
|
print ui_table_start( $text{'slice_header'}, undef, 2 );
|
|
print ui_table_row( $text{'part_device'},
|
|
"<tt>" . html_escape( $slice->{'device'} ) . "</tt>" );
|
|
print ui_table_row( $text{'slice_label'},
|
|
$slice_label ? "<tt>" . html_escape($slice_label) . "</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 {
|
|
# 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="
|
|
. $url_device
|
|
. "&slice=$url_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="
|
|
. $url_device
|
|
. "&slice="
|
|
. $url_slice
|
|
. "&part="
|
|
. &urlize( $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'>"
|
|
. html_escape( $fmt || get_format_type($p) ) . "</a>",
|
|
$ext,
|
|
( $psz_b ? safe_nice_size($psz_b) : '-' ),
|
|
$p->{'startblock'},
|
|
$p->{'startblock'} + $p->{'blocks'} - 1,
|
|
$stripesize,
|
|
$use_txt,
|
|
html_escape($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'} } ) {
|
|
my $mount_return =
|
|
"edit_slice.cgi?device=$url_device&slice=$url_slice";
|
|
show_filesystem_buttons( $hiddens, \@slice_status, $slice,
|
|
$mount_return );
|
|
}
|
|
print ui_buttons_row(
|
|
'change_slice_label.cgi',
|
|
$text{'slice_chglabel'},
|
|
$text{'slice_chglabeldesc'},
|
|
ui_hidden( "device", $in{'device'} ) . "\n"
|
|
. ui_hidden( "slice", $in{'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();
|
|
}
|
|
|
|
ui_print_footer( "edit_disk.cgi?device=$url_device", $text{'disk_return'} );
|