| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| |
| /* |
| * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved. |
| * Copyright 2018 OmniOS Community Edition (OmniOSce) Association. |
| */ |
| |
| /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ |
| /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ |
| /* All Rights Reserved */ |
| |
| /* Copyright (c) 1987, 1988 Microsoft Corporation */ |
| /* All Rights Reserved */ |
| |
| /* |
| * PROGRAM: fdisk(1M) |
| * This program reads the partition table on the specified device and |
| * also reads the drive parameters. The user can perform various |
| * operations from a supplied menu or from the command line. Diagnostic |
| * options are also available. |
| */ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <ctype.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <limits.h> |
| #include <sys/param.h> |
| #include <sys/systeminfo.h> |
| #include <sys/efi_partition.h> |
| #include <sys/byteorder.h> |
| #include <sys/systeminfo.h> |
| |
| #include <sys/dktp/fdisk.h> |
| #include <sys/dkio.h> |
| #include <sys/vtoc.h> |
| #ifdef i386 |
| #include <sys/tty.h> |
| #include <libfdisk.h> |
| #endif |
| |
| #define CLR_SCR "[1;1H[0J" |
| #define CLR_LIN "[0K" |
| #define HOME "[1;1H[0K[2;1H[0K[3;1H[0K[4;1H[0K[5;1H[0K" \ |
| "[6;1H[0K[7;1H[0K[8;1H[0K[9;1H[0K[10;1H[0K[1;1H" |
| #define Q_LINE "[22;1H[0K[21;1H[0K[20;1H[0K" |
| |
| #ifdef i386 |
| #define W_LINE "[11;1H[0K" |
| #else |
| #define W_LINE "[12;1H[0K[11;1H[0K" |
| #endif |
| |
| #define E_LINE "[24;1H[0K[23;1H[0K" |
| |
| #ifdef i386 |
| #define M_LINE "[12;1H[0K[13;1H[0K[14;1H[0K[15;1H[0K" \ |
| "[16;1H[0K[17;1H[0K[18;1H[0K[19;1H[0K[12;1H" |
| #else |
| #define M_LINE "[13;1H[0K[14;1H[0K[15;1H[0K[16;1H[0K[17;1H" \ |
| "[0K[18;1H[0K[19;1H[0K[13;1H" |
| #endif |
| |
| #define T_LINE "[1;1H[0K" |
| |
| #define DEFAULT_PATH "/dev/rdsk/" |
| |
| #define DK_MAX_2TB UINT32_MAX /* Max # of sectors in 2TB */ |
| |
| /* for clear_vtoc() */ |
| #define OLD 0 |
| #define NEW 1 |
| |
| /* readvtoc/writevtoc return codes */ |
| #define VTOC_OK 0 /* Good VTOC */ |
| #define VTOC_INVAL 1 /* invalid VTOC */ |
| #define VTOC_NOTSUP 2 /* operation not supported - EFI label */ |
| #define VTOC_RWERR 3 /* couldn't read or write VTOC */ |
| |
| /* |
| * Support for fdisk(1M) on the SPARC platform |
| * In order to convert little endian values to big endian for SPARC, |
| * byte/short and long values must be swapped. |
| * These swapping macros will be used to access information in the |
| * mboot and ipart structures. |
| */ |
| |
| #ifdef sparc |
| #define les(val) ((((val)&0xFF)<<8)|(((val)>>8)&0xFF)) |
| #define lel(val) (((unsigned)(les((val)&0x0000FFFF))<<16) | \ |
| (les((unsigned)((val)&0xffff0000)>>16))) |
| #else |
| #define les(val) (val) |
| #define lel(val) (val) |
| #endif |
| |
| #if defined(_SUNOS_VTOC_16) |
| #define VTOC_OFFSET 1 |
| #elif defined(_SUNOS_VTOC_8) |
| #define VTOC_OFFSET 0 |
| #else |
| #error No VTOC format defined. |
| #endif |
| |
| #ifdef i386 |
| #define FDISK_KB (1024) |
| #define FDISK_MB (FDISK_KB * 1024) |
| #define FDISK_GB (FDISK_MB * 1024) |
| #define TRUE 1 |
| |
| #define FDISK_MAX_VALID_PART_ID 255 |
| #define FDISK_MAX_VALID_PART_NUM_DIGITS 2 |
| #define FDISK_MAX_VALID_PART_ID_DIGITS 3 |
| |
| /* Maximum number of digits for a valid partition size */ |
| #define FDISK_MAX_VALID_CYL_NUM_DIGITS 10 |
| |
| /* Minimum partition size in cylinders */ |
| #define FDISK_MIN_PART_SIZE 1 |
| #endif |
| |
| static char Usage[] = "Usage: fdisk\n" |
| "[ -A id:act:bhead:bsect:bcyl:ehead:esect:ecyl:rsect:numsect ]\n" |
| "[ -b masterboot ]\n" |
| "[ -D id:act:bhead:bsect:bcyl:ehead:esect:ecyl:rsect:numsect ]\n" |
| "[ -E [slot:active] ]\n" |
| "[ -F fdisk_file ] [ -h ] [ -o offset ] [ -P fill_patt ] [ -s size ]\n" |
| "[ -S geom_file ] [ [ -v ] -W { creat_fdisk_file | - } ]\n" |
| "[ -w | r | d | n | I | B | g | G | R | t | T ] rdevice"; |
| |
| static char Usage1[] = " Partition options:\n" |
| " -A id:act:bhead:bsect:bcyl:ehead:esect:ecyl:rsect:numsect\n" |
| " Create a partition with specific attributes:\n" |
| " id = system id number (fdisk.h) for the partition type\n" |
| " act = active partition flag (0 is off and 128 is on)\n" |
| " bhead = beginning head for start of partition\n" |
| " bsect = beginning sector for start of partition\n" |
| " bcyl = beginning cylinder for start of partition\n" |
| " ehead = ending head for end of partition\n" |
| " esect = ending sector for end of partition\n" |
| " ecyl = ending cylinder for end of partition\n" |
| " rsect = sector number from start of disk for\n" |
| " start of partition\n" |
| " numsect = partition size in sectors\n" |
| " -b master_boot\n" |
| " Use master_boot as the master boot file.\n" |
| " -B Create one Solaris partition that uses the entire disk.\n" |
| " -D id:act:bhead:bsect:bcyl:ehead:esect:ecyl:rsect:numsect\n" |
| " Delete a partition. See attribute definitions for -A.\n" |
| " -E [slot:active]\n" |
| " Create an EFI partition that uses the entire disk.\n" |
| " -F fdisk_file\n" |
| " Use fdisk_file to initialize on-line fdisk table.\n" |
| " -I Forego device checks. Generate a file image of what would go\n" |
| " on a disk using the geometry specified with the -S option.\n" |
| " -n Do not run in interactive mode.\n" |
| " -R Open the disk device as read-only.\n" |
| " -t Check and adjust VTOC to be consistent with fdisk table.\n" |
| " VTOC slices exceeding the partition size will be truncated.\n" |
| " -T Check and adjust VTOC to be consistent with fdisk table.\n" |
| " VTOC slices exceeding the partition size will be removed.\n" |
| " -W fdisk_file\n" |
| " Write on-disk table to fdisk_file.\n" |
| " -W - Write on-disk table to standard output.\n" |
| " -v Display virtual geometry. Must be used with the -W option.\n" |
| " Diagnostic options:\n" |
| " -d Activate debug information about progress.\n" |
| " -g Write label geometry to standard output:\n" |
| " PCYL number of physical cylinders\n" |
| " NCYL number of usable cylinders\n" |
| " ACYL number of alternate cylinders\n" |
| " BCYL cylinder offset\n" |
| " NHEADS number of heads\n" |
| " NSECTORS number of sectors per track\n" |
| " SECTSIZ size of a sector in bytes\n" |
| " -G Write physical geometry to standard output (see -g).\n" |
| " -h Issue this verbose help message.\n" |
| " -o offset\n" |
| " Block offset from start of disk (default 0). Ignored if\n" |
| " -P # specified.\n" |
| " -P fill_patt\n" |
| " Fill disk with pattern fill_patt. fill_patt can be decimal or\n" |
| " hexadecimal and is used as number for constant long word\n" |
| " pattern. If fill_patt is \"#\" then pattern of block #\n" |
| " for each block. Pattern is put in each block as long words\n" |
| " and fills each block (see -o and -s).\n" |
| " -r Read from a disk to stdout (see -o and -s).\n" |
| " -s size Number of blocks on which to perform operation (see -o).\n" |
| " -S geom_file\n" |
| " Use geom_file to set the label geometry (see -g).\n" |
| " -w Write to a disk from stdin (see -o and -s)."; |
| |
| static char Ostr[] = "Other OS"; |
| static char Dstr[] = "DOS12"; |
| static char D16str[] = "DOS16"; |
| static char DDstr[] = "DOS-DATA"; |
| static char EDstr[] = "EXT-DOS"; |
| static char DBstr[] = "DOS-BIG"; |
| static char PCstr[] = "PCIX"; |
| static char Ustr[] = "UNIX System"; |
| static char SUstr[] = "Solaris"; |
| static char SU2str[] = "Solaris2"; |
| static char X86str[] = "x86 Boot"; |
| static char DIAGstr[] = "Diagnostic"; |
| static char IFSstr[] = "IFS: NTFS"; |
| static char AIXstr[] = "AIX Boot"; |
| static char AIXDstr[] = "AIX Data"; |
| static char OS2str[] = "OS/2 Boot"; |
| static char WINstr[] = "Win95 FAT32"; |
| static char EWINstr[] = "Ext Win95"; |
| static char FAT95str[] = "FAT16 LBA"; |
| static char EXTLstr[] = "EXT LBA"; |
| static char LINUXstr[] = "Linux"; |
| static char CPMstr[] = "CP/M"; |
| static char NOV2str[] = "Netware 286"; |
| static char NOVstr[] = "Netware 3.x+"; |
| static char QNXstr[] = "QNX 4.x"; |
| static char QNX2str[] = "QNX part 2"; |
| static char QNX3str[] = "QNX part 3"; |
| static char LINNATstr[] = "Linux native"; |
| #ifdef i386 |
| static char LINSWAPstr[] = "Linux swap"; |
| #endif |
| static char NTFSVOL1str[] = "NT volset 1"; |
| static char NTFSVOL2str[] = "NT volset 2"; |
| static char BSDstr[] = "BSD OS"; |
| static char NEXTSTEPstr[] = "NeXTSTEP"; |
| static char BSDIFSstr[] = "BSDI FS"; |
| static char BSDISWAPstr[] = "BSDI swap"; |
| static char Actvstr[] = "Active"; |
| static char EFIstr[] = "EFI"; |
| static char NAstr[] = " "; |
| |
| /* All the user options and flags */ |
| static char *Dfltdev; /* name of fixed disk drive */ |
| |
| /* Diagnostic options */ |
| static int io_wrt = 0; /* write stdin to disk (-w) */ |
| static int io_rd = 0; /* read disk and write stdout (-r) */ |
| static char *io_fatt; /* user supplied pattern (-P pattern) */ |
| static int io_patt = 0; /* write pattern to disk (-P pattern) */ |
| static int io_lgeom = 0; /* get label geometry (-g) */ |
| static int io_pgeom = 0; /* get drive physical geometry (-G) */ |
| static char *io_sgeom = 0; /* set label geometry (-S geom_file) */ |
| static int io_readonly = 0; /* do not write to disk (-R) */ |
| |
| /* The -o offset and -s size options specify the area of the disk on */ |
| /* which to perform the particular operation; i.e., -P, -r, or -w. */ |
| static off_t io_offset = 0; /* offset sector (-o offset) */ |
| static off_t io_size = 0; /* size in sectors (-s size) */ |
| |
| /* Partition table flags */ |
| static int v_flag = 0; /* virtual geometry-HBA flag (-v) */ |
| static int stdo_flag = 0; /* stdout flag (-W -) */ |
| static int io_fdisk = 0; /* do fdisk operation */ |
| static int io_ifdisk = 0; /* interactive partition */ |
| static int io_nifdisk = 0; /* non-interactive partition (-n) */ |
| |
| static int io_adjt = 0; /* check/adjust VTOC (truncate (-t)) */ |
| static int io_ADJT = 0; /* check/adjust VTOC (delete (-T)) */ |
| static char *io_ffdisk = 0; /* input fdisk file name (-F file) */ |
| static char *io_Wfdisk = 0; /* output fdisk file name (-W file) */ |
| static char *io_Afdisk = 0; /* add entry to partition table (-A) */ |
| static char *io_Dfdisk = 0; /* delete entry from part. table (-D) */ |
| |
| static char *io_mboot = 0; /* master boot record (-b boot_file) */ |
| |
| static struct mboot BootCod; /* buffer for master boot record */ |
| |
| static int io_wholedisk = 0; /* use whole disk for Solaris (-B) */ |
| static int io_EFIdisk = 0; /* use whole disk for EFI (-E) */ |
| static int io_EFIslot = 0; /* slot in which to place EFI entry */ |
| static int io_EFIactive = 0; /* mark EFI entry as active */ |
| static int io_debug = 0; /* activate verbose mode (-d) */ |
| static int io_image = 0; /* create image using geometry (-I) */ |
| |
| static struct mboot *Bootblk; /* pointer to cut/paste sector zero */ |
| static char *Bootsect; /* pointer to sector zero buffer */ |
| static char *Nullsect; |
| static struct extvtoc disk_vtoc; /* verify VTOC table */ |
| static int vt_inval = 0; |
| static int no_virtgeom_ioctl = 0; /* ioctl for virtual geometry failed */ |
| static int no_physgeom_ioctl = 0; /* ioctl for physical geometry failed */ |
| |
| static struct ipart Table[FD_NUMPART]; |
| static struct ipart Old_Table[FD_NUMPART]; |
| static int skip_verify[FD_NUMPART]; /* special case skip sz chk */ |
| |
| /* Disk geometry information */ |
| static struct dk_minfo minfo; |
| static struct dk_geom disk_geom; |
| |
| static int Dev; /* fd for open device */ |
| |
| static diskaddr_t dev_capacity; /* number of blocks on device */ |
| static diskaddr_t chs_capacity; /* Numcyl_usable * heads * sectors */ |
| |
| static int Numcyl_usable; /* Number of usable cylinders */ |
| /* used to limit fdisk to 2TB */ |
| |
| /* Physical geometry for the drive */ |
| static int Numcyl; /* number of cylinders */ |
| static int heads; /* number of heads */ |
| static int sectors; /* number of sectors per track */ |
| static int acyl; /* number of alternate sectors */ |
| |
| /* HBA (virtual) geometry for the drive */ |
| static int hba_Numcyl; /* number of cylinders */ |
| static int hba_heads; /* number of heads */ |
| static int hba_sectors; /* number of sectors per track */ |
| |
| static int sectsiz; /* sector size */ |
| |
| /* Load functions for fdisk table modification */ |
| #define LOADFILE 0 /* load fdisk from file */ |
| #define LOADDEL 1 /* delete an fdisk entry */ |
| #define LOADADD 2 /* add an fdisk entry */ |
| |
| #define CBUFLEN 80 |
| static char s[CBUFLEN]; |
| |
| #ifdef i386 |
| /* |
| * Complete list of all the 255 partition types. Some are unknown types |
| * and some entries are known to be unused. |
| * |
| * Courtesy of http://www.win.tue.nl/~aeb/partitions/partition_types-1.html |
| */ |
| char *fdisk_part_types[] = { |
| "Empty", /* 0 */ |
| "FAT12", /* 1 */ |
| "XENIX /", /* 2 */ |
| "XENIX /usr", /* 3 */ |
| "FAT16 (Upto 32M)", /* 4 */ |
| "DOS Extended", /* 5 */ |
| "FAT16 (>32M, HUGEDOS)", /* 6 */ |
| "IFS: NTFS", /* 7 */ |
| "AIX Boot/QNX(qny)", /* 8 */ |
| "AIX Data/QNX(qnz)", /* 9 */ |
| "OS/2 Boot/Coherent swap", /* 10 */ |
| "WIN95 FAT32(Upto 2047GB)", /* 11 */ |
| "WIN95 FAT32(LBA)", /* 12 */ |
| "Unused", /* 13 */ |
| "WIN95 FAT16(LBA)", /* 14 */ |
| "WIN95 Extended(LBA)", /* 15 */ |
| "OPUS", /* 16 */ |
| "Hidden FAT12", /* 17 */ |
| "Diagnostic", /* 18 */ |
| "Unknown", /* 19 */ |
| "Hidden FAT16(Upto 32M)", /* 20 */ |
| "Unknown", /* 21 */ |
| "Hidden FAT16(>=32M)", /* 22 */ |
| "Hidden IFS: HPFS", /* 23 */ |
| "AST SmartSleep Partition", /* 24 */ |
| "Unused/Willowtech Photon", /* 25 */ |
| "Unknown", /* 26 */ |
| "Hidden FAT32", /* 27 */ |
| "Hidden FAT32(LBA)", /* 28 */ |
| "Unused", /* 29 */ |
| "Hidden FAT16(LBA)", /* 30 */ |
| "Unknown", /* 31 */ |
| "Unused/OSF1", /* 32 */ |
| "Reserved/FSo2(Oxygen FS)", /* 33 */ |
| "Unused/(Oxygen EXT)", /* 34 */ |
| "Reserved", /* 35 */ |
| "NEC DOS 3.x", /* 36 */ |
| "Unknown", /* 37 */ |
| "Reserved", /* 38 */ |
| "Unknown", /* 39 */ |
| "Unknown", /* 40 */ |
| "Unknown", /* 41 */ |
| "AtheOS File System", /* 42 */ |
| "SyllableSecure", /* 43 */ |
| "Unknown", /* 44 */ |
| "Unknown", /* 45 */ |
| "Unknown", /* 46 */ |
| "Unknown", /* 47 */ |
| "Unknown", /* 48 */ |
| "Reserved", /* 49 */ |
| "NOS", /* 50 */ |
| "Reserved", /* 51 */ |
| "Reserved", /* 52 */ |
| "JFS on OS/2", /* 53 */ |
| "Reserved", /* 54 */ |
| "Unknown", /* 55 */ |
| "THEOS 3.2 2GB", /* 56 */ |
| "Plan9/THEOS 4", /* 57 */ |
| "THEOS 4 4GB", /* 58 */ |
| "THEOS 4 Extended", /* 59 */ |
| "PartitionMagic Recovery", /* 60 */ |
| "Hidden NetWare", /* 61 */ |
| "Unknown", /* 62 */ |
| "Unknown", /* 63 */ |
| "Venix 80286", /* 64 */ |
| "MINIX/PPC PReP Boot", /* 65 */ |
| "Win2K Dynamic Disk/SFS(DOS)", /* 66 */ |
| "Linux+DRDOS shared", /* 67 */ |
| "GoBack partition", /* 68 */ |
| "Boot-US boot manager", /* 69 */ |
| "EUMEL/Elan", /* 70 */ |
| "EUMEL/Elan", /* 71 */ |
| "EUMEL/Elan", /* 72 */ |
| "Unknown", /* 73 */ |
| "ALFS/THIN FS for DOS", /* 74 */ |
| "Unknown", /* 75 */ |
| "Oberon partition", /* 76 */ |
| "QNX 4,x", /* 77 */ |
| "QNX 4,x 2nd Part", /* 78 */ |
| "QNX 4,x 3rd Part", /* 79 */ |
| "OnTrack DM R/O, Lynx RTOS", /* 80 */ |
| "OnTrack DM R/W, Novell", /* 81 */ |
| "CP/M", /* 82 */ |
| "Disk Manager 6.0 Aux3", /* 83 */ |
| "Disk Manager 6.0 DDO", /* 84 */ |
| "EZ-Drive", /* 85 */ |
| "Golden Bow VFeature/AT&T MS-DOS", /* 86 */ |
| "DrivePro", /* 87 */ |
| "Unknown", /* 88 */ |
| "Unknown", /* 89 */ |
| "Unknown", /* 90 */ |
| "Unknown", /* 91 */ |
| "Priam EDisk", /* 92 */ |
| "Unknown", /* 93 */ |
| "Unknown", /* 94 */ |
| "Unknown", /* 95 */ |
| "Unknown", /* 96 */ |
| "SpeedStor", /* 97 */ |
| "Unknown", /* 98 */ |
| "Unix SysV, Mach, GNU Hurd", /* 99 */ |
| "PC-ARMOUR, Netware 286", /* 100 */ |
| "Netware 386", /* 101 */ |
| "Netware SMS", /* 102 */ |
| "Novell", /* 103 */ |
| "Novell", /* 104 */ |
| "Netware NSS", /* 105 */ |
| "Unknown", /* 106 */ |
| "Unknown", /* 107 */ |
| "Unknown", /* 108 */ |
| "Unknown", /* 109 */ |
| "Unknown", /* 110 */ |
| "Unknown", /* 111 */ |
| "DiskSecure Multi-Boot", /* 112 */ |
| "Reserved", /* 113 */ |
| "Unknown", /* 114 */ |
| "Reserved", /* 115 */ |
| "Scramdisk partition", /* 116 */ |
| "IBM PC/IX", /* 117 */ |
| "Reserved", /* 118 */ |
| "M2FS/M2CS,Netware VNDI", /* 119 */ |
| "XOSL FS", /* 120 */ |
| "Unknown", /* 121 */ |
| "Unknown", /* 122 */ |
| "Unknown", /* 123 */ |
| "Unknown", /* 124 */ |
| "Unknown", /* 125 */ |
| "Unused", /* 126 */ |
| "Unused", /* 127 */ |
| "MINIX until 1.4a", /* 128 */ |
| "MINIX since 1.4b, early Linux", /* 129 */ |
| "Solaris/Linux swap", /* 130 */ |
| "Linux native", /* 131 */ |
| "OS/2 hidden,Win Hibernation", /* 132 */ |
| "Linux extended", /* 133 */ |
| "Old Linux RAID,NT FAT16 RAID", /* 134 */ |
| "NTFS volume set", /* 135 */ |
| "Linux plaintext part table", /* 136 */ |
| "Unknown", /* 137 */ |
| "Linux Kernel Partition", /* 138 */ |
| "Fault Tolerant FAT32 volume", /* 139 */ |
| "Fault Tolerant FAT32 volume", /* 140 */ |
| "Free FDISK hidden PDOS FAT12", /* 141 */ |
| "Linux LVM partition", /* 142 */ |
| "Unknown", /* 143 */ |
| "Free FDISK hidden PDOS FAT16", /* 144 */ |
| "Free FDISK hidden DOS EXT", /* 145 */ |
| "Free FDISK hidden FAT16 Large", /* 146 */ |
| "Hidden Linux native, Amoeba", /* 147 */ |
| "Amoeba Bad Block Table", /* 148 */ |
| "MIT EXOPC Native", /* 149 */ |
| "Unknown", /* 150 */ |
| "Free FDISK hidden PDOS FAT32", /* 151 */ |
| "Free FDISK hidden FAT32 LBA", /* 152 */ |
| "DCE376 logical drive", /* 153 */ |
| "Free FDISK hidden FAT16 LBA", /* 154 */ |
| "Free FDISK hidden DOS EXT", /* 155 */ |
| "Unknown", /* 156 */ |
| "Unknown", /* 157 */ |
| "Unknown", /* 158 */ |
| "BSD/OS", /* 159 */ |
| "Laptop hibernation", /* 160 */ |
| "Laptop hibernate,HP SpeedStor", /* 161 */ |
| "Unknown", /* 162 */ |
| "HP SpeedStor", /* 163 */ |
| "HP SpeedStor", /* 164 */ |
| "BSD/386,386BSD,NetBSD,FreeBSD", /* 165 */ |
| "OpenBSD,HP SpeedStor", /* 166 */ |
| "NeXTStep", /* 167 */ |
| "Mac OS-X", /* 168 */ |
| "NetBSD", /* 169 */ |
| "Olivetti FAT12 1.44MB Service", /* 170 */ |
| "Mac OS-X Boot", /* 171 */ |
| "Unknown", /* 172 */ |
| "Unknown", /* 173 */ |
| "ShagOS filesystem", /* 174 */ |
| "ShagOS swap", /* 175 */ |
| "BootStar Dummy", /* 176 */ |
| "HP SpeedStor", /* 177 */ |
| "Unknown", /* 178 */ |
| "HP SpeedStor", /* 179 */ |
| "HP SpeedStor", /* 180 */ |
| "Unknown", /* 181 */ |
| "Corrupted FAT16 NT Mirror Set", /* 182 */ |
| "Corrupted NTFS NT Mirror Set", /* 183 */ |
| "Old BSDI BSD/386 swap", /* 184 */ |
| "Unknown", /* 185 */ |
| "Unknown", /* 186 */ |
| "Boot Wizard hidden", /* 187 */ |
| "Unknown", /* 188 */ |
| "Unknown", /* 189 */ |
| "Solaris x86 boot", /* 190 */ |
| "Solaris2", /* 191 */ |
| "REAL/32 or Novell DOS secured", /* 192 */ |
| "DRDOS/secured(FAT12)", /* 193 */ |
| "Hidden Linux", /* 194 */ |
| "Hidden Linux swap", /* 195 */ |
| "DRDOS/secured(FAT16,< 32M)", /* 196 */ |
| "DRDOS/secured(Extended)", /* 197 */ |
| "NT corrupted FAT16 volume", /* 198 */ |
| "NT corrupted NTFS volume", /* 199 */ |
| "DRDOS8.0+", /* 200 */ |
| "DRDOS8.0+", /* 201 */ |
| "DRDOS8.0+", /* 202 */ |
| "DRDOS7.04+ secured FAT32(CHS)", /* 203 */ |
| "DRDOS7.04+ secured FAT32(LBA)", /* 204 */ |
| "CTOS Memdump", /* 205 */ |
| "DRDOS7.04+ FAT16X(LBA)", /* 206 */ |
| "DRDOS7.04+ secure EXT DOS(LBA)", /* 207 */ |
| "REAL/32 secure big, MDOS", /* 208 */ |
| "Old MDOS secure FAT12", /* 209 */ |
| "Unknown", /* 210 */ |
| "Unknown", /* 211 */ |
| "Old MDOS secure FAT16 <32M", /* 212 */ |
| "Old MDOS secure EXT", /* 213 */ |
| "Old MDOS secure FAT16 >=32M", /* 214 */ |
| "Unknown", /* 215 */ |
| "CP/M-86", /* 216 */ |
| "Unknown", /* 217 */ |
| "Non-FS Data", /* 218 */ |
| "CP/M,Concurrent DOS,CTOS", /* 219 */ |
| "Unknown", /* 220 */ |
| "Hidden CTOS memdump", /* 221 */ |
| "Dell PowerEdge utilities(FAT)", /* 222 */ |
| "DG/UX virtual disk manager", /* 223 */ |
| "ST AVFS(STMicroelectronics)", /* 224 */ |
| "SpeedStor 12-bit FAT EXT", /* 225 */ |
| "Unknown", /* 226 */ |
| "SpeedStor", /* 227 */ |
| "SpeedStor 16-bit FAT EXT", /* 228 */ |
| "Tandy MSDOS", /* 229 */ |
| "Storage Dimensions SpeedStor", /* 230 */ |
| "Unknown", /* 231 */ |
| "Unknown", /* 232 */ |
| "Unknown", /* 233 */ |
| "Unknown", /* 234 */ |
| "BeOS BFS", /* 235 */ |
| "SkyOS SkyFS", /* 236 */ |
| "Unused", /* 237 */ |
| "EFI Header Indicator", /* 238 */ |
| "EFI Filesystem", /* 239 */ |
| "Linux/PA-RISC boot loader", /* 240 */ |
| "SpeedStor", /* 241 */ |
| "DOS 3.3+ secondary", /* 242 */ |
| "SpeedStor Reserved", /* 243 */ |
| "SpeedStor Large", /* 244 */ |
| "Prologue multi-volume", /* 245 */ |
| "SpeedStor", /* 246 */ |
| "Unused", /* 247 */ |
| "Unknown", /* 248 */ |
| "pCache", /* 249 */ |
| "Bochs", /* 250 */ |
| "VMware File System", /* 251 */ |
| "VMware swap", /* 252 */ |
| "Linux raid autodetect", /* 253 */ |
| "NT Disk Administrator hidden", /* 254 */ |
| "Xenix Bad Block Table" /* 255 */ |
| }; |
| |
| /* Allowed extended partition menu options */ |
| static char ext_part_menu_opts[] = "adhipr"; |
| |
| /* |
| * Structure holding all information about the extended partition |
| * NOTE : As of now, there will be just one instance of ext_part_t, since most |
| * known systems allow only one extended dos partition per disk. |
| */ |
| static ext_part_t *epp; |
| #endif |
| |
| static void update_disk_and_exit(boolean_t table_changed); |
| int main(int argc, char *argv[]); |
| static int read_geom(char *sgeom); |
| static void dev_mboot_read(void); |
| static void dev_mboot_write(off_t sect, char *buff, int bootsiz); |
| static void mboot_read(void); |
| static void fill_patt(void); |
| static void abs_read(void); |
| static void abs_write(void); |
| static void load(int funct, char *file); |
| static void Set_Table_CHS_Values(int ti); |
| static int nopartdefined(); |
| static int insert_tbl(int id, int act, |
| int bhead, int bsect, int bcyl, |
| int ehead, int esect, int ecyl, |
| uint32_t rsect, uint32_t numsect, int startindex); |
| static int entry_from_old_table(int id, int act, |
| int bhead, int bsect, int bcyl, |
| int ehead, int esect, int ecyl, |
| uint32_t rsect, uint32_t numsect, int startindex); |
| static int verify_tbl(void); |
| static int pars_fdisk(char *line, |
| int *id, int *act, |
| int *bhead, int *bsect, int *bcyl, |
| int *ehead, int *esect, int *ecyl, |
| uint32_t *rsect, uint32_t *numsect); |
| static int validate_part(int id, uint32_t rsect, uint32_t numsect); |
| static void stage0(void); |
| static int pcreate(void); |
| static int specify(uchar_t tsystid); |
| static void dispmenu(void); |
| static int pchange(void); |
| static int ppartid(void); |
| static char pdelete(void); |
| static void rm_blanks(char *s); |
| static int getcyl(void); |
| static void disptbl(void); |
| static void print_Table(void); |
| static void copy_Table_to_Old_Table(void); |
| static void nulltbl(void); |
| static void copy_Bootblk_to_Table(void); |
| static void fill_ipart(char *bootptr, struct ipart *partp); |
| #ifdef sparc |
| uchar_t getbyte(char **bp); |
| uint32_t getlong(char **bp); |
| #endif |
| static void copy_Table_to_Bootblk(void); |
| static int TableChanged(void); |
| static void ffile_write(char *file); |
| static void fix_slice(void); |
| static int yesno(void); |
| static int readvtoc(void); |
| static int writevtoc(void); |
| static int efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc); |
| static int clear_efi(void); |
| static void clear_vtoc(int table, int part); |
| static int lecture_and_query(char *warning, char *devname); |
| static void sanity_check_provided_device(char *devname, int fd); |
| static char *get_node(char *devname); |
| static int efi_create(void); |
| |
| #ifdef i386 |
| static void id_to_name(uchar_t sysid, char *buffer); |
| static void ext_read_input(char *buf); |
| static int ext_read_options(char *buf); |
| static int ext_invalid_option(char ch); |
| static void ext_read_valid_part_num(int *pno); |
| static void ext_read_valid_part_id(uchar_t *partid); |
| static int ext_read_valid_partition_start(uint32_t *begsec); |
| static void ext_read_valid_partition_size(uint32_t begsec, uint32_t *endsec); |
| static void ext_part_menu(); |
| static void add_logical_drive(); |
| static void delete_logical_drive(); |
| static void ext_print_help_menu(); |
| static void ext_change_logical_drive_id(); |
| static void ext_print_part_types(); |
| static void ext_print_logical_drive_layout(); |
| static void preach_and_continue(); |
| #ifdef DEBUG |
| static void ext_print_logdrive_layout_debug(); |
| #endif /* DEBUG */ |
| #endif /* i386 */ |
| |
| /* |
| * This function is called only during the non-interactive mode. |
| * It is touchy and does not tolerate any errors. If there are |
| * mounted logical drives, changes to the partition table |
| * are disallowed. |
| */ |
| static void |
| update_disk_and_exit(boolean_t table_changed) |
| { |
| #ifdef i386 |
| int rval; |
| #endif |
| if (table_changed) { |
| /* |
| * Copy the new table back to the sector buffer |
| * and write it to disk |
| */ |
| copy_Table_to_Bootblk(); |
| dev_mboot_write(0, Bootsect, sectsiz); |
| } |
| |
| /* If the VTOC table is wrong fix it (truncation only) */ |
| if (io_adjt) |
| fix_slice(); |
| |
| #ifdef i386 |
| if (!io_readonly) { |
| rval = fdisk_commit_ext_part(epp); |
| switch (rval) { |
| case FDISK_SUCCESS: |
| /* Success */ |
| break; |
| case FDISK_ENOEXTPART: |
| /* Nothing to do */ |
| break; |
| default: |
| (void) fprintf(stderr, "Error in" |
| " fdisk_commit_ext_part\n"); |
| exit(rval); |
| } |
| } |
| libfdisk_fini(&epp); |
| #endif |
| exit(0); |
| } |
| |
| /* |
| * main |
| * Process command-line options. |
| */ |
| int |
| main(int argc, char *argv[]) |
| { |
| int c, i; |
| extern int optind; |
| extern char *optarg; |
| int errflg = 0; |
| int diag_cnt = 0; |
| int openmode; |
| #ifdef i386 |
| int rval; |
| int lf_op_flag = 0; |
| #endif |
| |
| setbuf(stderr, 0); /* so all output gets out on exit */ |
| setbuf(stdout, 0); |
| |
| /* Process the options. */ |
| while ((c = getopt(argc, argv, "o:s:P:F:b:A:D:W:S:tTIhwvrndgGRBE:")) |
| != EOF) { |
| switch (c) { |
| |
| case 'o': |
| io_offset = (off_t)strtoull(optarg, 0, 0); |
| continue; |
| case 's': |
| io_size = (off_t)strtoull(optarg, 0, 0); |
| continue; |
| case 'P': |
| diag_cnt++; |
| io_patt++; |
| io_fatt = optarg; |
| continue; |
| case 'w': |
| diag_cnt++; |
| io_wrt++; |
| continue; |
| case 'r': |
| diag_cnt++; |
| io_rd++; |
| continue; |
| case 'd': |
| io_debug++; |
| continue; |
| case 'I': |
| io_image++; |
| continue; |
| case 'R': |
| io_readonly++; |
| continue; |
| case 'S': |
| diag_cnt++; |
| io_sgeom = optarg; |
| continue; |
| case 'T': |
| io_ADJT++; |
| /* FALLTHROUGH */ |
| case 't': |
| io_adjt++; |
| continue; |
| case 'B': |
| io_wholedisk++; |
| io_fdisk++; |
| continue; |
| case 'E': |
| if (sscanf(optarg, "%d:%d", |
| &io_EFIslot, &io_EFIactive) != 2) { |
| io_EFIslot = io_EFIactive = 0; |
| optind--; |
| } |
| io_EFIdisk++; |
| io_fdisk++; |
| continue; |
| case 'g': |
| diag_cnt++; |
| io_lgeom++; |
| continue; |
| case 'G': |
| diag_cnt++; |
| io_pgeom++; |
| continue; |
| case 'n': |
| io_nifdisk++; |
| io_fdisk++; |
| continue; |
| case 'F': |
| io_fdisk++; |
| io_ffdisk = optarg; |
| continue; |
| case 'b': |
| io_mboot = optarg; |
| continue; |
| case 'W': |
| /* |
| * If '-' is the -W argument, then write |
| * to standard output, otherwise write |
| * to the specified file. |
| */ |
| if (strncmp(optarg, "-", 1) == 0) |
| stdo_flag = 1; |
| else |
| io_Wfdisk = optarg; |
| io_fdisk++; |
| continue; |
| case 'A': |
| io_fdisk++; |
| io_Afdisk = optarg; |
| continue; |
| case 'D': |
| io_fdisk++; |
| io_Dfdisk = optarg; |
| continue; |
| case 'h': |
| (void) fprintf(stderr, "%s\n", Usage); |
| (void) fprintf(stderr, "%s\n", Usage1); |
| exit(0); |
| /* FALLTHROUGH */ |
| case 'v': |
| v_flag = 1; |
| continue; |
| case '?': |
| errflg++; |
| break; |
| } |
| break; |
| } |
| |
| if (io_image && io_sgeom && diag_cnt == 1) { |
| diag_cnt = 0; |
| } |
| |
| /* User option checking */ |
| |
| /* By default, run in interactive mode */ |
| if (!io_fdisk && !diag_cnt && !io_nifdisk) { |
| io_ifdisk++; |
| io_fdisk++; |
| } |
| if (((io_fdisk || io_adjt) && diag_cnt) || (diag_cnt > 1)) { |
| errflg++; |
| } |
| |
| /* Was any error detected? */ |
| if (errflg || argc == optind) { |
| (void) fprintf(stderr, "%s\n", Usage); |
| (void) fprintf(stderr, |
| "\nDetailed help is available with the -h option.\n"); |
| exit(2); |
| } |
| |
| /* Figure out the correct device node to open */ |
| Dfltdev = get_node(argv[optind]); |
| |
| if (io_readonly) |
| openmode = O_RDONLY; |
| else |
| openmode = O_RDWR|O_CREAT; |
| |
| if ((Dev = open(Dfltdev, openmode, 0666)) == -1) { |
| (void) fprintf(stderr, |
| "fdisk: Cannot open device %s.\n", |
| Dfltdev); |
| exit(1); |
| } |
| /* |
| * not all disk (or disklike) drivers support DKIOCGMEDIAINFO |
| * in that case leave the minfo structure zeroed |
| */ |
| if (ioctl(Dev, DKIOCGMEDIAINFO, &minfo)) { |
| (void) memset(&minfo, 0, sizeof (minfo)); |
| } |
| |
| /* Get the disk geometry */ |
| if (!io_image) { |
| /* Get disk's HBA (virtual) geometry */ |
| errno = 0; |
| if (ioctl(Dev, DKIOCG_VIRTGEOM, &disk_geom)) { |
| |
| /* |
| * If ioctl isn't implemented on this platform, then |
| * turn off flag to print out virtual geometry (-v), |
| * otherwise use the virtual geometry. |
| */ |
| |
| if (errno == ENOTTY) { |
| v_flag = 0; |
| no_virtgeom_ioctl = 1; |
| } else if (errno == EINVAL) { |
| /* |
| * This means that the ioctl exists, but |
| * is invalid for this disk, meaning the |
| * disk doesn't have an HBA geometry |
| * (like, say, it's larger than 8GB). |
| */ |
| v_flag = 0; |
| hba_Numcyl = hba_heads = hba_sectors = 0; |
| } else { |
| (void) fprintf(stderr, |
| "%s: Cannot get virtual disk geometry.\n", |
| argv[optind]); |
| exit(1); |
| } |
| } else { |
| /* save virtual geometry values obtained by ioctl */ |
| hba_Numcyl = disk_geom.dkg_ncyl; |
| hba_heads = disk_geom.dkg_nhead; |
| hba_sectors = disk_geom.dkg_nsect; |
| } |
| |
| errno = 0; |
| if (ioctl(Dev, DKIOCG_PHYGEOM, &disk_geom)) { |
| if (errno == ENOTTY) { |
| no_physgeom_ioctl = 1; |
| } else { |
| (void) fprintf(stderr, |
| "%s: Cannot get physical disk geometry.\n", |
| argv[optind]); |
| exit(1); |
| } |
| |
| } |
| /* |
| * Call DKIOCGGEOM if the ioctls for physical and virtual |
| * geometry fail. Get both from this generic call. |
| */ |
| if (no_virtgeom_ioctl && no_physgeom_ioctl) { |
| errno = 0; |
| if (ioctl(Dev, DKIOCGGEOM, &disk_geom)) { |
| (void) fprintf(stderr, |
| "%s: Cannot get disk label geometry.\n", |
| argv[optind]); |
| exit(1); |
| } |
| } |
| |
| Numcyl = disk_geom.dkg_ncyl; |
| heads = disk_geom.dkg_nhead; |
| sectors = disk_geom.dkg_nsect; |
| |
| if (minfo.dki_lbsize != 0) |
| sectsiz = minfo.dki_lbsize; |
| else |
| sectsiz = 512; |
| |
| acyl = disk_geom.dkg_acyl; |
| |
| /* |
| * if hba geometry was not set by DKIOC_VIRTGEOM |
| * or we got an invalid hba geometry |
| * then set hba geometry based on max values |
| */ |
| if (no_virtgeom_ioctl || |
| disk_geom.dkg_ncyl == 0 || |
| disk_geom.dkg_nhead == 0 || |
| disk_geom.dkg_nsect == 0 || |
| disk_geom.dkg_ncyl > MAX_CYL || |
| disk_geom.dkg_nhead > MAX_HEAD + 1 || |
| disk_geom.dkg_nsect > MAX_SECT) { |
| |
| /* |
| * turn off flag to print out virtual geometry (-v) |
| */ |
| v_flag = 0; |
| hba_sectors = MAX_SECT; |
| hba_heads = MAX_HEAD + 1; |
| hba_Numcyl = (Numcyl * heads * sectors) / |
| (hba_sectors * hba_heads); |
| } |
| |
| if (io_debug) { |
| (void) fprintf(stderr, "Physical Geometry:\n"); |
| (void) fprintf(stderr, |
| " cylinders[%d] heads[%d] sectors[%d]\n" |
| " sector size[%d] blocks[%d] mbytes[%d]\n", |
| Numcyl, |
| heads, |
| sectors, |
| sectsiz, |
| Numcyl * heads * sectors, |
| (Numcyl * heads * sectors * sectsiz) / 1048576); |
| (void) fprintf(stderr, "Virtual (HBA) Geometry:\n"); |
| (void) fprintf(stderr, |
| " cylinders[%d] heads[%d] sectors[%d]\n" |
| " sector size[%d] blocks[%d] mbytes[%d]\n", |
| hba_Numcyl, |
| hba_heads, |
| hba_sectors, |
| sectsiz, |
| hba_Numcyl * hba_heads * hba_sectors, |
| (hba_Numcyl * hba_heads * hba_sectors * sectsiz) / |
| 1048576); |
| } |
| } |
| |
| /* If user has requested a geometry report just do it and exit */ |
| if (io_lgeom) { |
| if (ioctl(Dev, DKIOCGGEOM, &disk_geom)) { |
| (void) fprintf(stderr, |
| "%s: Cannot get disk label geometry.\n", |
| argv[optind]); |
| exit(1); |
| } |
| Numcyl = disk_geom.dkg_ncyl; |
| heads = disk_geom.dkg_nhead; |
| sectors = disk_geom.dkg_nsect; |
| if (minfo.dki_lbsize != 0) |
| sectsiz = minfo.dki_lbsize; |
| else |
| sectsiz = 512; |
| |
| acyl = disk_geom.dkg_acyl; |
| (void) printf("* Label geometry for device %s\n", Dfltdev); |
| (void) printf( |
| "* PCYL NCYL ACYL BCYL NHEAD NSECT" |
| " SECSIZ\n"); |
| (void) printf(" %-8d %-8d %-8d %-8d %-5d %-5d %-6d\n", |
| Numcyl, |
| disk_geom.dkg_ncyl, |
| disk_geom.dkg_acyl, |
| disk_geom.dkg_bcyl, |
| heads, |
| sectors, |
| sectsiz); |
| exit(0); |
| } else if (io_pgeom) { |
| if (ioctl(Dev, DKIOCG_PHYGEOM, &disk_geom)) { |
| (void) fprintf(stderr, |
| "%s: Cannot get physical disk geometry.\n", |
| argv[optind]); |
| exit(1); |
| } |
| (void) printf("* Physical geometry for device %s\n", Dfltdev); |
| (void) printf( |
| "* PCYL NCYL ACYL BCYL NHEAD NSECT" |
| " SECSIZ\n"); |
| (void) printf(" %-8d %-8d %-8d %-8d %-5d %-5d %-6d\n", |
| disk_geom.dkg_pcyl, |
| disk_geom.dkg_ncyl, |
| disk_geom.dkg_acyl, |
| disk_geom.dkg_bcyl, |
| disk_geom.dkg_nhead, |
| disk_geom.dkg_nsect, |
| sectsiz); |
| exit(0); |
| } else if (io_sgeom) { |
| if (read_geom(io_sgeom)) { |
| exit(1); |
| } else if (!io_image) { |
| exit(0); |
| } |
| } |
| |
| /* |
| * some drivers may not support DKIOCGMEDIAINFO |
| * in that case use CHS |
| */ |
| chs_capacity = (diskaddr_t)Numcyl * heads * sectors; |
| dev_capacity = chs_capacity; |
| Numcyl_usable = Numcyl; |
| |
| if (chs_capacity > DK_MAX_2TB) { |
| /* limit to 2TB */ |
| Numcyl_usable = DK_MAX_2TB / (heads * sectors); |
| chs_capacity = (diskaddr_t)Numcyl_usable * heads * sectors; |
| } |
| |
| if (minfo.dki_capacity > 0) |
| dev_capacity = minfo.dki_capacity; |
| |
| /* Allocate memory to hold three complete sectors */ |
| Bootsect = (char *)calloc(3 * sectsiz, 1); |
| if (Bootsect == NULL) { |
| (void) fprintf(stderr, |
| "fdisk: Unable to obtain enough buffer memory" |
| " (%d bytes).\n", |
| 3 * sectsiz); |
| exit(1); |
| } |
| |
| Nullsect = Bootsect + sectsiz; |
| /* Zero out the "NULL" sector */ |
| for (i = 0; i < sectsiz; i++) { |
| Nullsect[i] = 0; |
| } |
| |
| /* Find out what the user wants done */ |
| if (io_rd) { /* abs disk read */ |
| abs_read(); /* will not return */ |
| } else if (io_wrt && !io_readonly) { |
| abs_write(); /* will not return */ |
| } else if (io_patt && !io_readonly) { |
| fill_patt(); /* will not return */ |
| } |
| |
| /* This is the fdisk edit, the real reason for the program. */ |
| |
| sanity_check_provided_device(Dfltdev, Dev); |
| |
| /* Get the new BOOT program in case we write a new fdisk table */ |
| mboot_read(); |
| |
| /* Read from disk master boot */ |
| dev_mboot_read(); |
| |
| /* |
| * Verify and copy the device's fdisk table. This will be used |
| * as the prototype mboot if the device's mboot looks invalid. |
| */ |
| Bootblk = (struct mboot *)Bootsect; |
| copy_Bootblk_to_Table(); |
| |
| /* save away a copy of Table in Old_Table for sensing changes */ |
| copy_Table_to_Old_Table(); |
| |
| #ifdef i386 |
| /* |
| * Read extended partition only when the fdisk table is not |
| * supplied from a file |
| */ |
| if (!io_ffdisk) { |
| lf_op_flag |= FDISK_READ_DISK; |
| } |
| if ((rval = libfdisk_init(&epp, Dfltdev, &Table[0], lf_op_flag)) |
| != FDISK_SUCCESS) { |
| switch (rval) { |
| /* |
| * FDISK_EBADLOGDRIVE, FDISK_ENOLOGDRIVE and |
| * FDISK_EBADMAGIC can be considered as |
| * soft errors and hence we do not exit |
| */ |
| case FDISK_EBADLOGDRIVE: |
| break; |
| case FDISK_ENOLOGDRIVE: |
| break; |
| case FDISK_EBADMAGIC: |
| break; |
| case FDISK_ENOVGEOM: |
| (void) fprintf(stderr, "Could not get virtual" |
| " geometry for this device\n"); |
| exit(1); |
| break; |
| case FDISK_ENOPGEOM: |
| (void) fprintf(stderr, "Could not get physical" |
| " geometry for this device\n"); |
| exit(1); |
| break; |
| case FDISK_ENOLGEOM: |
| (void) fprintf(stderr, "Could not get label" |
| " geometry for this device\n"); |
| exit(1); |
| break; |
| default: |
| perror("Failed to initialise libfdisk.\n"); |
| exit(1); |
| break; |
| } |
| } |
| #endif |
| |
| /* Load fdisk table from specified file (-F fdisk_file) */ |
| if (io_ffdisk) { |
| /* Load and verify user-specified table parameters */ |
| load(LOADFILE, io_ffdisk); |
| } |
| |
| /* Does user want to delete or add an entry? */ |
| if (io_Dfdisk) { |
| load(LOADDEL, io_Dfdisk); |
| } |
| if (io_Afdisk) { |
| load(LOADADD, io_Afdisk); |
| } |
| |
| if (!io_ffdisk && !io_Afdisk && !io_Dfdisk) { |
| /* Check if there is no fdisk table */ |
| if (nopartdefined() || io_wholedisk || io_EFIdisk) { |
| if (io_ifdisk && !io_wholedisk && !io_EFIdisk) { |
| (void) printf( |
| "No fdisk table exists. The default" |
| " partition for the disk is:\n\n" |
| " a 100%% \"SOLARIS System\" " |
| "partition\n\n" |
| "Type \"y\" to accept the default " |
| "partition, otherwise type \"n\" to " |
| "edit the\n partition table.\n"); |
| |
| if (Numcyl > Numcyl_usable) |
| (void) printf("WARNING: Disk is larger" |
| " than 2TB. Solaris partition will" |
| " be limited to 2 TB.\n"); |
| } |
| |
| /* Edit the partition table as directed */ |
| if (io_wholedisk ||(io_ifdisk && yesno())) { |
| |
| /* Default scenario */ |
| nulltbl(); |
| /* now set up UNIX System partition */ |
| Table[0].bootid = ACTIVE; |
| Table[0].relsect = LE_32(heads * sectors); |
| |
| Table[0].numsect = |
| LE_32((ulong_t)((Numcyl_usable - 1) * |
| heads * sectors)); |
| |
| Table[0].systid = SUNIXOS2; /* Solaris */ |
| |
| /* calculate CHS values for table entry 0 */ |
| Set_Table_CHS_Values(0); |
| update_disk_and_exit(B_TRUE); |
| } else if (io_EFIdisk) { |
| if (efi_create() == 0) { |
| (void) fprintf(stderr, |
| "Error creating EFI partition\n"); |
| exit(1); |
| } |
| (void) close(Dev); |
| exit(0); |
| /* NOTREACHED */ |
| } |
| } |
| } |
| |
| /* Display complete fdisk table entries for debugging purposes */ |
| if (io_debug) { |
| (void) fprintf(stderr, "Partition Table Entry Values:\n"); |
| print_Table(); |
| if (io_ifdisk) { |
| (void) fprintf(stderr, "\n"); |
| (void) fprintf(stderr, "Press Enter to continue.\n"); |
| (void) fgets(s, sizeof (s), stdin); |
| } |
| } |
| |
| /* Interactive fdisk mode */ |
| if (io_ifdisk) { |
| (void) printf(CLR_SCR); |
| disptbl(); |
| for (;;) { |
| stage0(); |
| copy_Bootblk_to_Table(); |
| disptbl(); |
| } |
| } |
| |
| /* If user wants to write the table to a file, do it */ |
| if (io_Wfdisk) |
| ffile_write(io_Wfdisk); |
| else if (stdo_flag) |
| ffile_write((char *)stdout); |
| |
| update_disk_and_exit(TableChanged() == 1); |
| return (0); |
| } |
| |
| /* |
| * read_geom |
| * Read geometry from specified file (-S). |
| */ |
| |
| static int |
| read_geom(char *sgeom) |
| { |
| char line[256]; |
| FILE *fp; |
| |
| /* open the prototype file */ |
| if ((fp = fopen(sgeom, "r")) == NULL) { |
| (void) fprintf(stderr, "fdisk: Cannot open file %s.\n", |
| io_sgeom); |
| return (1); |
| } |
| |
| /* Read a line from the file */ |
| while (fgets(line, sizeof (line) - 1, fp)) { |
| if (line[0] == '\0' || line[0] == '\n' || line[0] == '*') |
| continue; |
| else { |
| line[strlen(line)] = '\0'; |
| if (sscanf(line, "%hu %hu %hu %hu %hu %hu %d", |
| &disk_geom.dkg_pcyl, |
| &disk_geom.dkg_ncyl, |
| &disk_geom.dkg_acyl, |
| &disk_geom.dkg_bcyl, |
| &disk_geom.dkg_nhead, |
| &disk_geom.dkg_nsect, |
| §siz) != 7) { |
| (void) fprintf(stderr, |
| "Syntax error:\n \"%s\".\n", |
| line); |
| return (1); |
| } |
| break; |
| } /* else */ |
| } /* while (fgets(line, sizeof (line) - 1, fp)) */ |
| |
| if (!io_image) { |
| if (ioctl(Dev, DKIOCSGEOM, &disk_geom)) { |
| (void) fprintf(stderr, |
| "fdisk: Cannot set label geometry.\n"); |
| return (1); |
| } |
| } else { |
| Numcyl = hba_Numcyl = disk_geom.dkg_ncyl; |
| heads = hba_heads = disk_geom.dkg_nhead; |
| sectors = hba_sectors = disk_geom.dkg_nsect; |
| acyl = disk_geom.dkg_acyl; |
| } |
| |
| (void) fclose(fp); |
| return (0); |
| } |
| |
| /* |
| * dev_mboot_read |
| * Read the master boot sector from the device. |
| */ |
| static void |
| dev_mboot_read(void) |
| { |
| if ((ioctl(Dev, DKIOCGMBOOT, Bootsect) < 0) && (errno != ENOTTY)) { |
| perror("Error in ioctl DKIOCGMBOOT"); |
| } |
| if (errno == 0) |
| return; |
| if (lseek(Dev, 0, SEEK_SET) == -1) { |
| (void) fprintf(stderr, |
| "fdisk: Error seeking to partition table on %s.\n", |
| Dfltdev); |
| if (!io_image) |
| exit(1); |
| } |
| if (read(Dev, Bootsect, sectsiz) != sectsiz) { |
| (void) fprintf(stderr, |
| "fdisk: Error reading partition table from %s.\n", |
| Dfltdev); |
| if (!io_image) |
| exit(1); |
| } |
| } |
| |
| /* |
| * dev_mboot_write |
| * Write the master boot sector to the device. |
| */ |
| static void |
| dev_mboot_write(off_t sect, char *buff, int bootsiz) |
| { |
| int new_pt, old_pt, error; |
| int clr_efi = -1; |
| |
| if (io_readonly) |
| return; |
| |
| if (io_debug) { |
| (void) fprintf(stderr, "About to write fdisk table:\n"); |
| print_Table(); |
| if (io_ifdisk) { |
| (void) fprintf(stderr, "Press Enter to continue.\n"); |
| (void) fgets(s, sizeof (s), stdin); |
| } |
| } |
| |
| /* |
| * If the new table has any Solaris partitions and the old |
| * table does not have an entry that describes it |
| * exactly then clear the old vtoc (if any). |
| */ |
| for (new_pt = 0; new_pt < FD_NUMPART; new_pt++) { |
| |
| /* We only care about potential Solaris parts. */ |
| if (Table[new_pt].systid != SUNIXOS && |
| Table[new_pt].systid != SUNIXOS2) |
| continue; |
| |
| /* Does the old table have an exact entry for the new entry? */ |
| for (old_pt = 0; old_pt < FD_NUMPART; old_pt++) { |
| |
| /* We only care about old Solaris partitions. */ |
| if ((Old_Table[old_pt].systid == SUNIXOS) || |
| (Old_Table[old_pt].systid == SUNIXOS2)) { |
| |
| /* Is this old one the same as a new one? */ |
| if ((Old_Table[old_pt].relsect == |
| Table[new_pt].relsect) && |
| (Old_Table[old_pt].numsect == |
| Table[new_pt].numsect)) |
| break; /* Yes */ |
| } |
| } |
| |
| /* Did a solaris partition change location or size? */ |
| if (old_pt >= FD_NUMPART) { |
| /* Yes clear old vtoc */ |
| if (io_debug) { |
| (void) fprintf(stderr, |
| "Clearing VTOC labels from NEW" |
| " table\n"); |
| } |
| clear_vtoc(NEW, new_pt); |
| } |
| } |
| |
| /* see if the old table had EFI */ |
| for (old_pt = 0; old_pt < FD_NUMPART; old_pt++) { |
| if (Old_Table[old_pt].systid == EFI_PMBR) { |
| clr_efi = old_pt; |
| } |
| } |
| |
| /* look to see if a EFI partition changed in relsect/numsect */ |
| for (new_pt = 0; new_pt < FD_NUMPART; new_pt++) { |
| if (Table[new_pt].systid != EFI_PMBR) |
| continue; |
| for (old_pt = 0; old_pt < FD_NUMPART; old_pt++) { |
| if ((Old_Table[old_pt].systid == |
| Table[new_pt].systid) && |
| (Old_Table[old_pt].relsect == |
| Table[new_pt].relsect) && |
| (Old_Table[old_pt].numsect == |
| Table[new_pt].numsect)) { |
| /* |
| * New EFI guard partition matches old. |
| * Do not clear EFI labels in this case. |
| */ |
| clr_efi = -1; |
| break; |
| } |
| } |
| |
| /* |
| * if EFI partition changed, set the flag to clear |
| * the EFI GPT |
| */ |
| if (old_pt == FD_NUMPART && Table[new_pt].begcyl != 0) { |
| clr_efi = 0; |
| } |
| break; |
| } |
| |
| /* clear labels if necessary */ |
| if (clr_efi >= 0) { |
| if (io_debug) { |
| (void) fprintf(stderr, "Clearing EFI labels\n"); |
| } |
| if ((error = clear_efi()) != 0) { |
| if (io_debug) { |
| (void) fprintf(stderr, |
| "\tError %d clearing EFI labels" |
| " (probably no EFI labels exist)\n", |
| error); |
| } |
| } |
| } |
| |
| if ((ioctl(Dev, DKIOCSMBOOT, buff) == -1) && (errno != ENOTTY)) { |
| (void) fprintf(stderr, |
| "fdisk: Error in ioctl DKIOCSMBOOT on %s.\n", |
| Dfltdev); |
| } |
| if (errno == 0) |
| return; |
| |
| /* write to disk drive */ |
| if (lseek(Dev, sect, SEEK_SET) == -1) { |
| (void) fprintf(stderr, |
| "fdisk: Error seeking to master boot record on %s.\n", |
| Dfltdev); |
| exit(1); |
| } |
| if (write(Dev, buff, bootsiz) != bootsiz) { |
| (void) fprintf(stderr, |
| "fdisk: Error writing master boot record to %s.\n", |
| Dfltdev); |
| exit(1); |
| } |
| } |
| |
| /* |
| * mboot_read |
| * Read the prototype boot records from the files. |
| */ |
| static void |
| mboot_read(void) |
| { |
| int mDev, i; |
| struct ipart *part; |
| |
| #if defined(i386) || defined(sparc) |
| /* |
| * If the master boot file hasn't been specified, use the |
| * implementation architecture name to generate the default one. |
| */ |
| if (io_mboot == (char *)0) { |
| /* |
| * Bug ID 1249035: |
| * The mboot file must be delivered on all platforms |
| * and installed in a non-platform-dependent |
| * directory; i.e., /usr/lib/fs/ufs. |
| */ |
| io_mboot = "/usr/lib/fs/ufs/mboot"; |
| } |
| |
| /* First read in the master boot record */ |
| |
| /* Open the master boot proto file */ |
| if ((mDev = open(io_mboot, O_RDONLY, 0666)) == -1) { |
| (void) fprintf(stderr, |
| "fdisk: Cannot open master boot file %s.\n", |
| io_mboot); |
| exit(1); |
| } |
| |
| /* Read the master boot program */ |
| if (read(mDev, &BootCod, sizeof (struct mboot)) != sizeof |
| (struct mboot)) { |
| (void) fprintf(stderr, |
| "fdisk: Cannot read master boot file %s.\n", |
| io_mboot); |
| exit(1); |
| } |
| |
| /* Is this really a master boot record? */ |
| if (LE_16(BootCod.signature) != MBB_MAGIC) { |
| (void) fprintf(stderr, |
| "fdisk: Invalid master boot file %s.\n", io_mboot); |
| (void) fprintf(stderr, |
| "Bad magic number: is %x, but should be %x.\n", |
| LE_16(BootCod.signature), MBB_MAGIC); |
| exit(1); |
| } |
| |
| (void) close(mDev); |
| #else |
| #error fdisk needs to be ported to new architecture |
| #endif |
| |
| /* Zero out the partitions part of this record */ |
| part = (struct ipart *)BootCod.parts; |
| for (i = 0; i < FD_NUMPART; i++, part++) { |
| (void) memset(part, 0, sizeof (struct ipart)); |
| } |
| |
| } |
| |
| /* |
| * fill_patt |
| * Fill the disk with user/sector number pattern. |
| */ |
| static void |
| fill_patt(void) |
| { |
| int *buff_ptr, i; |
| off_t *off_ptr; |
| int io_fpatt = 0; |
| int io_ipatt = 0; |
| |
| if (strncmp(io_fatt, "#", 1) != 0) { |
| io_fpatt++; |
| io_ipatt = strtoul(io_fatt, 0, 0); |
| buff_ptr = (int *)Bootsect; |
| for (i = 0; i < sectsiz; i += 4, buff_ptr++) |
| *buff_ptr = io_ipatt; |
| } |
| |
| /* |
| * Fill disk with pattern based on block number. |
| * Write to the disk at absolute relative block io_offset |
| * for io_size blocks. |
| */ |
| while (io_size--) { |
| off_ptr = (off_t *)Bootsect; |
| if (!io_fpatt) { |
| for (i = 0; i < sectsiz; |
| i += sizeof (off_t), off_ptr++) |
| *off_ptr = io_offset; |
| } |
| /* Write the data to disk */ |
| if (lseek(Dev, (off_t)(sectsiz * io_offset++), |
| SEEK_SET) == -1) { |
| (void) fprintf(stderr, "fdisk: Error seeking on %s.\n", |
| Dfltdev); |
| exit(1); |
| } |
| if (write(Dev, Bootsect, sectsiz) != sectsiz) { |
| (void) fprintf(stderr, "fdisk: Error writing %s.\n", |
| Dfltdev); |
| exit(1); |
| } |
| } /* while (--io_size); */ |
| } |
| |
| /* |
| * abs_read |
| * Read from the disk at absolute relative block io_offset for |
| * io_size blocks. Write the data to standard ouput (-r). |
| */ |
| static void |
| abs_read(void) |
| { |
| int c; |
| |
| while (io_size--) { |
| if (lseek(Dev, (off_t)(sectsiz * io_offset++), |
| SEEK_SET) == -1) { |
| (void) fprintf(stderr, "fdisk: Error seeking on %s.\n", |
| Dfltdev); |
| exit(1); |
| } |
| if (read(Dev, Bootsect, sectsiz) != sectsiz) { |
| (void) fprintf(stderr, "fdisk: Error reading %s.\n", |
| Dfltdev); |
| exit(1); |
| } |
| |
| /* Write to standard ouptut */ |
| if ((c = write(1, Bootsect, (unsigned)sectsiz)) != sectsiz) { |
| if (c >= 0) { |
| if (io_debug) |
| (void) fprintf(stderr, |
| "fdisk: Output warning: %d of %d" |
| " characters written.\n", |
| c, sectsiz); |
| exit(2); |
| } else { |
| perror("write error on output file."); |
| exit(2); |
| } |
| } /* if ((c = write(1, Bootsect, (unsigned)sectsiz)) */ |
| /* != sectsiz) */ |
| } /* while (--io_size); */ |
| exit(0); |
| } |
| |
| /* |
| * abs_write |
| * Read the data from standard input. Write to the disk at |
| * absolute relative block io_offset for io_size blocks (-w). |
| */ |
| static void |
| abs_write(void) |
| { |
| int c, i; |
| |
| while (io_size--) { |
| int part_exit = 0; |
| /* Read from standard input */ |
| if ((c = read(0, Bootsect, (unsigned)sectsiz)) != sectsiz) { |
| if (c >= 0) { |
| if (io_debug) |
| (void) fprintf(stderr, |
| "fdisk: WARNING: Incomplete read (%d of" |
| " %d characters read) on input file.\n", |
| c, sectsiz); |
| /* Fill pattern to mark partial sector in buf */ |
| for (i = c; i < sectsiz; ) { |
| Bootsect[i++] = 0x41; |
| Bootsect[i++] = 0x62; |
| Bootsect[i++] = 0x65; |
| Bootsect[i++] = 0; |
| } |
| part_exit++; |
| } else { |
| perror("read error on input file."); |
| exit(2); |
| } |
| |
| } |
| /* Write to disk drive */ |
| if (lseek(Dev, (off_t)(sectsiz * io_offset++), |
| SEEK_SET) == -1) { |
| (void) fprintf(stderr, "fdisk: Error seeking on %s.\n", |
| Dfltdev); |
| exit(1); |
| } |
| if (write(Dev, Bootsect, sectsiz) != sectsiz) { |
| (void) fprintf(stderr, "fdisk: Error writing %s.\n", |
| Dfltdev); |
| exit(1); |
| } |
| if (part_exit) |
| exit(0); |
| } /* while (--io_size); */ |
| exit(1); |
| } |
| |
| |
| /* |
| * load |
| * Load will either read the fdisk table from a file or add or |
| * delete an entry (-A, -D, -F). |
| */ |
| |
| static void |
| load(int funct, char *file) |
| { |
| int id; |
| int act; |
| int bhead; |
| int bsect; |
| int bcyl; |
| int ehead; |
| int esect; |
| int ecyl; |
| uint32_t rsect; |
| uint32_t numsect; |
| char line[256]; |
| int i = 0; |
| FILE *fp; |
| int startindex = 0; |
| int tmpindex = 0; |
| #ifdef i386 |
| int ext_part_present = 0; |
| uint32_t begsec, endsec, relsect; |
| logical_drive_t *temp; |
| int part_count = 0, ldcnt = 0; |
| uint32_t ext_beg_sec; |
| uint32_t old_ext_beg_sec = 0, old_ext_num_sec = 0; |
| uint32_t new_ext_beg_sec = 0, new_ext_num_sec = 0; |
| int ext_part_inited = 0; |
| uchar_t systid; |
| #endif |
| |
| switch (funct) { |
| |
| case LOADFILE: |
| |
| /* |
| * Zero out the table before loading it, which will |
| * force it to be updated on disk later (-F |
| * fdisk_file). |
| */ |
| nulltbl(); |
| |
| /* Open the prototype file */ |
| if ((fp = fopen(file, "r")) == NULL) { |
| (void) fprintf(stderr, |
| "fdisk: Cannot open prototype partition file %s.\n", |
| file); |
| exit(1); |
| } |
| |
| /* Read a line from the file */ |
| while (fgets(line, sizeof (line) - 1, fp)) { |
| if (pars_fdisk(line, &id, &act, &bhead, &bsect, |
| &bcyl, &ehead, &esect, &ecyl, &rsect, &numsect)) { |
| continue; |
| } |
| #ifdef i386 |
| part_count++; |
| |
| if (fdisk_is_dos_extended((uchar_t)id)) { |
| if (ext_part_present) { |
| (void) fprintf(stderr, |
| "Extended partition" |
| " already exists\n"); |
| (void) fprintf(stderr, "fdisk: Error on" |
| " entry \"%s\".\n", line); |
| exit(1); |
| } |
| ext_part_present = 1; |
| /* |
| * If the existing extended partition's start |
| * and size matches the new one, do not |
| * initialize the extended partition EBR |
| * (Extended Boot Record) because there could |
| * be existing logical drives. |
| */ |
| for (i = 0; i < FD_NUMPART; i++) { |
| systid = Old_Table[i].systid; |
| if (fdisk_is_dos_extended(systid)) { |
| old_ext_beg_sec = |
| Old_Table[i].relsect; |
| old_ext_num_sec = |
| Old_Table[i].numsect; |
| break; |
| } |
| } |
| new_ext_beg_sec = rsect; |
| new_ext_num_sec = numsect; |
| if ((old_ext_beg_sec != new_ext_beg_sec) || |
| (old_ext_num_sec != new_ext_num_sec)) { |
| (void) fdisk_init_ext_part(epp, |
| new_ext_beg_sec, new_ext_num_sec); |
| ext_part_inited = 1; |
| } |
| } |
| |
| if (part_count > FD_NUMPART) { |
| /* This line should be logical drive info */ |
| int offset = MAX_LOGDRIVE_OFFSET; |
| if (!ext_part_present) { |
| /* Erroneous input file */ |
| (void) fprintf(stderr, |
| "More than 4 primary" |
| " partitions found in input\n"); |
| (void) fprintf(stderr, "Exiting...\n"); |
| exit(1); |
| } |
| |
| if (numsect == 0) { |
| continue; |
| } |
| |
| /* |
| * If the start and size of the existing |
| * extended partition matches the new one and |
| * new logical drives are being defined via |
| * the input file, initialize the EBR. |
| */ |
| if (!ext_part_inited) { |
| (void) fdisk_init_ext_part(epp, |
| new_ext_beg_sec, new_ext_num_sec); |
| ext_part_inited = 1; |
| } |
| |
| begsec = rsect - offset; |
| if ((ldcnt = |
| fdisk_get_logical_drive_count(epp)) == 0) { |
| /* Adding the first logical drive */ |
| /* |
| * Make sure that begsec doesnt wrap |
| * around. This can happen if rsect is |
| * less than offset. |
| */ |
| if (rsect < offset) { |
| (void) fprintf(stderr, |
| "Minimum of " |
| "63 free sectors required " |
| "before the beginning of " |
| "a logical drive."); |
| exit(1); |
| } |
| /* |
| * Check if the first logical drive |
| * is out of order. In that case, do |
| * not subtract MAX_LOGDRIVE_OFFSET |
| * from the given start of partition. |
| */ |
| if (begsec != new_ext_beg_sec) { |
| begsec = rsect; |
| offset = 0; |
| } |
| } |
| if (ldcnt >= MAX_EXT_PARTS) { |
| (void) fprintf(stderr, |
| "\nError : Number of " |
| "logical drives exceeds limit of " |
| "%d.\n", MAX_EXT_PARTS); |
| exit(1); |
| } |
| |
| if (id > FDISK_MAX_VALID_PART_ID) { |
| (void) fprintf(stderr, |
| "Invalid partition ID\n"); |
| (void) fprintf(stderr, "fdisk: Error on" |
| " entry \"%s\".\n", line); |
| exit(1); |
| } |
| |
| endsec = rsect + numsect - 1; |
| if (fdisk_validate_logical_drive(epp, |
| begsec, offset, numsect) == 0) { |
| if (id == EFI_PMBR) { |
| (void) fprintf(stderr, "EFI " |
| "partitions not supported " |
| "inside extended " |
| "partition\n"); |
| exit(1); |
| } |
| fdisk_add_logical_drive(epp, begsec, |
| endsec, id); |
| continue; |
| } else { |
| (void) fprintf(stderr, "fdisk: Error on" |
| " entry \"%s\".\n", line); |
| exit(1); |
| } |
| } |
| #endif |
| |
| /* |
| * Validate the partition. It cannot start at sector |
| * 0 unless it is UNUSED or already exists |
| */ |
| if (validate_part(id, rsect, numsect) < 0) { |
| (void) fprintf(stderr, |
| "fdisk: Error on entry \"%s\".\n", |
| line); |
| exit(1); |
| } |
| |
| if ((tmpindex = entry_from_old_table(id, act, bhead, |
| bsect, bcyl, ehead, esect, ecyl, rsect, numsect, |
| startindex)) != -1) { |
| /* |
| * If we got here it means we copied an |
| * unmodified entry. So there is no need |
| * to insert it in the table or do any |
| * checks against disk size. |
| * |
| * This is a work around on the following |
| * situation (for IDE disks, at least): |
| * Different operation systems calculate |
| * disk size different ways, of which there |
| * are two main ways. |
| * |
| * The first, rounds the disk size to modulo |
| * cylinder size (virtual made-up cylinder |
| * usually based on maximum number of heads |
| * and sectors in partition table fields). |
| * Our OS's (for IDE) and most other "Unix" |
| * type OS's do this. |
| * |
| * The second, uses every single block |
| * on the disk (to maximize available space). |
| * Since disk manufactures do not know about |
| * "virtual cylinders", there are some number |
| * of blocks that make up a partial cylinder |
| * at the end of the disk. |
| * |
| * The difference between these two methods |
| * is where the problem is. When one |
| * tries to install Solaris/OpenSolaris on |
| * a disk that has another OS using that |
| * "partial cylinder", install fails. It fails |
| * since fdisk thinks its asked to create a |
| * partition with the -F option that contains |
| * a partition that runs off the end of the |
| * disk. |
| */ |
| startindex = tmpindex + 1; |
| continue; |
| } |
| |
| /* |
| * Find an unused entry to use and put the entry |
| * in table |
| */ |
| if ((startindex = insert_tbl(id, act, bhead, bsect, |
| bcyl, ehead, esect, ecyl, rsect, numsect, |
| startindex)) < 0) { |
| (void) fprintf(stderr, |
| "fdisk: Error on entry \"%s\".\n", |
| line); |
| exit(1); |
| } |
| startindex++; |
| } /* while (fgets(line, sizeof (line) - 1, fp)) */ |
| |
| if (verify_tbl() < 0) { |
| (void) fprintf(stderr, |
| "fdisk: Cannot create partition table\n"); |
| exit(1); |
| } |
| |
| (void) fclose(fp); |
| return; |
| |
| case LOADDEL: |
| |
| /* Parse the user-supplied deletion line (-D) */ |
| if (pars_fdisk(file, &id, &act, &bhead, &bsect, &bcyl, |
| &ehead, &esect, &ecyl, &rsect, &numsect)) { |
| (void) fprintf(stderr, |
| "fdisk: Syntax error \"%s\"\n", file); |
| exit(1); |
| } |
| |
| /* Find the exact entry in the table */ |
| for (i = 0; i < FD_NUMPART; i++) { |
| if (Table[i].systid == id && |
| Table[i].bootid == act && |
| Table[i].beghead == bhead && |
| Table[i].begsect == ((bsect & 0x3f) | |
| (uchar_t)((bcyl>>2) & 0xc0)) && |
| Table[i].begcyl == (uchar_t)(bcyl & 0xff) && |
| Table[i].endhead == ehead && |
| Table[i].endsect == ((esect & 0x3f) | |
| (uchar_t)((ecyl>>2) & 0xc0)) && |
| Table[i].endcyl == (uchar_t)(ecyl & 0xff) && |
| Table[i].relsect == LE_32(rsect) && |
| Table[i].numsect == LE_32(numsect)) { |
| |
| (void) memset(&Table[i], 0, |
| sizeof (struct ipart)); |
| #ifdef i386 |
| if (fdisk_is_dos_extended(id)) { |
| (void) fdisk_delete_ext_part(epp); |
| } |
| #endif |
| return; |
| } |
| } |
| |
| #ifdef i386 |
| ldcnt = FD_NUMPART + 1; |
| for (temp = fdisk_get_ld_head(epp); temp != NULL; |
| temp = temp->next) { |
| relsect = temp->abs_secnum + temp->logdrive_offset; |
| if (temp->parts[0].systid == id && |
| temp->parts[0].bootid == act && |
| temp->parts[0].beghead == bhead && |
| temp->parts[0].begsect == ((bsect & 0x3f) | |
| (uchar_t)((bcyl>>2) & 0xc0)) && |
| temp->parts[0].begcyl == (uchar_t)(bcyl & 0xff) && |
| temp->parts[0].endhead == ehead && |
| temp->parts[0].endsect == ((esect & 0x3f) | |
| (uchar_t)((ecyl>>2) & 0xc0)) && |
| temp->parts[0].endcyl == (uchar_t)(ecyl & 0xff) && |
| relsect == LE_32(rsect) && |
| temp->parts[0].numsect == LE_32(numsect)) { |
| fdisk_delete_logical_drive(epp, ldcnt); |
| return; |
| } |
| ldcnt++; |
| } |
| #endif |
| |
| (void) fprintf(stderr, |
| "fdisk: Entry does not match any existing partition:\n" |
| " \"%s\"\n", |
| file); |
| exit(1); |
| /* FALLTHROUGH */ |
| |
| case LOADADD: |
| |
| /* Parse the user-supplied addition line (-A) */ |
| if (pars_fdisk(file, &id, &act, &bhead, &bsect, &bcyl, &ehead, |
| &esect, &ecyl, &rsect, &numsect)) { |
| (void) fprintf(stderr, |
| "fdisk: Syntax error \"%s\"\n", file); |
| exit(1); |
| } |
| |
| /* Validate the partition. It cannot start at sector 0 */ |
| if (rsect == 0) { |
| (void) fprintf(stderr, |
| "fdisk: New partition cannot start at sector 0:\n" |
| " \"%s\".\n", |
| file); |
| exit(1); |
| } |
| |
| /* |
| * if the user wishes to add an EFI partition, we need |
| * more extensive validation. rsect should be 1, and |
| * numsect should equal the entire disk capacity - 1 |
| */ |
| |
| if (id == EFI_PMBR) { |
| if (rsect != 1) { |
| (void) fprintf(stderr, |
| "fdisk: EFI partitions must start at sector" |
| " 1 (input rsect = %d)\n", rsect); |
| exit(1); |
| } |
| |
| |
| if (dev_capacity > DK_MAX_2TB) { |
| if (numsect != DK_MAX_2TB) { |
| (void) fprintf(stderr, |
| "fdisk: EFI partitions must " |
| "encompass the entire maximum 2 TB " |
| "(input numsect: %u - max: %llu)\n", |
| numsect, (diskaddr_t)DK_MAX_2TB); |
| exit(1); |
| } |
| } else if (numsect != dev_capacity - 1) { |
| (void) fprintf(stderr, |
| "fdisk: EFI partitions must encompass the " |
| "entire disk\n" |
| "(input numsect: %u - avail: %llu)\n", |
| numsect, |
| dev_capacity - 1); |
| exit(1); |
| } |
| } |
| |
| #ifdef i386 |
| if (id > FDISK_MAX_VALID_PART_ID) { |
| (void) printf("Invalid partition ID\n"); |
| exit(1); |
| } |
| |
| if ((fdisk_ext_part_exists(epp)) && |
| (fdisk_is_dos_extended(id))) { |
| (void) fprintf(stderr, |
| "Extended partition already exists.\n"); |
| (void) fprintf(stderr, |
| "fdisk: Invalid entry could not be " |
| "inserted:\n \"%s\"\n", file); |
| exit(1); |
| } |
| |
| if (fdisk_ext_part_exists(epp) && |
| (rsect >= (ext_beg_sec = fdisk_get_ext_beg_sec(epp))) && |
| (rsect <= (fdisk_get_ext_end_sec(epp)))) { |
| int offset = MAX_LOGDRIVE_OFFSET; |
| |
| /* |
| * Make sure that begsec doesnt wrap around. |
| * This can happen if rsect is less than offset |
| */ |
| if (rsect < offset) { |
| return; |
| } |
| begsec = rsect - offset; |
| if ((ldcnt = fdisk_get_logical_drive_count(epp)) == 0) { |
| /* |
| * Adding the first logical drive |
| * Check if the first logical drive |
| * is out of order. In that case, do |
| * not subtract MAX_LOGDRIVE_OFFSET |
| * from the given start of partition. |
| */ |
| if (begsec != ext_beg_sec) { |
| begsec = rsect; |
| offset = 0; |
| } |
| } |
| |
| if (ldcnt >= MAX_EXT_PARTS) { |
| (void) printf("\nNumber of logical drives " |
| "exceeds limit of %d.\n", MAX_EXT_PARTS); |
| (void) printf("Failing further additions.\n"); |
| exit(1); |
| } |
| |
| if (numsect == 0) { |
| (void) fprintf(stderr, |
| "fdisk: Partition size cannot be zero:\n" |
| " \"%s\".\n", |
| file); |
| exit(1); |
| } |
| endsec = rsect + numsect - 1; |
| if (fdisk_validate_logical_drive(epp, begsec, |
| offset, numsect) == 0) { |
| /* Valid logical drive */ |
| fdisk_add_logical_drive(epp, begsec, endsec, |
| id); |
| return; |
| } else { |
| (void) fprintf(stderr, |
| "fdisk: Invalid entry could not be " |
| "inserted:\n \"%s\"\n", file); |
| exit(1); |
| } |
| } |
| #endif |
| |
| /* Find unused entry for use and put entry in table */ |
| if (insert_tbl(id, act, bhead, bsect, bcyl, ehead, esect, |
| ecyl, rsect, numsect, 0) < 0) { |
| (void) fprintf(stderr, |
| "fdisk: Invalid entry could not be inserted:\n" |
| " \"%s\"\n", |
| file); |
| exit(1); |
| } |
| |
| /* Make sure new entry does not overlap existing entry */ |
| if (verify_tbl() < 0) { |
| (void) fprintf(stderr, |
| "fdisk: Cannot create partition \"%s\"\n", file); |
| exit(1); |
| } |
| } /* switch funct */ |
| } |
| |
| /* |
| * Set_Table_CHS_Values |
| * |
| * This will calculate the CHS values for beginning and ending CHS |
| * for a single partition table entry (ti) based on the relsect |
| * and numsect values contained in the partion table entry. |
| * |
| * hba_heads and hba_sectors contain the number of heads and sectors. |
| * |
| * If the number of cylinders exceeds the MAX_CYL, |
| * then maximum values will be placed in the corresponding chs entry. |
| */ |
| static void |
| Set_Table_CHS_Values(int ti) |
| { |
| uint32_t lba, cy, hd, sc; |
| |
| lba = (uint32_t)Table[ti].relsect; |
| if (lba >= hba_heads * hba_sectors * MAX_CYL) { |
| /* |
| * the lba address cannot be expressed in CHS value |
| * so store the maximum CHS field values in the CHS fields. |
| */ |
| cy = MAX_CYL + 1; |
| hd = MAX_HEAD + 1; |
| sc = MAX_SECT; |
| } else { |
| cy = lba / hba_sectors / hba_heads; |
| hd = lba / hba_sectors % hba_heads; |
| sc = lba % hba_sectors + 1; |
| } |
| Table[ti].begcyl = cy & 0xff; |
| Table[ti].beghead = (uchar_t)hd; |
| Table[ti].begsect = (uchar_t)(((cy >> 2) & 0xc0) | sc); |
| |
| /* |
| * This code is identical to the code above |
| * except that it works on ending CHS values |
| */ |
| lba = (uint32_t)(Table[ti].relsect + Table[ti].numsect - 1); |
| if (lba >= hba_heads * hba_sectors * MAX_CYL) { |
| cy = MAX_CYL + 1; |
| hd = MAX_HEAD + 1; |
| sc = MAX_SECT; |
| } else { |
| cy = lba / hba_sectors / hba_heads; |
| hd = lba / hba_sectors % hba_heads; |
| sc = lba % hba_sectors + 1; |
| } |
| Table[ti].endcyl = cy & 0xff; |
| Table[ti].endhead = (uchar_t)hd; |
| Table[ti].endsect = (uchar_t)(((cy >> 2) & 0xc0) | sc); |
| } |
| |
| /* |
| * insert_tbl |
| * Insert entry into fdisk table. Check all user-supplied values |
| * for the entry, but not the validity relative to other table |
| * entries! |
| */ |
| static int |
| insert_tbl( |
| int id, int act, |
| int bhead, int bsect, int bcyl, |
| int ehead, int esect, int ecyl, |
| uint32_t rsect, uint32_t numsect, int startindex) |
| { |
| int i; |
| |
| /* validate partition size */ |
| if (((diskaddr_t)rsect + numsect) > dev_capacity) { |
| (void) fprintf(stderr, |
| "fdisk: Partition table exceeds the size of the disk.\n"); |
| return (-1); |
| } |
| |
| |
| /* find UNUSED partition table entry */ |
| for (i = startindex; i < FD_NUMPART; i++) { |
| if (Table[i].systid == UNUSED) { |
| break; |
| } |
| } |
| if (i >= FD_NUMPART) { |
| (void) fprintf(stderr, "fdisk: Partition table is full.\n"); |
| return (-1); |
| } |
| |
| Table[i].systid = (uchar_t)id; |
| Table[i].bootid = (uchar_t)act; |
| Table[i].numsect = LE_32(numsect); |
| Table[i].relsect = LE_32(rsect); |
| |
| if (id == UNUSED) { |
| (void) memset(&Table[i], 0, sizeof (struct ipart)); |
| } else if (0 < bsect && bsect <= MAX_SECT && |
| 0 <= bhead && bhead <= MAX_HEAD + 1 && |
| 0 < esect && esect <= MAX_SECT && |
| 0 <= ehead && ehead <= MAX_HEAD + 1) { |
| |
| /* |
| * If we have been called with a valid geometry, use it |
| * valid means non-zero values that fit in the BIOS fields |
| */ |
| if (bcyl > MAX_CYL) { |
| /* |
| * bcyl is set to the special fence value indicating |
| * that the geometry is not representable for the |
| * start LBA. |
| * Set all fields to the maximum values. |
| */ |
| bcyl = MAX_CYL + 1; |
| bhead = MAX_HEAD + 1; |
| bsect = MAX_SECT; |
| } |
| if (ecyl > MAX_CYL) { |
| ecyl = MAX_CYL + 1; |
| ehead = MAX_HEAD + 1; |
| esect = MAX_SECT; |
| } |
| Table[i].begcyl = bcyl & 0xff; |
| Table[i].endcyl = ecyl & 0xff; |
| Table[i].beghead = (uchar_t)bhead; |
| Table[i].endhead = (uchar_t)ehead; |
| Table[i].begsect = (uchar_t)(((bcyl >> 2) & 0xc0) | bsect); |
| Table[i].endsect = ((ecyl >> 2) & 0xc0) | esect; |
| } else { |
| |
| /* |
| * The specified values are invalid, |
| * so calculate the values based on hba_heads, hba_sectors |
| */ |
| Set_Table_CHS_Values(i); |
| } |
| |
| /* |
| * return partition index |
| */ |
| return (i); |
| } |
| |
| /* |
| * entry_from_old_table |
| * If the specified entry is in the old table and is not a Solaris entry |
| * then insert same entry into new fdisk table. If we do this then |
| * all checks are skipped for that entry! |
| */ |
| static int |
| entry_from_old_table( |
| int id, int act, |
| int bhead, int bsect, int bcyl, |
| int ehead, int esect, int ecyl, |
| uint32_t rsect, uint32_t numsect, int startindex) |
| { |
| uint32_t i, j; |
| |
| if (id == SUNIXOS || id == SUNIXOS2 || id == UNUSED) |
| return (-1); |
| for (i = 0; i < FD_NUMPART; i++) { |
| if (Old_Table[i].systid == id && |
| Old_Table[i].bootid == act && |
| Old_Table[i].beghead == bhead && |
| Old_Table[i].begsect == ((bsect & 0x3f) | |
| (uchar_t)((bcyl>>2) & 0xc0)) && |
| Old_Table[i].begcyl == (uchar_t)(bcyl & 0xff) && |
| Old_Table[i].endhead == ehead && |
| Old_Table[i].endsect == ((esect & 0x3f) | |
| (uchar_t)((ecyl>>2) & 0xc0)) && |
| Old_Table[i].endcyl == (uchar_t)(ecyl & 0xff) && |
| Old_Table[i].relsect == lel(rsect) && |
| Old_Table[i].numsect == lel(numsect)) { |
| /* find UNUSED partition table entry */ |
| for (j = startindex; j < FD_NUMPART; j++) { |
| if (Table[j].systid == UNUSED) { |
| (void) memcpy(&Table[j], &Old_Table[i], |
| sizeof (Table[0])); |
| skip_verify[j] = 1; |
| return (j); |
| |
| } |
| } |
| return (-1); |
| } |
| |
| } |
| return (-1); |
| } |
| |
| /* |
| * verify_tbl |
| * Verify that no partition entries overlap or exceed the size of |
| * the disk. |
| */ |
| static int |
| verify_tbl(void) |
| { |
| uint32_t i, j, rsect, numsect; |
| int noMoreParts = 0; |
| int numParts = 0; |
| |
| /* Make sure new entry does not overlap an existing entry */ |
| for (i = 0; i < FD_NUMPART - 1; i++) { |
| if (Table[i].systid != UNUSED) { |
| numParts++; |
| /* |
| * No valid partitions allowed after EFI_PMBR part |
| */ |
| if (noMoreParts) { |
| return (-1); |
| } |
| |
| if (Table[i].systid == EFI_PMBR) { |
| /* |
| * EFI_PMBR partition must be the only |
| * partition |
| */ |
| noMoreParts = 1; |
| |
| if (Table[i].relsect != 1) { |
| (void) fprintf(stderr, "ERROR: " |
| "Invalid starting sector " |
| "for EFI_PMBR partition:\n" |
| "relsect %d " |
| "(should be 1)\n", |
| Table[i].relsect); |
| |
| return (-1); |
| } |
| |
| if (Table[i].numsect != |
| ((dev_capacity > DK_MAX_2TB) ? DK_MAX_2TB: |
| (dev_capacity - 1))) { |
| |
| (void) fprintf(stderr, "ERROR: " |
| "EFI_PMBR partition must " |
| "encompass the entire"); |
| |
| if (dev_capacity > DK_MAX_2TB) |
| (void) fprintf(stderr, |
| "maximum 2 TB.\n " |
| "numsect %u - " |
| "actual %llu\n", |
| Table[i].numsect, |
| (diskaddr_t)DK_MAX_2TB); |
| |
| else |
| (void) fprintf(stderr, |
| "disk.\n numsect %u - " |
| "actual %llu\n", |
| Table[i].numsect, |
| dev_capacity - 1); |
| |
| return (-1); |
| } |
| } |
| |
| /* make sure the partition isn't larger than the disk */ |
| rsect = LE_32(Table[i].relsect); |
| numsect = LE_32(Table[i].numsect); |
| |
| if ((((diskaddr_t)rsect + numsect) > dev_capacity) || |
| (((diskaddr_t)rsect + numsect) > DK_MAX_2TB)) { |
| if (!skip_verify[i]) |
| return (-1); |
| } |
| |
| for (j = i + 1; j < FD_NUMPART; j++) { |
| if (Table[j].systid != UNUSED) { |
| uint32_t t_relsect = |
| LE_32(Table[j].relsect); |
| uint32_t t_numsect = |
| LE_32(Table[j].numsect); |
| |
| if (noMoreParts) { |
| (void) fprintf(stderr, |
| "Cannot add partition to " |
| "table; no more partitions " |
| "allowed\n"); |
| |
| if (io_debug) { |
| (void) fprintf(stderr, |
| "DEBUG: Current " |
| "partition:\t" |
| "%d:%d:%d:%d:%d:" |
| "%d:%d:%d:%d:%d\n" |
| " Next " |
| "partition:\t\t" |
| "%d:%d:%d:%d:%d:" |
| "%d:%d:%d:%d:%d\n", |
| Table[i].systid, |
| Table[i].bootid, |
| Table[i].begcyl, |
| Table[i].beghead, |
| Table[i].begsect, |
| Table[i].endcyl, |
| Table[i].endhead, |
| Table[i].endsect, |
| Table[i].relsect, |
| Table[i].numsect, |
| Table[j].systid, |
| Table[j].bootid, |
| Table[j].begcyl, |
| Table[j].beghead, |
| Table[j].begsect, |
| Table[j].endcyl, |
| Table[j].endhead, |
| Table[j].endsect, |
| Table[j].relsect, |
| Table[j].numsect); |
| } |
| |
| return (-1); |
| } |
| if ((rsect >= |
| (t_relsect + t_numsect)) || |
| ((rsect + numsect) <= t_relsect)) { |
| continue; |
| } else { |
| (void) fprintf(stderr, "ERROR: " |
| "current partition overlaps" |
| " following partition\n"); |
| |
| return (-1); |
| } |
| } |
| } |
| } |
| } |
| if (Table[i].systid != UNUSED) { |
| if (noMoreParts) |
| return (-1); |
| if (!skip_verify[i] && |
| ((((diskaddr_t)lel(Table[i].relsect) + |
| lel(Table[i].numsect)) > dev_capacity) || |
| (((diskaddr_t)lel(Table[i].relsect) + |
| lel(Table[i].numsect)) > DK_MAX_2TB))) { |
| return (-1); |
| } |
| } |
| |
| return (numParts); |
| } |
| |
| /* |
| * pars_fdisk |
| * Parse user-supplied data to set up fdisk partitions |
| * (-A, -D, -F). |
| */ |
| static int |
| pars_fdisk( |
| char *line, |
| int *id, int *act, |
| int *bhead, int *bsect, int *bcyl, |
| int *ehead, int *esect, int *ecyl, |
| uint32_t *rsect, uint32_t *numsect) |
| { |
| int i; |
| int64_t test; |
| char *tok, *p; |
| char buf[256]; |
| |
| if (line[0] == '\0' || line[0] == '\n' || line[0] == '*') |
| return (1); |
| line[strlen(line)] = '\0'; |
| for (i = 0; i < strlen(line); i++) { |
| if (line[i] == '\0') { |
| break; |
| } else if (line[i] == ':') { |
| line[i] = ' '; |
| } |
| } |
| (void) strncpy(buf, line, 256); |
| errno = 0; |
| tok = strtok(buf, ": \t\n"); |
| while (tok != NULL) { |
| for (p = tok; *p != '\0'; p++) { |
| if (!isdigit(*p)) { |
| (void) printf("Invalid input %s in line %s.\n", |
| tok, line); |
| exit(1); |
| } |
| } |
| |
| test = strtoll(tok, (char **)NULL, 10); |
| if ((test < 0) || (test > 0xFFFFFFFF) || (errno != 0)) { |
| (void) printf("Invalid input %s in line %s.\n", tok, |
| line); |
| exit(1); |
| } |
| tok = strtok(NULL, ": \t\n"); |
| } |
| if (sscanf(line, "%d %d %d %d %d %d %d %d %u %u", |
| id, act, bhead, bsect, bcyl, ehead, esect, ecyl, |
| rsect, numsect) != 10) { |
| (void) fprintf(stderr, "Syntax error:\n \"%s\".\n", line); |
| exit(1); |
| } |
| return (0); |
| } |
| |
| /* |
| * validate_part |
| * Validate that a new partition does not start at sector 0. Only UNUSED |
| * partitions and previously existing partitions are allowed to start at 0. |
| */ |
| static int |
| validate_part(int id, uint32_t rsect, uint32_t numsect) |
| { |
| int i; |
| if ((id != UNUSED) && (rsect == 0)) { |
| for (i = 0; i < FD_NUMPART; i++) { |
| if ((Old_Table[i].systid == id) && |
| (Old_Table[i].relsect == LE_32(rsect)) && |
| (Old_Table[i].numsect == LE_32(numsect))) |
| return (0); |
| } |
| (void) fprintf(stderr, |
| "New partition cannot start at sector 0\n"); |
| return (-1); |
| } |
| #ifdef i386 |
| if (id > FDISK_MAX_VALID_PART_ID) { |
| (void) fprintf(stderr, "Invalid partition ID\n"); |
| return (-1); |
| } |
| #endif |
| return (0); |
| } |
| |
| /* |
| * stage0 |
| * Print out interactive menu and process user input. |
| */ |
| static void |
| stage0(void) |
| { |
| #ifdef i386 |
| int rval; |
| #endif |
| dispmenu(); |
| for (;;) { |
| (void) printf(Q_LINE); |
| (void) printf("Enter Selection: "); |
| (void) fgets(s, sizeof (s), stdin); |
| rm_blanks(s); |
| #ifdef i386 |
| while (!((s[0] > '0') && (s[0] < '8') && |
| ((s[1] == '\0') || (s[1] == '\n')))) { |
| #else |
| while (!((s[0] > '0') && (s[0] < '7') && |
| ((s[1] == '\0') || (s[1] == '\n')))) { |
| #endif |
| (void) printf(E_LINE); /* Clear any previous error */ |
| #ifdef i386 |
| (void) printf( |
| "Enter a one-digit number between 1 and 7."); |
| #else |
| (void) printf( |
| "Enter a one-digit number between 1 and 6."); |
| #endif |
| (void) printf(Q_LINE); |
| (void) printf("Enter Selection: "); |
| (void) fgets(s, sizeof (s), stdin); |
| rm_blanks(s); |
| } |
| (void) printf(E_LINE); |
| switch (s[0]) { |
| case '1': |
| if (pcreate() == -1) |
| return; |
| break; |
| case '2': |
| if (pchange() == -1) |
| return; |
| break; |
| case '3': |
| if (pdelete() == -1) |
| return; |
| break; |
| case '4': |
| if (ppartid() == -1) |
| return; |
| break; |
| #ifdef i386 |
| case '5': |
| if (fdisk_ext_part_exists(epp)) { |
| ext_part_menu(); |
| } else { |
| (void) printf(Q_LINE); |
| (void) printf("\nNo extended partition" |
| " found\n"); |
| (void) printf("Press enter to " |
| "continue\n"); |
| ext_read_input(s); |
| } |
| break; |
| case '6': |
| /* update disk partition table, if changed */ |
| if (TableChanged() == 1) { |
| copy_Table_to_Bootblk(); |
| dev_mboot_write(0, Bootsect, sectsiz); |
| } |
| |
| /* |
| * If the VTOC table is wrong fix it |
| * (truncate only) |
| */ |
| if (io_adjt) { |
| fix_slice(); |
| } |
| if (!io_readonly) { |
| rval = fdisk_commit_ext_part(epp); |
| switch (rval) { |
| case FDISK_SUCCESS: |
| /* Success */ |
| /* Fallthrough */ |
| case FDISK_ENOEXTPART: |
| /* Nothing to do */ |
| break; |
| case FDISK_EMOUNTED: |
| (void) printf(Q_LINE); |
| preach_and_continue(); |
| continue; |
| default: |
| perror("Commit failed"); |
| exit(1); |
| } |
| libfdisk_fini(&epp); | <