#!/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'} ); 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'}, "" . html_escape( $slice->{'device'} ) . "" ); print ui_table_row( $text{'slice_label'}, $slice_label ? "" . html_escape($slice_label) . "" : "-" ); 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', html_escape($slice_use) ) : text( 'part_foruse', html_escape($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 ? ( "" . $text{'slice_add'} . "" ) : (); 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( "", int( $scale * ( $p->{'startblock'} - 1 ) ) ); my $img_type = $p->{'extended'} ? "ext" : "use"; my $partition_img = sprintf( "", $img_type, int( $scale * $p->{'blocks'} ) ); my $gap_after = sprintf( "", 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( [ "" . uc( $p->{'letter'} ) . "", "" . html_escape( $fmt || get_format_type($p) ) . "", $ext, ( $psz_b ? safe_nice_size($psz_b) : '-' ), $p->{'startblock'}, $p->{'startblock'} + $p->{'blocks'} - 1, $stripesize, html_escape($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 "$text{'slice_none2'}

\n"; } else { print "$text{'slice_none'}

\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=$in{'device'}&slice=$in{'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=$in{'device'}", $text{'disk_return'} );