diff options
31 files changed, 4598 insertions, 139 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint index 5db3761aa9..865e36e348 100644 --- a/usr/src/Makefile.lint +++ b/usr/src/Makefile.lint @@ -475,7 +475,8 @@ i386_SUBDIRS= \ cmd/rtc \ cmd/ucodeadm \ lib/brand/lx \ - lib/cfgadm_plugins/sata + lib/cfgadm_plugins/sata \ + lib/libfdisk sparc_SUBDIRS= \ cmd/datadm \ diff --git a/usr/src/Targetdirs b/usr/src/Targetdirs index ea2b61a2e8..cefa12dff8 100644 --- a/usr/src/Targetdirs +++ b/usr/src/Targetdirs @@ -925,6 +925,8 @@ $(ROOT)/usr/lib/libefi.so.1:= REALPATH=../../lib/libefi.so.1 $(ROOT)/usr/lib/libefi.so:= REALPATH=../../lib/libefi.so.1 $(ROOT)/usr/lib/libelf.so.1:= REALPATH=../../lib/libelf.so.1 $(ROOT)/usr/lib/libelf.so:= REALPATH=../../lib/libelf.so.1 +$(ROOT)/usr/lib/libfdisk.so.1:= REALPATH=../../lib/libfdisk.so.1 +$(ROOT)/usr/lib/libfdisk.so:= REALPATH=../../lib/libfdisk.so.1 $(ROOT)/usr/lib/libgen.so.1:= REALPATH=../../lib/libgen.so.1 $(ROOT)/usr/lib/libgen.so:= REALPATH=../../lib/libgen.so.1 $(ROOT)/usr/lib/libinetcfg.so.1:= REALPATH=../../lib/libinetcfg.so.1 @@ -1049,6 +1051,8 @@ $(ROOT)/usr/lib/llib-lefi.ln:= REALPATH=../../lib/llib-lefi.ln $(ROOT)/usr/lib/llib-lefi:= REALPATH=../../lib/llib-lefi $(ROOT)/usr/lib/llib-lelf.ln:= REALPATH=../../lib/llib-lelf.ln $(ROOT)/usr/lib/llib-lelf:= REALPATH=../../lib/llib-lelf +$(ROOT)/usr/lib/llib-lfdisk.ln:= REALPATH=../../lib/llib-lfdisk.ln +$(ROOT)/usr/lib/llib-lfdisk:= REALPATH=../../lib/llib-lfdisk $(ROOT)/usr/lib/llib-lgen.ln:= REALPATH=../../lib/llib-lgen.ln $(ROOT)/usr/lib/llib-lgen:= REALPATH=../../lib/llib-lgen $(ROOT)/usr/lib/llib-linetcfg.ln:= REALPATH=../../lib/llib-linetcfg.ln @@ -1471,7 +1475,14 @@ $(ROOT)/usr/lib/$(MACH64)/nss_nisplus.so.1:= \ $(ROOT)/usr/lib/$(MACH64)/nss_user.so.1:= \ REALPATH=../../../lib/$(MACH64)/nss_user.so.1 +i386_SYM.USRLIB= \ + /usr/lib/libfdisk.so \ + /usr/lib/libfdisk.so.1 \ + /usr/lib/llib-lfdisk \ + /usr/lib/llib-lfdisk.ln + SYM.USRLIB= \ + $($(MACH)_SYM.USRLIB) \ /lib/libposix4.so \ /lib/libposix4.so.1 \ /lib/llib-lposix4 \ diff --git a/usr/src/cmd/boot/installgrub/Makefile b/usr/src/cmd/boot/installgrub/Makefile index 125c3a7ad8..39aa17462b 100644 --- a/usr/src/cmd/boot/installgrub/Makefile +++ b/usr/src/cmd/boot/installgrub/Makefile @@ -18,7 +18,7 @@ # # CDDL HEADER END # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # @@ -37,11 +37,17 @@ CPPFLAGS += -I$(SRC)/uts/i86pc -I$(SRC)/uts/intel -I$(SRC)/uts/common LDLIBS += -lmd5 +i386_CFLAGS += -D_LARGEFILE64_SOURCE +i386_CFLAGS += -D_FILE_OFFSET_BITS=64 + +LDLIBS += -lfdisk + LINTFLAGS += \ -erroff=E_BAD_PTR_CAST_ALIGN \ -erroff=E_STATIC_UNUSED \ -erroff=E_FUNC_RET_MAYBE_IGNORED \ - -erroff=E_FUNC_RET_MAYBE_IGNORED2 + -erroff=E_FUNC_RET_MAYBE_IGNORED2 \ + -xerroff=E_NAME_DEF_NOT_USED2 .KEEP_STATE: diff --git a/usr/src/cmd/boot/installgrub/installgrub.c b/usr/src/cmd/boot/installgrub/installgrub.c index c233cded29..f37f1430f5 100644 --- a/usr/src/cmd/boot/installgrub/installgrub.c +++ b/usr/src/cmd/boot/installgrub/installgrub.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -43,6 +43,7 @@ #include <locale.h> #include "message.h" #include <errno.h> +#include <libfdisk.h> #include <md5.h> #ifndef TEXT_DOMAIN @@ -83,7 +84,8 @@ static int is_bootpar = 0; static int strip = 0; static int stage2_fd; static int partition, slice = 0xff; -static unsigned int stage2_first_sector, stage2_second_sector; +static char *device_p0; +static uint32_t stage2_first_sector, stage2_second_sector; static char bpb_sect[SECTOR_SIZE]; @@ -226,10 +228,11 @@ static unsigned int get_start_sector(int fd) { static unsigned int start_sect = 0; - - int i; + uint32_t secnum, numsec; + int i, pno, rval, ext_sol_part_found = 0; struct mboot *mboot; struct ipart *part; + ext_part_t *epp; if (start_sect) return (start_sect); @@ -243,6 +246,44 @@ get_start_sector(int fd) } } + /* Read extended partition to find a solaris partition */ + if ((rval = libfdisk_init(&epp, device_p0, NULL, FDISK_READ_DISK)) + != FDISK_SUCCESS) { + switch (rval) { + /* + * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can + * be considered as soft errors and hence + * we do not exit + */ + case FDISK_EBADLOGDRIVE: + break; + case FDISK_ENOLOGDRIVE: + break; + case FDISK_ENOVGEOM: + (void) fprintf(stderr, NO_VIRT_GEOM); + exit(1); + break; + case FDISK_ENOPGEOM: + (void) fprintf(stderr, NO_PHYS_GEOM); + exit(1); + break; + case FDISK_ENOLGEOM: + (void) fprintf(stderr, NO_LABEL_GEOM); + exit(1); + break; + default: + (void) fprintf(stderr, LIBFDISK_INIT_FAIL); + exit(1); + break; + } + } + + rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec); + if (rval == FDISK_SUCCESS) { + ext_sol_part_found = 1; + } + libfdisk_fini(&epp); + /* * If there is no boot partition, find the solaris partition */ @@ -272,6 +313,7 @@ get_start_sector(int fd) (void) fprintf(stderr, BAD_PART, i); exit(-1); } + if (edkpi.p_start >= part->relsect && edkpi.p_start < (part->relsect + part->numsect)) { /* Found the partition */ @@ -280,7 +322,7 @@ get_start_sector(int fd) } } - if (i == FD_NUMPART) { + if ((i == FD_NUMPART) && (!ext_sol_part_found)) { (void) fprintf(stderr, BOOTPAR); exit(-1); } @@ -294,12 +336,18 @@ get_start_sector(int fd) } } - start_sect = part->relsect; + if (fdisk_is_dos_extended(part->systid)) { + start_sect = secnum; + partition = pno; + } else { + start_sect = part->relsect; + partition = i; + } + if (part->bootid != 128 && write_mboot == 0) { (void) fprintf(stdout, BOOTPAR_INACTIVE, i + 1); } - partition = i; return (start_sect); } @@ -395,6 +443,7 @@ read_boot_sect(char *device) device[i - 2] = 'p'; device[i - 1] = '0'; + device_p0 = strdup(device); fd = open(device, O_RDONLY); if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) { (void) fprintf(stderr, READ_FAIL_MBR, device); @@ -623,8 +672,8 @@ modify_and_write_stage2(int dev_fd) if (is_floppy || is_bootpar) { int i = 0; - uint_t partition_offset; - uint_t install_addr = 0x8200; + uint32_t partition_offset; + uint32_t install_addr = 0x8200; uchar_t *pos = (uchar_t *)stage2_buffer + STAGE2_BLOCKLIST; stage2_first_sector = blocklist[0]; diff --git a/usr/src/cmd/boot/installgrub/message.h b/usr/src/cmd/boot/installgrub/message.h index 1e644fb16c..594a7da1a1 100644 --- a/usr/src/cmd/boot/installgrub/message.h +++ b/usr/src/cmd/boot/installgrub/message.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -104,6 +104,14 @@ extern "C" { #define OUT_OF_MEMORY gettext("diskread: out of memory\n") +#define NO_VIRT_GEOM gettext("Could not get virtual geometry\n") + +#define NO_PHYS_GEOM gettext("Could not get physical geometry\n") + +#define NO_LABEL_GEOM gettext("Could not get label geometry\n") + +#define LIBFDISK_INIT_FAIL gettext("Failed to initialize libfdisk.\n") + #ifdef __cplusplus } #endif diff --git a/usr/src/cmd/devfsadm/disk_link.c b/usr/src/cmd/devfsadm/disk_link.c index b1cc0a48fb..95cda78a19 100644 --- a/usr/src/cmd/devfsadm/disk_link.c +++ b/usr/src/cmd/devfsadm/disk_link.c @@ -44,6 +44,14 @@ #define MN_SMI "h" #define MN_EFI "wd" #define ASCIIWWNSIZE 255 +#if defined(__i386) || defined(__amd64) +/* + * The number of minor nodes per LUN is defined by the disk drivers. + * Currently it is set to 64. Refer CMLBUNIT_SHIFT (cmlb_impl.h) + */ +#define NUM_MINORS_PER_INSTANCE 64 +#endif + extern int system_labeled; @@ -344,14 +352,48 @@ disk_common(di_minor_t minor, di_node_t node, char *disk, int flags) char *nt = NULL; int *int_prop; int nflags = 0; +#if defined(__i386) || defined(__amd64) + char mn_copy[4]; + char *part; + int part_num; +#endif - if (strstr(mn = di_minor_name(minor), ",raw")) { + mn = di_minor_name(minor); + if (strstr(mn, ",raw")) { dir = "rdsk"; +#if defined(__i386) || defined(__amd64) + (void) strncpy(mn_copy, mn, 4); + part = strtok(mn_copy, ","); +#endif } else { dir = "dsk"; +#if defined(__i386) || defined(__amd64) + part = mn; +#endif } - if (mn[0] < 113) { +#if defined(__i386) || defined(__amd64) + /* + * The following is a table describing the allocation of + * minor numbers, minor names and /dev/dsk names for partitions + * and slices on x86 systems. + * + * Minor Number Minor Name /dev/dsk name + * --------------------------------------------- + * 0 to 15 "a" to "p" s0 to s15 + * 16 "q" p0 + * 17 to 20 "r" to "u" p1 to p4 + * 21 to 52 "p5" to "p36" p5 to p36 + * + */ + part_num = atoi(part + 1); + + if ((mn[0] == 'p') && (part_num >= 5)) { + /* logical drive */ + (void) snprintf(slice, 4, "%s", part); + } else { +#endif + if (mn[0] < 'q') { (void) sprintf(slice, "s%d", mn[0] - 'a'); } else if (strncmp(mn, MN_EFI, 2) != 0) { (void) sprintf(slice, "p%d", mn[0] - 'q'); @@ -359,6 +401,9 @@ disk_common(di_minor_t minor, di_node_t node, char *disk, int flags) /* For EFI label */ (void) sprintf(slice, SLICE_EFI); } +#if defined(__i386) || defined(__amd64) + } +#endif nflags = 0; if (system_labeled) { diff --git a/usr/src/cmd/fdisk/Makefile b/usr/src/cmd/fdisk/Makefile index 4555481ce5..530f8cb70e 100644 --- a/usr/src/cmd/fdisk/Makefile +++ b/usr/src/cmd/fdisk/Makefile @@ -19,10 +19,9 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" # # Makefile for fdisk @@ -36,7 +35,12 @@ include ../Makefile.cmd CPPFLAGS += -D_FILE_OFFSET_BITS=64 -LDLIBS += -ladm -lefi +LDLIBS_i386= -lfdisk +LDLIBS_sparc= +LDLIBS += -ladm -lefi $(LDLIBS_$(MACH)) + +i386_CFLAGS += -D_LARGEFILE64_SOURCE +i386_CFLAGS += -D_FILE_OFFSET_BITS=64 all: $(ROOTFS_PROG) diff --git a/usr/src/cmd/fdisk/fdisk.c b/usr/src/cmd/fdisk/fdisk.c index 947d509064..f5a9706d6c 100644 --- a/usr/src/cmd/fdisk/fdisk.c +++ b/usr/src/cmd/fdisk/fdisk.c @@ -57,16 +57,33 @@ #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/" @@ -122,6 +139,23 @@ #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" @@ -213,6 +247,7 @@ 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"; @@ -314,6 +349,283 @@ static int sectsiz; /* sector size */ #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); @@ -373,9 +685,41 @@ 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); +#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 int is_linux_swap(uint32_t part_start, off_t *lsm_offset); +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 + * is 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 @@ -389,11 +733,27 @@ update_disk_and_exit(boolean_t table_changed) 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: + fprintf(stderr, "Error in" + " fdisk_commit_ext_part\n"); + exit(rval); + } + } + libfdisk_fini(&epp); +#endif exit(0); } - - /* * main * Process command-line options. @@ -407,6 +767,10 @@ main(int argc, char *argv[]) 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); @@ -805,6 +1169,49 @@ main(int argc, char *argv[]) /* 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 and FDISK_ENOLOGDRIVE can + * be considered as soft errors and hence + * we do not exit + */ + case FDISK_EBADLOGDRIVE: + break; + case FDISK_ENOLOGDRIVE: + break; + case FDISK_ENOVGEOM: + fprintf(stderr, "Could not get virtual" + " geometry for this device\n"); + exit(1); + break; + case FDISK_ENOPGEOM: + fprintf(stderr, "Could not get physical" + " geometry for this device\n"); + exit(1); + break; + case FDISK_ENOLGEOM: + 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 */ @@ -845,10 +1252,10 @@ main(int argc, char *argv[]) nulltbl(); /* now set up UNIX System partition */ Table[0].bootid = ACTIVE; - Table[0].relsect = lel(heads * sectors); + Table[0].relsect = LE_32(heads * sectors); Table[0].numsect = - lel((ulong_t)((Numcyl_usable - 1) * + LE_32((ulong_t)((Numcyl_usable - 1) * heads * sectors)); Table[0].systid = SUNIXOS2; /* Solaris */ @@ -1024,6 +1431,57 @@ dev_mboot_write(off_t sect, char *buff, int bootsiz) Table[new_pt].systid != SUNIXOS2) continue; +#ifdef i386 + + /* + * Check if a solaris old partition is there in the new table. + * If so, this could potentially have been a linux swap. + * Check to see if the linux swap magic is there, and destroy + * the magic if there is one. + */ + if (Table[new_pt].systid == SUNIXOS) { + off_t lsmo; + char *lsm_buf; + + if ((lsm_buf = calloc(1, sectsiz)) == NULL) { + fprintf(stderr, "Could not allocate memory\n"); + exit(1); + } + + if (is_linux_swap(Table[new_pt].relsect, &lsmo) == 0) { + if (lseek(Dev, lsmo, SEEK_SET) < 0) { + fprintf(stderr, "Error seeking on " + "%s\n", Dfltdev); + exit(1); + } + + if (read(Dev, lsm_buf, sectsiz) < sectsiz) { + fprintf(stderr, "Error reading on " + "%s\n", Dfltdev); + exit(1); + } + + bzero(lsm_buf + sectsiz - + LINUX_SWAP_MAGIC_LENGTH, + LINUX_SWAP_MAGIC_LENGTH); + + if (lseek(Dev, lsmo, SEEK_SET) < 0) { + fprintf(stderr, "Error seeking on " + "%s\n", Dfltdev); + exit(1); + } + + if (write(Dev, lsm_buf, sectsiz) < sectsiz) { + fprintf(stderr, "Error writing on " + "%s\n", Dfltdev); + exit(1); + } + } + free(lsm_buf); + } + +#endif + /* Does the old table have an exact entry for the new entry? */ for (old_pt = 0; old_pt < FD_NUMPART; old_pt++) { @@ -1167,12 +1625,12 @@ mboot_read(void) } /* Is this really a master boot record? */ - if (les(BootCod.signature) != MBB_MAGIC) { + 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", - les(BootCod.signature), MBB_MAGIC); + LE_16(BootCod.signature), MBB_MAGIC); exit(1); } @@ -1354,6 +1812,17 @@ load(int funct, char *file) int i = 0; int j; FILE *fp; +#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, ext_end_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) { @@ -1380,6 +1849,134 @@ load(int funct, char *file) &bcyl, &ehead, &esect, &ecyl, &rsect, &numsect)) { continue; } +#ifdef i386 + part_count++; + + if (fdisk_is_dos_extended((uchar_t)id)) { + if (ext_part_present) { + fprintf(stderr, "Extended partition" + " already exists\n"); + 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)) { + 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 */ + fprintf(stderr, "More than 4 primary" + " partitions found in input\n"); + 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) { + 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) { + 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) { + fprintf(stderr, "\nError : Number of " + "logical drives exceeds limit of " + "%d.\n", MAX_EXT_PARTS); + exit(1); + } + + if (id > FDISK_MAX_VALID_PART_ID) { + fprintf(stderr, "Invalid partition " + "ID\n"); + 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) { + fprintf(stderr, "EFI " + "partitions not supported " + "inside extended " + "partition\n"); + exit(1); + } + fdisk_add_logical_drive(epp, begsec, + endsec, id); + continue; + } else { + fprintf(stderr, "fdisk: Error on" + " entry \"%s\".\n", line); + exit(1); + } + } +#endif /* * Validate the partition. It cannot start at sector @@ -1477,8 +2074,8 @@ load(int funct, char *file) Table[i].endsect == ((esect & 0x3f) | (uchar_t)((ecyl>>2) & 0xc0)) && Table[i].endcyl == (uchar_t)(ecyl & 0xff) && - Table[i].relsect == lel(rsect) && - Table[i].numsect == lel(numsect)) { + Table[i].relsect == LE_32(rsect) && + Table[i].numsect == LE_32(numsect)) { /* * Found the entry. Now move rest of @@ -1507,9 +2104,39 @@ load(int funct, char *file) Table[FD_NUMPART - 1].systid = UNUSED; Table[FD_NUMPART - 1].bootid = 0; +#ifdef i386 + if (fdisk_is_dos_extended(id)) { + 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", @@ -1571,6 +2198,74 @@ load(int funct, char *file) } } +#ifdef i386 + if (id > FDISK_MAX_VALID_PART_ID) { + 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 <= (ext_end_sec = 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) { + printf("\nNumber of logical drives exceeds " + "limit of %d.\n", MAX_EXT_PARTS); + 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; + } + } +#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) { @@ -1681,8 +2376,8 @@ insert_tbl( Table[i].systid = (uchar_t)id; Table[i].bootid = (uchar_t)act; - Table[i].numsect = lel(numsect); - Table[i].relsect = lel(rsect); + Table[i].numsect = LE_32(numsect); + Table[i].relsect = LE_32(rsect); /* * If we have been called with a valid geometry, use it @@ -1824,8 +2519,8 @@ verify_tbl(void) } /* make sure the partition isn't larger than the disk */ - rsect = lel(Table[i].relsect); - numsect = lel(Table[i].numsect); + 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)) { @@ -1836,9 +2531,9 @@ verify_tbl(void) for (j = i + 1; j < FD_NUMPART; j++) { if (Table[j].systid != UNUSED) { uint32_t t_relsect = - lel(Table[j].relsect); + LE_32(Table[j].relsect); uint32_t t_numsect = - lel(Table[j].numsect); + LE_32(Table[j].numsect); if (noMoreParts) { (void) fprintf(stderr, @@ -1926,6 +2621,10 @@ pars_fdisk( 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'; @@ -1936,6 +2635,25 @@ pars_fdisk( line[i] = ' '; } } + strncpy(buf, line, 256); + errno = 0; + tok = strtok(buf, ": \t\n"); + while (tok != NULL) { + for (p = tok; *p != '\0'; p++) { + if (!isdigit(*p)) { + 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)) { + 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) { @@ -1957,14 +2675,20 @@ validate_part(int id, uint32_t rsect, uint32_t numsect) if ((id != UNUSED) && (rsect == 0)) { for (i = 0; i < FD_NUMPART; i++) { if ((Old_Table[i].systid == id) && - (Old_Table[i].relsect == lel(rsect)) && - (Old_Table[i].numsect == lel(numsect))) + (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) { + fprintf(stderr, "Invalid partition ID\n"); + return (-1); + } +#endif return (0); } @@ -1975,17 +2699,30 @@ validate_part(int id, uint32_t rsect, uint32_t numsect) 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); @@ -2009,6 +2746,54 @@ stage0(void) if (ppartid() == -1) return; break; +#ifdef i386 + case '5': + if (fdisk_ext_part_exists(epp)) { + ext_part_menu(); + } else { + printf(Q_LINE); + printf("\nNo extended partition found" + "\n"); + 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: + printf(Q_LINE); + preach_and_continue(); + continue; + default: + perror("Commit failed"); + exit(1); + } + libfdisk_fini(&epp); + } + (void) close(Dev); + exit(0); +#else case '5': /* update disk partition table, if changed */ if (TableChanged() == 1) { @@ -2025,7 +2810,12 @@ stage0(void) (void) close(Dev); exit(0); /* FALLTHRU */ +#endif +#ifdef i386 + case '7': +#else case '6': +#endif /* * If the VTOC table is wrong fix it * (truncate only) @@ -2056,6 +2846,9 @@ pcreate(void) int i, j; uint32_t numsect; int retCode = 0; +#ifdef i386 + int ext_part_present = 0; +#endif i = 0; for (;;) { @@ -2076,8 +2869,14 @@ pcreate(void) numsect = 0; for (i = 0; i < FD_NUMPART; i++) { if (Table[i].systid != UNUSED) { - numsect += lel(Table[i].numsect); + numsect += LE_32(Table[i].numsect); + } +#ifdef i386 + /* Check if an extended partition already exists */ + if (fdisk_is_dos_extended(Table[i].systid)) { + ext_part_present = 1; } +#endif if (numsect >= chs_capacity) { (void) printf(E_LINE); (void) printf("There is no more room on the disk for" @@ -2135,6 +2934,17 @@ pcreate(void) tsystid = DOSOS16; /* DOS 16 bit fat */ break; case '7': +#ifdef i386 + if (ext_part_present) { + printf(Q_LINE); + printf(E_LINE); + fprintf(stderr, + "Extended partition already exists\n"); + fprintf(stderr, "Press enter to continue\n"); + ext_read_input(s); + continue; + } +#endif tsystid = EXTDOS; break; case '8': @@ -2214,6 +3024,18 @@ pcreate(void) Table[i].bootid = 0; } +#ifdef i386 + /* + * If partition created is an extended partition, null + * out the first sector of the first cylinder of the + * extended partition + */ + if (fdisk_is_dos_extended(Table[i].systid)) { + fdisk_init_ext_part(epp, + LE_32(Table[i].relsect), + LE_32(Table[i].numsect)); + } +#endif /* set up the return code */ i = 1; } @@ -2300,8 +3122,8 @@ specify(uchar_t tsystid) for (j = i + 1; j < FD_NUMPART; j++) { if (partition[j]->systid == UNUSED) break; - if (lel(partition[j]->relsect) < - lel(partition[i]->relsect)) { + if (LE_32(partition[j]->relsect) < + LE_32(partition[i]->relsect)) { struct ipart *temp = partition[i]; partition[i] = partition[j]; partition[j] = temp; @@ -2381,8 +3203,8 @@ specify(uchar_t tsystid) */ if (i) { /* Not an empty table */ - first_free = lel(partition[i - 1]->relsect) + - lel(partition[i - 1]->numsect); + first_free = LE_32(partition[i - 1]->relsect) + + LE_32(partition[i - 1]->numsect); } else { first_free = cyl_size; } @@ -2399,9 +3221,9 @@ specify(uchar_t tsystid) * Make sure free space is not negative. */ size_free = - (lel(partition[i]->relsect > first_free)) ? - (lel(partition[i]->relsect) - first_free) : - 0; + (LE_32(partition[i]->relsect > first_free)) + ? (LE_32(partition[i]->relsect) - + first_free) : 0; } /* save largest free space */ @@ -2475,8 +3297,8 @@ specify(uchar_t tsystid) if (partition[i]->systid == UNUSED) break; - t_relsect = lel(partition[i]->relsect); - t_numsect = lel(partition[i]->numsect); + t_relsect = LE_32(partition[i]->relsect); + t_numsect = LE_32(partition[i]->numsect); if (cyl * cyl_size >= t_relsect && cyl * cyl_size < t_relsect + t_numsect) { @@ -2551,6 +3373,17 @@ static void dispmenu(void) { (void) printf(M_LINE); +#ifdef i386 + (void) printf( + "SELECT ONE OF THE FOLLOWING:\n" + " 1. Create a partition\n" + " 2. Specify the active partition\n" + " 3. Delete a partition\n" + " 4. Change between Solaris and Solaris2 Partition IDs\n" + " 5. Edit/View extended partitions\n" + " 6. Exit (update disk configuration and exit)\n" + " 7. Cancel (exit without updating disk configuration)\n"); +#else (void) printf( "SELECT ONE OF THE FOLLOWING:\n" " 1. Create a partition\n" @@ -2559,6 +3392,7 @@ dispmenu(void) " 4. Change between Solaris and Solaris2 Partition IDs\n" " 5. Exit (update disk configuration and exit)\n" " 6. Cancel (exit without updating disk configuration)\n"); +#endif } /* @@ -2719,16 +3553,46 @@ DEL1: (void) printf(Q_LINE); return (-1); } - (void) printf(Q_LINE); - (void) printf("Are you sure you want to delete partition %d?" - " This will make all files and \n", i + 1); - (void) printf("programs in this partition inaccessible (type" - " \"y\" or \"n\"). "); +#ifdef i386 + if (fdisk_is_dos_extended(Table[i].systid) && + (Table[i].relsect == fdisk_get_ext_beg_sec(epp)) && + fdisk_get_logical_drive_count(epp)) { + (void) printf(Q_LINE); + (void) printf("There are logical drives inside the" + " extended partition\n"); + (void) printf("Are you sure of proceeding with deletion ?" + " (type \"y\" or \"n\") "); - (void) printf(E_LINE); - if (! yesno()) { - return (1); + (void) printf(E_LINE); + if (! yesno()) { + return (1); + } + if (fdisk_mounted_logical_drives(epp) == FDISK_EMOUNTED) { + (void) printf(Q_LINE); + (void) printf("There are mounted logical drives. " + "Committing changes now can cause data loss or " + "corruption. Unmount all logical drives and then " + "try committing the changes again.\n"); + (void) printf("Press enter to continue.\n"); + ext_read_input(s); + return (1); + } + fdisk_delete_ext_part(epp); + } else { +#endif + (void) printf(Q_LINE); + (void) printf("Are you sure you want to delete partition %d?" + " This will make all files and \n", i + 1); + (void) printf("programs in this partition inaccessible (type" + " \"y\" or \"n\"). "); + + (void) printf(E_LINE); + if (! yesno()) { + return (1); + } +#ifdef i386 } +#endif if (Table[i].bootid == ACTIVE) { pactive = 1; @@ -2912,6 +3776,9 @@ disptbl(void) case FDISK_CPM: type = CPMstr; break; + case FDISK_NOVELL2: + type = NOV2str; + break; case FDISK_NOVELL3: type = NOVstr; break; @@ -2947,7 +3814,7 @@ disptbl(void) break; case EFI_PMBR: type = EFIstr; - if (lel(Table[i].numsect) == DK_MAX_2TB) + if (LE_32(Table[i].numsect) == DK_MAX_2TB) is_pmbr = 1; break; @@ -2955,16 +3822,16 @@ disptbl(void) type = Ostr; break; } - startcyl = lel(Table[i].relsect) / + startcyl = LE_32(Table[i].relsect) / (unsigned long)(heads * sectors); - if (lel(Table[i].numsect) == DK_MAX_2TB) { + if (LE_32(Table[i].numsect) == DK_MAX_2TB) { endcyl = Numcyl - 1; length = endcyl - startcyl + 1; } else { - length = lel(Table[i].numsect) / + length = LE_32(Table[i].numsect) / (unsigned long)(heads * sectors); - if (lel(Table[i].numsect) % + if (LE_32(Table[i].numsect) % (unsigned long)(heads * sectors)) length++; endcyl = startcyl + length - 1; @@ -3028,8 +3895,8 @@ print_Table(void) (void) fprintf(stderr, "%-5d ", Table[i].endsect & 0x3f); (void) fprintf(stderr, "%-8d ", (((uint_t)Table[i].endsect & 0xc0) << 2) + Table[i].endcyl); - (void) fprintf(stderr, "%-10u ", lel(Table[i].relsect)); - (void) fprintf(stderr, "%-10u\n", lel(Table[i].numsect)); + (void) fprintf(stderr, "%-10u ", LE_32(Table[i].relsect)); + (void) fprintf(stderr, "%-10u\n", LE_32(Table[i].numsect)); } } @@ -3061,8 +3928,8 @@ nulltbl(void) for (i = 0; i < FD_NUMPART; i++) { Table[i].systid = UNUSED; - Table[i].numsect = lel(UNUSED); - Table[i].relsect = lel(UNUSED); + Table[i].numsect = LE_32(UNUSED); + Table[i].relsect = LE_32(UNUSED); Table[i].bootid = 0; skip_verify[i] = 0; } @@ -3083,7 +3950,7 @@ copy_Bootblk_to_Table(void) /* Get an aligned copy of the partition tables */ (void) memcpy(iparts, Bootblk->parts, sizeof (iparts)); bootptr = (char *)iparts; /* Points to start of partition table */ - if (les(Bootblk->signature) != MBB_MAGIC) { + if (LE_16(Bootblk->signature) != MBB_MAGIC) { /* Signature is missing */ nulltbl(); (void) memcpy(Bootblk->bootinst, &BootCod, BOOTSZ); @@ -3108,8 +3975,8 @@ copy_Bootblk_to_Table(void) } for (i = j; i < FD_NUMPART; i++) { Table[i].systid = UNUSED; - Table[i].numsect = lel(UNUSED); - Table[i].relsect = lel(UNUSED); + Table[i].numsect = LE_32(UNUSED); + Table[i].relsect = LE_32(UNUSED); Table[i].bootid = 0; } @@ -3193,7 +4060,7 @@ copy_Table_to_Bootblk(void) else (void) memcpy(boot_ptr, tbl_ptr, sizeof (struct ipart)); } - Bootblk->signature = les(MBB_MAGIC); + Bootblk->signature = LE_16(MBB_MAGIC); } /* @@ -3281,6 +4148,7 @@ ffile_write(char *file) (void) fprintf(fp, "* 86: DOSDATA\n"); (void) fprintf(fp, "* 98: OTHEROS\n"); (void) fprintf(fp, "* 99: UNIXOS\n"); + (void) fprintf(fp, "* 100: FDISK_NOVELL2\n"); (void) fprintf(fp, "* 101: FDISK_NOVELL3\n"); (void) fprintf(fp, "* 119: FDISK_QNX4\n"); (void) fprintf(fp, "* 120: FDISK_QNX42\n"); @@ -3303,23 +4171,53 @@ ffile_write(char *file) " Rsect Numsect\n"); for (i = 0; i < FD_NUMPART; i++) { - if (Table[i].systid != UNUSED) + (void) fprintf(fp, + " %-5d %-4d %-6d %-6d %-7d %-6d %-6d %-7d %-10u" + " %-10u\n", + Table[i].systid, + Table[i].bootid, + Table[i].beghead, + Table[i].begsect & 0x3f, + ((Table[i].begcyl & 0xff) | ((Table[i].begsect & + 0xc0) << 2)), + Table[i].endhead, + Table[i].endsect & 0x3f, + ((Table[i].endcyl & 0xff) | ((Table[i].endsect & + 0xc0) << 2)), + LE_32(Table[i].relsect), + LE_32(Table[i].numsect)); + } +#ifdef i386 + if (fdisk_ext_part_exists(epp)) { + struct ipart ext_tab; + logical_drive_t *temp; + uint32_t rsect, numsect, tempsect = 0; + for (temp = fdisk_get_ld_head(epp); temp != NULL; + temp = temp->next) { + ext_tab = temp->parts[0]; + rsect = tempsect + LE_32(ext_tab.relsect) + + fdisk_get_ext_beg_sec(epp); + numsect = LE_32(ext_tab.numsect); + tempsect = LE_32(temp->parts[1].relsect); (void) fprintf(fp, - " %-5d %-4d %-6d %-6d %-7d %-6d %-6d %-7d %-10u" - " %-10u\n", - Table[i].systid, - Table[i].bootid, - Table[i].beghead, - Table[i].begsect & 0x3f, - ((Table[i].begcyl & 0xff) | ((Table[i].begsect & - 0xc0) << 2)), - Table[i].endhead, - Table[i].endsect & 0x3f, - ((Table[i].endcyl & 0xff) | ((Table[i].endsect & - 0xc0) << 2)), - lel(Table[i].relsect), - lel(Table[i].numsect)); + " %-5d %-4d %-6d %-6d %-7d %-6d %-6d " + "%-7d %-8u %-8u\n", + ext_tab.systid, + ext_tab.bootid, + ext_tab.beghead, + ext_tab.begsect & 0x3f, + ((ext_tab.begcyl & 0xff) | + ((ext_tab.begsect & 0xc0) << 2)), + ext_tab.endhead, + ext_tab.endsect & 0x3f, + ((ext_tab.endcyl & 0xff) | + ((ext_tab.endsect & 0xc0) << 2)), + rsect, + numsect); + } } +#endif + if (fp != stdout) (void) fclose(fp); } @@ -3347,7 +4245,7 @@ fix_slice(void) * VTOC entries are relative to the start of * the partition. */ - numsect = lel(Table[i].numsect); + numsect = LE_32(Table[i].numsect); break; } } @@ -3717,13 +4615,13 @@ clear_vtoc(int table, int part) return; } - seek_byte = (off_t)(lel(clr_table->relsect) + VTOC_OFFSET) * sectsiz; + seek_byte = (off_t)(LE_32(clr_table->relsect) + VTOC_OFFSET) * sectsiz; if (io_debug) { (void) fprintf(stderr, "\tClearing primary VTOC at byte %llu (block %llu)\n", (uint64_t)seek_byte, - (uint64_t)(lel(clr_table->relsect) + VTOC_OFFSET)); + (uint64_t)(LE_32(clr_table->relsect) + VTOC_OFFSET)); } if (lseek(Dev, seek_byte, SEEK_SET) == -1) { @@ -3779,8 +4677,8 @@ clear_vtoc(int table, int part) #endif /* DEBUG */ /* Clear backup label */ - pcyl = lel(clr_table->numsect) / (heads * sectors); - solaris_offset = lel(clr_table->relsect); + pcyl = LE_32(clr_table->numsect) / (heads * sectors); + solaris_offset = LE_32(clr_table->relsect); ncyl = pcyl - acyl; backup_block = ((ncyl + acyl - 1) * @@ -4024,3 +4922,741 @@ get_node(char *devname) return (node); } + +#ifdef i386 +static void +preach_and_continue() +{ + (void) fprintf(stderr, "There are mounted logical drives. Committing " + "changes now can lead to inconsistancy in internal system state " + "which can eventually cause data loss or corruption. Unmount all " + "logical drives and try committing the changes again.\n"); + ext_read_input(s); +} + +/* + * Convert a given partition ID to an descriptive string. + * Just an index into the partition types table. + */ +void +id_to_name(uchar_t sysid, char *buffer) +{ + strcpy(buffer, fdisk_part_types[sysid]); +} + +/* + * Procedure to check the validity of the extended partition menu option + * entered by the user + */ +static int +ext_invalid_option(char ch) +{ + char *p; + + p = strchr(ext_part_menu_opts, tolower(ch)); + + if (p == NULL) { + return (1); + } + return (0); +} + +/* + * Read 16 bytes of the input (assuming that no valid user input spans more + * than that). Flush the input stream, so that the next read does not reap + * stale data from the previous input that was not processed. + * Note that fgets also reads the trailing '\n' + */ +static void +ext_read_input(char *buf) +{ + fgets(buf, 16, stdin); + fflush(stdin); +} + +/* + * Procedure to read and validate the user option at the extended partition menu + */ +static int +ext_read_options(char *buf) +{ + ext_read_input(buf); + if ((strlen(buf) != 2) || (ext_invalid_option(buf[0]))) { + printf("\nUnknown Command\n"); + return (-1); + } + return (0); +} + +/* + * Procedure to print the list of known partition types and their IDs + */ +static void +ext_print_part_types() +{ + int i, rowmax, rowcount = 1; + struct winsize ws; + char buf[80]; + + /* Get the current window dimensions */ + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) { + perror("ioctl"); + rowmax = 20; + } else { + /* + * Accommodate the initial headings by reducing the number of + * partition IDs being printed. + */ + rowmax = ws.ws_row - 5; + } + + if (rowmax < 3) { + fprintf(stderr, "Window size too small." + " Try resizing the window\n"); + return; + } + + printf("List of known partition types : \n"); + printf("PartID Partition Type\n"); + printf("====== ==============\n"); + for (i = 0; i <= FDISK_MAX_VALID_PART_ID; i++) { + printf("%-3d %s\n", i, fdisk_part_types[i]); + rowcount++; + if (rowcount == rowmax) { + /* + * After the initial screen, use all the rows for + * printing the partition IDs, but one. + */ + rowmax = ws.ws_row - 1; + fprintf(stderr, "\nPress enter to see next page or 'q'" + " to quit : "); + ext_read_input(buf); + if ((strlen(buf) == 2) && (tolower(buf[0]) == 'q')) { + return; + } + rowcount = 1; + } + } +} + +static void +ext_read_valid_part_num(int *pno) +{ + char buf[80]; + int len, i; + + for (;;) { + printf("Enter the partition number : "); + ext_read_input(buf); + + len = strlen(buf); + + /* Check length of the input */ + if ((len < 2) || (len > (FDISK_MAX_VALID_PART_NUM_DIGITS+1))) { + goto print_error_and_continue; + } + + /* Check if there is a non-digit in the input */ + for (i = 0; i < len-1; i++) { + if (!isdigit(buf[i])) { + goto print_error_and_continue; + } + } + + *pno = atoi(buf); + + if ((*pno <= FD_NUMPART) || + *pno > (fdisk_get_logical_drive_count(epp) + FD_NUMPART)) { + goto print_error_and_continue; + } + + break; +print_error_and_continue: + printf("Invalid partition number\n"); + continue; + } +} + +static void +ext_read_valid_part_id(uchar_t *partid) +{ + char buf[80]; + int len, i, id; + + for (;;) { + printf("Enter the ID ( Type I for list of partition IDs ) : "); + ext_read_input(buf); + len = strlen(buf); + + if ((len < 2) || (len > (FDISK_MAX_VALID_PART_ID_DIGITS + 1))) { + printf("Invalid partition ID\n"); + continue; + } + + if ((len == 2) && (toupper(buf[0]) == 'I')) { + ext_print_part_types(); + continue; + } + + /* Check if there is a non-digit in the input */ + for (i = 0; i < len-1; i++) { + if (!isdigit(buf[i])) { + printf("Invalid partition ID\n"); + break; + } + } + + if (i < len - 1) { + continue; + } + + /* Check if the (now) valid number is greater than the limit */ + if ((id = atoi(buf)) > FDISK_MAX_VALID_PART_ID) { + printf("Invalid partition ID\n"); + continue; + } + + *partid = (uchar_t)id; + + /* Disallow multiple extended partitions */ + if (fdisk_is_dos_extended(*partid)) { + printf("Multiple extended partitions not allowed\n"); + continue; + } + + /* Disallow EFI partitions within extended partition */ + if (*partid == EFI_PMBR) { + printf("EFI partitions within an extended partition" + " is not allowed\n"); + continue; + } + + return; /* Valid partition ID is in partid */ + } +} + +static void +delete_logical_drive() +{ + int pno; + + if (!fdisk_get_logical_drive_count(epp)) { + printf("\nNo logical drives defined.\n"); + return; + } + + printf("\n"); + ext_read_valid_part_num(&pno); + fdisk_delete_logical_drive(epp, pno); + printf("Partition %d deleted\n", pno); +} + +static int +ext_read_valid_partition_start(uint32_t *begsec) +{ + char buf[80]; + int ret, len, i; + uint32_t begcyl; + uint32_t first_free_cyl; + uint32_t first_free_sec; + + ret = fdisk_ext_find_first_free_sec(epp, &first_free_sec); + if (ret != FDISK_SUCCESS) { + return (ret); + } + + first_free_cyl = FDISK_SECT_TO_CYL(epp, first_free_sec); + for (;;) { + printf("Enter the beginning cylinder (Default - %d) : ", + first_free_cyl); + ext_read_input(buf); + len = strlen(buf); + if (len == 1) { /* User accepted the default value */ + *begsec = first_free_sec; + return (FDISK_SUCCESS); + } + + if (len > (FDISK_MAX_VALID_CYL_NUM_DIGITS + 1)) { + printf("Input too long\n"); + printf("Invalid beginning cylinder number\n"); + continue; + } + /* Check if there is a non-digit in the input */ + for (i = 0; i < len - 1; i++) { + if (!isdigit(buf[i])) { + printf("Invalid beginning cylinder number\n"); + break; + } + } + if (i < len - 1) { + continue; + } + + begcyl = atoi(buf); + ret = fdisk_ext_validate_part_start(epp, begcyl, begsec); + switch (ret) { + case FDISK_SUCCESS: + /* + * Success. + * Valid beginning sector is in begsec + */ + break; + + case FDISK_EOVERLAP: + printf("Partition boundary overlaps with "); + printf("existing partitions\n"); + printf("Invalid beginning cylinder number\n"); + continue; + + case FDISK_EOOBOUND: + printf("Cylinder boundary beyond the limits\n"); + printf("Invalid beginning cylinder number\n"); + continue; + } + return (FDISK_SUCCESS); + } +} + +/* + * Algorithm : + * 1. Check if the first character is a + + * a) If yes, check if the last character is 'k', 'm' or 'g' + * 2. If not, check if there are any non-digits + * 3. Check for the length of the numeral string + * 4. atoi the numeral string + * 5. In case of data entered in KB, MB or GB, convert it to number of cylinders + * a) Adjust size to be cylinder boundary aligned + * 6. If size specifies is zero, flag error + * 7. Check if the size is less than 1 cylinder + * a) If yes, default the size FDISK_MIN_PART_SIZE + * b) If no, Check if the size is within endcyl - begcyl + */ +static void +ext_read_valid_partition_size(uint32_t begsec, uint32_t *endsec) +{ + char buf[80]; + uint32_t tempcyl; + uint32_t last_free_sec; + uint32_t last_free_cyl; + int i, len, ch, mbgb = 0, scale = FDISK_SECTS_PER_CYL(epp); + uint64_t size = 0; + int copy_len; + char numbuf[FDISK_MAX_VALID_CYL_NUM_DIGITS + 1]; + int sectsize = fdisk_get_disk_geom(epp, PHYSGEOM, SSIZE); + uint32_t remdr, spc, poss_end; + + if (sectsize == EINVAL) { + fprintf(stderr, "Unsupported geometry statistics.\n"); + exit(1); + } + + last_free_sec = fdisk_ext_find_last_free_sec(epp, begsec); + last_free_cyl = FDISK_SECT_TO_CYL(epp, last_free_sec); + + for (;;) { + printf("Enter the size in cylinders (Default End Cylinder -"); + printf(" %u)\n", last_free_cyl); + printf("Type +<size>K, +<size>M or +<size>G to enter size in"); + printf("KB, MB or GB : "); + ext_read_input(buf); + len = strlen(buf); + mbgb = 0; + scale = FDISK_SECTS_PER_CYL(epp); + + if (len == 1) { /* User accepted the default value */ + *endsec = last_free_sec; + return; + } + + copy_len = len - 1; + + if ((buf[0] == '+') && (isdigit(buf[1]))) { + copy_len--; + if ((ch = toupper(buf[len - 2])) == 'B') { + ch = toupper(buf[len - 3]); + copy_len--; + } + + if (!((ch == 'K') || (ch == 'M') || (ch == 'G'))) { + printf("Invalid partition size\n"); + continue; + } + + copy_len--; + mbgb = 1; + scale = ((ch == 'K') ? FDISK_KB : + ((ch == 'M') ? FDISK_MB : FDISK_GB)); + } + + if (copy_len > FDISK_MAX_VALID_CYL_NUM_DIGITS) { + printf("Input too long\n"); + printf("Invalid partition size\n"); + continue; + } + + strncpy(numbuf, &buf[mbgb], copy_len); + numbuf[copy_len] = '\0'; + + for (i = mbgb; i < copy_len + mbgb; i++) { + if (!isdigit(buf[i])) { + break; + } + } + + if (i < copy_len + mbgb) { + printf("Invalid partition size\n"); + continue; + } + + size = (atoll(numbuf) * (scale)); + + if (size == 0) { + printf("Zero size is invalid\n"); + printf("Invalid partition size\n"); + continue; + } + + if (mbgb) { + size /= sectsize; + } + + if (size > (last_free_sec - begsec + 1)) { + printf("Cylinder boundary beyond the limits"); + printf(" or overlaps with existing"); + printf(" partitions\n"); + printf("Invalid partition size\n"); + continue; + } + + /* + * Adjust the ending sector such that there are no partial + * cylinders allocated. But at the same time, make sure it + * doesn't over shoot boundaries. + */ + spc = FDISK_SECTS_PER_CYL(epp); + poss_end = begsec + size - 1; + if (remdr = (poss_end % spc)) { + poss_end += spc - remdr - 1; + } + *endsec = (poss_end > last_free_sec) ? last_free_sec : + poss_end; + + return; + } +} + +/* + * ALGORITHM: + * 1. Get the starting and ending sectors/cylinder of the extended partition. + * 2. Keep track of the first free sector/cylinder + * 3. Allow the user to specify the beginning cylinder of the new partition + * 4. Check for the validity of the entered data + * a) If it is non-numeric + * b) If it is beyond the extended partition limits + * c) If it overlaps with the current logical drives + * 5. Allow the user to specify the size in cylinders/ human readable form + * 6. Check for the validity of the entered data + * a) If it is non-numeric + * b) If it is beyond the extended partition limits + * c) If it overlaps with the current logical drives + * d) If it is a number lesser than the starting cylinder + * 7. Request partition ID for the new partition. + * 8. Update the first free cylinder available + * 9. Display Success message + */ + +static void +add_logical_drive() +{ + uint32_t begsec, endsec; + uchar_t partid; + char buf[80]; + int rval; + + if (fdisk_get_logical_drive_count(epp) >= MAX_EXT_PARTS) { + printf("\nNumber of logical drives exceeds limit of %d.\n", + MAX_EXT_PARTS); + printf("Command did not succeed. Press enter to continue\n"); + ext_read_input(buf); + return; + } + + printf("\n"); + rval = ext_read_valid_partition_start(&begsec); + switch (rval) { + case FDISK_SUCCESS: + break; + + case FDISK_EOOBOUND: + printf("\nNo space left in the extended partition\n"); + printf("Press enter to continue\n"); + ext_read_input(buf); + return; + } + + ext_read_valid_partition_size(begsec, &endsec); + ext_read_valid_part_id(&partid); + fdisk_add_logical_drive(epp, begsec, endsec, partid); + + printf("New partition with ID %d added\n", partid); +} + +static void +ext_change_logical_drive_id() +{ + int pno; + uchar_t partid; + + if (!fdisk_get_logical_drive_count(epp)) { + printf("\nNo logical drives defined.\n"); + return; + } + + printf("\n"); + ext_read_valid_part_num(&pno); + ext_read_valid_part_id(&partid); + fdisk_change_logical_drive_id(epp, pno, partid); + + printf("Partition ID of partition %d changed to %d\n", pno, partid); +} + +#ifdef DEBUG +static void +ext_print_logdrive_layout_debug() +{ + int pno; + char namebuff[255]; + logical_drive_t *head = fdisk_get_ld_head(epp); + logical_drive_t *temp; + + if (!fdisk_get_logical_drive_count(epp)) { + printf("\nNo logical drives defined.\n"); + return; + } + + printf("\n\n"); + puts("# start block end block abs start abs end OSType"); + for (temp = head, pno = 5; temp != NULL; temp = temp->next, pno++) { + /* Print the logical drive details */ + id_to_name(temp->parts[0].systid, namebuff); + printf("%d: %.10u %.10u %.10u %.10u", + pno, + LE_32(temp->parts[0].relsect), + LE_32(temp->parts[0].numsect), + temp->abs_secnum, + temp->abs_secnum + temp->numsect - 1 + + MAX_LOGDRIVE_OFFSET); + printf(" %s\n", namebuff); + /* + * Print the second entry in the EBR which is information + * about the location and the size of the next extended + * partition. + */ + id_to_name(temp->parts[1].systid, namebuff); + printf("%d: %.10u %.10u %.10s %.10s", + pno, + LE_32(temp->parts[1].relsect), + LE_32(temp->parts[1].numsect), + " ", " "); + printf(" %s\n", namebuff); + } +} +#endif + +static void +ext_print_logical_drive_layout() +{ + int sysid; + unsigned int startcyl, endcyl, length, percent, remainder; + logical_drive_t *temp; + struct ipart *fpart; + char namebuff[255]; + int numcyl = fdisk_get_disk_geom(epp, PHYSGEOM, NCYL); + int pno; + + if (numcyl == EINVAL) { + fprintf(stderr, "Unsupported geometry statistics.\n"); + exit(1); + } + + if (!fdisk_get_logical_drive_count(epp)) { + printf("\nNo logical drives defined.\n"); + return; + } + + printf("\n"); + printf("Number of cylinders in disk : %u\n", numcyl); + printf("Beginning cylinder of extended partition : %u\n", + fdisk_get_ext_beg_cyl(epp)); + printf("Ending cylinder of extended partition : %u\n", + fdisk_get_ext_end_cyl(epp)); + printf("\n"); + printf("Part# StartCyl EndCyl Length %% " + "Part ID (Type)\n"); + printf("===== ======== ======== ======= ===" + " ==============\n"); + for (temp = fdisk_get_ld_head(epp), pno = 5; temp != NULL; + temp = temp->next, pno++) { + /* Print the logical drive details */ + fpart = &temp->parts[0]; + sysid = fpart->systid; + id_to_name(sysid, namebuff); + startcyl = temp->begcyl; + endcyl = temp->endcyl; + if (startcyl == endcyl) { + length = 1; + } else { + length = endcyl - startcyl + 1; + } + percent = length * 100 / numcyl; + if ((remainder = (length * 100 % numcyl)) != 0) { + if ((remainder * 100 / numcyl) > 50) { + /* round up */ + percent++; + } + /* Else leave the percent as is since it's already */ + /* rounded down */ + } + if (percent > 100) { + percent = 100; + } + printf("%-5d %-8u %-8u %-7u %-3d %-3d (%-.28s)\n", + pno, startcyl, endcyl, length, percent, sysid, namebuff); + } +#ifdef DEBUG + ext_print_logdrive_layout_debug(); +#endif + printf("\n"); +} + +static void +ext_print_help_menu() +{ + printf("\n"); + printf("a Add a logical drive\n"); + printf("d Delete a logical drive\n"); + printf("h Print this help menu\n"); + printf("i Change the id of the logical drive\n"); + printf("p Print the logical drive layout\n"); + printf("r Return to the main fdisk menu\n"); + printf(" (To commit or cancel the changes)\n"); + printf("\n"); +} + +static void +ext_part_menu() +{ + char buf[80]; + uchar_t *bbsigp; + static int bbsig_disp_flag = 1; + + int i; + + printf(CLR_SCR); + + if (fdisk_corrupt_logical_drives(epp)) { + printf("One or more logical drives seem to be corrupt.\n"); + printf("Displaying only sane logical drives.\n"); + } + + if (bbsig_disp_flag && fdisk_invalid_bb_sig(epp, &bbsigp)) { + printf("The following logical drives have a wrong boot block" + " signature :\n\n"); + for (i = 0; bbsigp[i]; i++) { + printf("%d ", bbsigp[i]); + } + printf("\n\n"); + printf("They will be corrected when you choose to commit\n"); + bbsig_disp_flag = 0; + } + + printf("Extended partition menu\n"); + + for (;;) { + printf("\nEnter Command (Type h for help) : "); + if ((ext_read_options(buf)) < 0) { + printf("\nCommand Options : \n"); + ext_print_help_menu(); + continue; + } + switch (buf[0]) { + case 'a': + add_logical_drive(); + break; + case 'd': + delete_logical_drive(); + break; + case 'h': + ext_print_help_menu(); + break; + case 'i': + ext_change_logical_drive_id(); + break; + case 'p': + ext_print_logical_drive_layout(); + break; + case 'r': + printf(CLR_SCR); + return; + default : /* NOTREACHED */ + break; + } + } +} +#endif + +#ifdef i386 + +static int +is_linux_swap(uint32_t part_start, off_t *lsm_offset) +{ + int i; + int rval = -1; + off_t seek_offset; + uint32_t linux_pg_size; + char *buf, *linux_swap_magic; + /* + * Known linux kernel page sizes + * The linux swap magic is found as the last 10 bytes of a disk chunk + * at the beginning of the linux swap partition whose size is that of + * kernel page size. + */ + uint32_t linux_pg_size_arr[] = {4096, }; + + if ((buf = calloc(1, sectsiz)) == NULL) { + return (ENOMEM); + } + + linux_swap_magic = buf + sectsiz - LINUX_SWAP_MAGIC_LENGTH; + + for (i = 0; i < sizeof (linux_pg_size_arr)/sizeof (uint32_t); i++) { + linux_pg_size = linux_pg_size_arr[i]; + seek_offset = linux_pg_size/sectsiz - 1; + seek_offset += part_start; + seek_offset *= sectsiz; + + if ((rval = lseek(Dev, seek_offset, SEEK_SET)) < 0) { + break; + } + + if ((rval = read(Dev, buf, sectsiz)) < sectsiz) { + rval = EIO; + break; + } + + if ((strncmp(linux_swap_magic, "SWAP-SPACE", + LINUX_SWAP_MAGIC_LENGTH) == 0) || + (strncmp(linux_swap_magic, "SWAPSPACE2", + LINUX_SWAP_MAGIC_LENGTH) == 0)) { + /* Found a linux swap */ + rval = 0; + *lsm_offset = seek_offset; + break; + } + } + + free(buf); + return (rval); +} + +#endif diff --git a/usr/src/cmd/format/Makefile b/usr/src/cmd/format/Makefile index 1060271ac4..8e475661c1 100644 --- a/usr/src/cmd/format/Makefile +++ b/usr/src/cmd/format/Makefile @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # @@ -53,8 +53,11 @@ $(ROOTETCDATA) := FILEMODE = 0644 $(ROOTETCDATA) := OWNER = root $(ROOTETCDATA) := GROUP = sys -LDLIBS += -ladm -lefi -ldiskmgt -lnvpair -ldevid +LDLIBS_i386= -lfdisk +LDLIBS_sparc= +LDLIBS += -ladm -lefi -ldiskmgt -lnvpair -ldevid $(LDLIBS_$(MACH)) +LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2 CPPFLAGS += -D_EXTVTOC .KEEP_STATE: diff --git a/usr/src/cmd/format/menu_fdisk.c b/usr/src/cmd/format/menu_fdisk.c index e1affd636a..7970ae778f 100644 --- a/usr/src/cmd/format/menu_fdisk.c +++ b/usr/src/cmd/format/menu_fdisk.c @@ -38,6 +38,9 @@ #include <sys/dktp/fdisk.h> #include <sys/stat.h> #include <sys/dklabel.h> +#ifdef i386 +#include <libfdisk.h> +#endif #include "main.h" #include "analyze.h" @@ -105,6 +108,9 @@ static int get_solaris_part(); #endif /* __STDC__ */ +#ifdef i386 +int extpart_init(ext_part_t **epp); +#endif /* * Handling the alignment problem of struct ipart. */ @@ -274,22 +280,23 @@ open_cur_file(int mode) char pbuf[MAXPATHLEN]; switch (mode) { - case FD_USE_P0_PATH: - (void) get_pname(&pbuf[0]); - dkpath = pbuf; - break; - case FD_USE_CUR_DISK_PATH: - if (cur_disk->fdisk_part.systid == SUNIXOS || - cur_disk->fdisk_part.systid == SUNIXOS2) { - (void) get_sname(&pbuf[0]); + case FD_USE_P0_PATH: + (void) get_pname(&pbuf[0]); dkpath = pbuf; - } else { - dkpath = cur_disk->disk_path; - } - break; - default: - err_print("Error: Invalid mode option for opening cur_file\n"); - fullabort(); + break; + case FD_USE_CUR_DISK_PATH: + if (cur_disk->fdisk_part.systid == SUNIXOS || + cur_disk->fdisk_part.systid == SUNIXOS2) { + (void) get_sname(&pbuf[0]); + dkpath = pbuf; + } else { + dkpath = cur_disk->disk_path; + } + break; + default: + err_print("Error: Invalid mode option for opening " + "cur_file\n"); + fullabort(); } /* Close previous cur_file */ @@ -441,6 +448,12 @@ get_solaris_part(int fd, struct ipart *ipart) char *mbr; char *bootptr; struct dk_label update_label; + ushort_t found = 0; +#ifdef i386 + uint32_t relsec, numsec; + int pno, rval, ext_part_found = 0; + ext_part_t *epp; +#endif (void) lseek(fd, 0, 0); @@ -474,6 +487,41 @@ get_solaris_part(int fd, struct ipart *ipart) bootptr = &boot_sec.parts[ipc]; (void) fill_ipart(bootptr, &ip); +#ifdef i386 + if (fdisk_is_dos_extended(ip.systid) && (ext_part_found == 0)) { + /* We support only one extended partition per disk */ + ext_part_found = 1; + (void) extpart_init(&epp); + rval = fdisk_get_solaris_part(epp, &pno, &relsec, + &numsec); + if (rval == FDISK_SUCCESS) { + /* + * Found a solaris partition inside the + * extended partition. Update the statistics. + */ + if (nhead != 0 && nsect != 0) { + pcyl = numsec / (nhead * nsect); + xstart = relsec / (nhead * nsect); + ncyl = pcyl - acyl; + } + solaris_offset = relsec; + found = 2; + ip.bootid = 0; + ip.beghead = ip.begsect = ip.begcyl = 0xff; + ip.endhead = ip.endsect = ip.endcyl = 0xff; + ip.systid = SUNIXOS2; + ip.relsect = relsec; + ip.numsect = numsec; + ipart->bootid = ip.bootid; + status = bcmp(&ip, ipart, + sizeof (struct ipart)); + bcopy(&ip, ipart, sizeof (struct ipart)); + } + libfdisk_fini(&epp); + continue; + } +#endif + /* * we are interested in Solaris and EFI partition types */ @@ -493,29 +541,31 @@ get_solaris_part(int fd, struct ipart *ipart) #ifdef DEBUG else { err_print("Critical geometry values are zero:\n" - "\tnhead = %d; nsect = %d\n", nhead, - nsect); + "\tnhead = %d; nsect = %d\n", nhead, nsect); } #endif /* DEBUG */ solaris_offset = (uint_t)lel(ip.relsect); + found = 1; break; } } - if (i == FD_NUMPART) { + if (!found) { err_print("Solaris fdisk partition not found\n"); return (-1); - } - - /* - * compare the previous and current Solaris partition - * but don't use bootid in determination of Solaris partition changes - */ - ipart->bootid = ip.bootid; - status = bcmp(&ip, ipart, sizeof (struct ipart)); + } else if (found == 1) { + /* + * Found a primary solaris partition. + * compare the previous and current Solaris partition + * but don't use bootid in determination of Solaris partition + * changes + */ + ipart->bootid = ip.bootid; + status = bcmp(&ip, ipart, sizeof (struct ipart)); - bcopy(&ip, ipart, sizeof (struct ipart)); + bcopy(&ip, ipart, sizeof (struct ipart)); + } /* if the disk partitioning has changed - get the VTOC */ if (status) { @@ -570,7 +620,6 @@ get_solaris_part(int fd, struct ipart *ipart) nsect = cur_dtype->dtype_nsect; nhead = cur_dtype->dtype_nhead; } - return (0); } @@ -586,6 +635,11 @@ copy_solaris_part(struct ipart *ipart) char buf[MAXPATHLEN]; char *bootptr; struct stat statbuf; +#ifdef i386 + uint32_t relsec, numsec; + int pno, rval, ext_part_found = 0; + ext_part_t *epp; +#endif (void) get_pname(&buf[0]); if (stat(buf, &statbuf) == -1 || @@ -641,6 +695,36 @@ copy_solaris_part(struct ipart *ipart) bootptr = &mboot.parts[ipc]; (void) fill_ipart(bootptr, &ip); +#ifdef i386 + if (fdisk_is_dos_extended(ip.systid) && (ext_part_found == 0)) { + /* We support only one extended partition per disk */ + ext_part_found = 1; + (void) extpart_init(&epp); + rval = fdisk_get_solaris_part(epp, &pno, &relsec, + &numsec); + if (rval == FDISK_SUCCESS) { + /* + * Found a solaris partition inside the + * extended partition. Update the statistics. + */ + if (nhead != 0 && nsect != 0) { + pcyl = numsec / (nhead * nsect); + ncyl = pcyl - acyl; + } + solaris_offset = relsec; + ip.bootid = 0; + ip.beghead = ip.begsect = ip.begcyl = 0xff; + ip.endhead = ip.endsect = ip.endcyl = 0xff; + ip.systid = SUNIXOS2; + ip.relsect = relsec; + ip.numsect = numsec; + bcopy(&ip, ipart, sizeof (struct ipart)); + } + libfdisk_fini(&epp); + continue; + } +#endif + if (ip.systid == SUNIXOS || ip.systid == SUNIXOS2 || ip.systid == EFI_PMBR) { @@ -660,8 +744,7 @@ copy_solaris_part(struct ipart *ipart) #ifdef DEBUG else { err_print("Critical geometry values are zero:\n" - "\tnhead = %d; nsect = %d\n", nhead, - nsect); + "\tnhead = %d; nsect = %d\n", nhead, nsect); } #endif /* DEBUG */ @@ -685,6 +768,11 @@ auto_solaris_part(struct dk_label *label) struct ipart ip; char *bootptr; char pbuf[MAXPATHLEN]; +#ifdef i386 + uint32_t relsec, numsec; + int pno, rval, ext_part_found = 0; + ext_part_t *epp; +#endif (void) get_pname(&pbuf[0]); if ((fd = open_disk(pbuf, O_RDONLY)) < 0) { @@ -720,6 +808,33 @@ auto_solaris_part(struct dk_label *label) bootptr = &mboot.parts[ipc]; (void) fill_ipart(bootptr, &ip); +#ifdef i386 + if (fdisk_is_dos_extended(ip.systid) && (ext_part_found == 0)) { + /* We support only one extended partition per disk */ + ext_part_found = 1; + (void) extpart_init(&epp); + rval = fdisk_get_solaris_part(epp, &pno, &relsec, + &numsec); + if (rval == FDISK_SUCCESS) { + /* + * Found a solaris partition inside the + * extended partition. Update the statistics. + */ + if ((label->dkl_nhead != 0) && + (label->dkl_nsect != 0)) { + label->dkl_pcyl = + numsec / (label->dkl_nhead * + label->dkl_nsect); + label->dkl_ncyl = label->dkl_pcyl - + label->dkl_acyl; + } + solaris_offset = relsec; + } + libfdisk_fini(&epp); + continue; + } +#endif + /* * if the disk has an EFI label, the nhead and nsect fields * the label may be zero. This protects us from FPE's, and @@ -786,3 +901,49 @@ good_fdisk() return (0); } } + +#ifdef i386 +int +extpart_init(ext_part_t **epp) +{ + int rval, lf_op_flag = 0; + char p0_path[MAXPATHLEN]; + + get_pname(&p0_path[0]); + lf_op_flag |= FDISK_READ_DISK; + if ((rval = libfdisk_init(epp, p0_path, NULL, lf_op_flag)) != + FDISK_SUCCESS) { + switch (rval) { + /* + * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can + * be considered as soft errors and hence + * we do not exit + */ + case FDISK_EBADLOGDRIVE: + break; + case FDISK_ENOLOGDRIVE: + break; + case FDISK_ENOVGEOM: + err_print("Could not get virtual geometry for" + " this device\n"); + fullabort(); + break; + case FDISK_ENOPGEOM: + err_print("Could not get physical geometry for" + " this device\n"); + fullabort(); + break; + case FDISK_ENOLGEOM: + err_print("Could not get label geometry for " + " this device\n"); + fullabort(); + break; + default: + err_print("Failed to initialise libfdisk.\n"); + fullabort(); + break; + } + } + return (0); +} +#endif diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index 48fdf92273..934df8f983 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -263,7 +263,8 @@ SUBDIRS += \ i386_SUBDIRS= \ libntfs \ - libparted + libparted \ + libfdisk sparc_SUBDIRS= .WAIT \ efcode \ @@ -366,6 +367,8 @@ sparc_MSGSUBDIRS= \ libprtdiag \ libprtdiag_psr +i386_MSGSUBDIRS= libfdisk + HDRSUBDIRS= \ auditd_plugins \ libast \ @@ -491,7 +494,8 @@ $(CLOSED_BUILD)HDRSUBDIRS += \ $(CLOSED)/lib/smartcard i386_HDRSUBDIRS= \ - libparted + libparted \ + libfdisk sparc_HDRSUBDIRS= \ libds \ diff --git a/usr/src/lib/libfdisk/Makefile b/usr/src/lib/libfdisk/Makefile new file mode 100644 index 0000000000..a0b6cf09ca --- /dev/null +++ b/usr/src/lib/libfdisk/Makefile @@ -0,0 +1,64 @@ +# +# 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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# + +include ../Makefile.lib + +LIBRARY= libfdisk.a +VERS= .1 + +HDRS= libfdisk.h + +HDRDIR= $(MACH) + +all:= TARGET= all +install:= TARGET= install +clean:= TARGET= clean +clobber:= TARGET= clobber +lint:= TARGET= lint +_msg:= TARGET= _msg + +.KEEP_STATE: + +SUBDIRS= $(MACH) + +all install clean clobber lint: $(SUBDIRS) + + +# install rule for install_h target + +install_h: $(ROOTHDRS) + +check: $(CHECKHDRS) + +_msg: $(MSGSUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../Makefile.targ +include ../../Makefile.msg.targ diff --git a/usr/src/lib/libfdisk/i386/Makefile b/usr/src/lib/libfdisk/i386/Makefile new file mode 100644 index 0000000000..7063a2d944 --- /dev/null +++ b/usr/src/lib/libfdisk/i386/Makefile @@ -0,0 +1,83 @@ +# +# 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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# + +HDRS = libfdisk.h +ROOTHDRDIR= $(ROOT)/usr/include +ROOTHDRS= $(HDRS:%=$(ROOTHDRDIR)/%) +CHECKDIRS= $(HDRS:%.h=%.check) +HDRDIR = ./ + +$(ROOTHDRDIR)/%: % + $(INS.file) + +all := TARGET = all +install_h:= TARGET = install_h + +install_h: $(ROOTHDRS) + +LIBRARY= libfdisk.a +VERS= .1 + +PICS= pics/libfdisk.o + +pics/%.o: %.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +OBJECTS= \ +libfdisk.o + +# include library definitions +include ../../Makefile.lib + +# install this library in the root filesystem +include ../../Makefile.rootfs + +SRCDIR = . + +C99MODE= $(C99_DISABLE) + +MAPFILES += mapfile-vers + +CPPFLAGS += -I. +LDLIBS += -lc + +i386_CFLAGS += -D_LARGEFILE64_SOURCE +i386_CFLAGS += -D_FILE_OFFSET_BITS=64 + +.KEEP_STATE: + +LIBS= $(DYNLIB) $(LINTLIB) + +all: $(LIBS) + +lint: lintcheck + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) + + +# include library targets +include ../../Makefile.targ diff --git a/usr/src/lib/libfdisk/i386/libfdisk.c b/usr/src/lib/libfdisk/i386/libfdisk.c new file mode 100644 index 0000000000..fa273753e1 --- /dev/null +++ b/usr/src/lib/libfdisk/i386/libfdisk.c @@ -0,0 +1,1372 @@ +/* + * 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 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systeminfo.h> +#include <sys/efi_partition.h> +#include <sys/byteorder.h> + +#include <sys/vtoc.h> +#include <sys/tty.h> +#include <sys/dktp/fdisk.h> +#include <sys/dkio.h> +#include <sys/mnttab.h> +#include "libfdisk.h" + +#define DEFAULT_PATH_PREFIX "/dev/rdsk/" + +static void fdisk_free_ld_nodes(ext_part_t *epp); +static void fdisk_ext_place_in_sorted_list(ext_part_t *epp, + logical_drive_t *newld); +static void fdisk_ext_remove_from_sorted_list(ext_part_t *epp, + logical_drive_t *delld); +static int fdisk_ext_overlapping_parts(ext_part_t *epp, uint32_t begsec, + uint32_t endsec); +static int fdisk_read_extpart(ext_part_t *epp); +static void fdisk_set_CHS_values(ext_part_t *epp, struct ipart *part); +static int fdisk_init_master_part_table(ext_part_t *epp); +static struct ipart *fdisk_alloc_part_table(); +static int fdisk_read_master_part_table(ext_part_t *epp); + +static int +fdisk_init_disk_geom(ext_part_t *epp) +{ + struct dk_geom disk_geom; + struct dk_minfo disk_info; + int no_virtgeom_ioctl = 0, no_physgeom_ioctl = 0; + + /* Get disk's HBA (virtual) geometry */ + errno = 0; + if (ioctl(epp->dev_fd, DKIOCG_VIRTGEOM, &disk_geom)) { + if (errno == ENOTTY) { + 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). + */ + epp->disk_geom.virt_cyl = epp->disk_geom.virt_heads = + epp->disk_geom.virt_sec = 0; + } else { + return (FDISK_ENOVGEOM); + } + } else { + /* save virtual geometry values obtained by ioctl */ + epp->disk_geom.virt_cyl = disk_geom.dkg_ncyl; + epp->disk_geom.virt_heads = disk_geom.dkg_nhead; + epp->disk_geom.virt_sec = disk_geom.dkg_nsect; + } + + errno = 0; + if (ioctl(epp->dev_fd, DKIOCG_PHYGEOM, &disk_geom)) { + if (errno == ENOTTY) { + no_physgeom_ioctl = 1; + } else { + return (FDISK_ENOPGEOM); + } + } + /* + * 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(epp->dev_fd, DKIOCGGEOM, &disk_geom)) { + return (FDISK_ENOLGEOM); + } + } + + epp->disk_geom.phys_cyl = disk_geom.dkg_ncyl; + epp->disk_geom.phys_heads = disk_geom.dkg_nhead; + epp->disk_geom.phys_sec = disk_geom.dkg_nsect; + epp->disk_geom.alt_cyl = disk_geom.dkg_acyl; + + /* + * If DKIOCGMEDIAINFO ioctl succeeds, set the dki_lbsize as the + * size of the sector, else default to 512 + */ + if (ioctl(epp->dev_fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info) < 0) { + /* ioctl failed, falling back to default value of 512 bytes */ + epp->disk_geom.sectsize = 512; + } else { + epp->disk_geom.sectsize = ((disk_info.dki_lbsize) ? + disk_info.dki_lbsize : 512); + } + + /* + * 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 || + disk_geom.dkg_nsect > MAX_SECT) { + epp->disk_geom.virt_sec = MAX_SECT; + epp->disk_geom.virt_heads = MAX_HEAD + 1; + epp->disk_geom.virt_cyl = (epp->disk_geom.phys_cyl * + epp->disk_geom.phys_heads * epp->disk_geom.phys_sec) / + (epp->disk_geom.virt_sec * epp->disk_geom.virt_heads); + } + return (FDISK_SUCCESS); +} + +/* + * Initialise important members of the ext_part_t structure and + * other data structures vital to functionality of libfdisk + */ +int +libfdisk_init(ext_part_t **epp, char *devstr, struct ipart *parttab, int opflag) +{ + ext_part_t *temp; + char *canonp; + struct stat sbuf; + int rval = FDISK_SUCCESS; + + if ((temp = calloc(1, sizeof (ext_part_t))) == NULL) { + return (ENOMEM); + } + canonp = strstr(devstr, DEFAULT_PATH_PREFIX); + if (canonp == NULL) { + (void) snprintf(temp->device_name, sizeof (temp->device_name), + "%s%s", DEFAULT_PATH_PREFIX, devstr); + } else { + (void) strncpy(temp->device_name, devstr, + sizeof (temp->device_name)); + } + /* + * In case of an EFI labeled disk, the device name could be cN[tN]dN. + * There is no pN. So we add "p0" at the end if we do not find it. + */ + if (strrchr(temp->device_name, 'p') == NULL) { + (void) strcat(temp->device_name, "p0"); + } + + if (stat(temp->device_name, &sbuf) != 0) { + free(temp); + return (EINVAL); + } + temp->ld_head = NULL; + temp->sorted_ld_head = NULL; + + if ((temp->dev_fd = open(temp->device_name, O_RDWR, 0666)) < 0) { + free(temp); + return (EINVAL); + } + + if ((temp->mtable = parttab) == NULL) { + if ((rval = fdisk_init_master_part_table(temp)) != + FDISK_SUCCESS) { + return (rval); + } + } + + temp->op_flag = opflag; + + if ((rval = fdisk_init_disk_geom(temp)) != FDISK_SUCCESS) { + return (rval); + } + + *epp = temp; + + if (opflag & FDISK_READ_DISK) { + rval = fdisk_read_extpart(*epp); + } + return (rval); +} + +int +libfdisk_reset(ext_part_t *epp) +{ + int rval = FDISK_SUCCESS; + + fdisk_free_ld_nodes(epp); + epp->first_ebr_is_null = 1; + epp->corrupt_logical_drives = 0; + epp->logical_drive_count = 0; + epp->invalid_bb_sig[0] = 0; + if (epp->op_flag & FDISK_READ_DISK) { + rval = fdisk_read_extpart(epp); + } + return (rval); +} + +void +libfdisk_fini(ext_part_t **epp) +{ + fdisk_free_ld_nodes(*epp); + (void) close((*epp)->dev_fd); + free(*epp); + *epp = NULL; +} + +int +fdisk_is_linux_swap(ext_part_t *epp, uint32_t part_start, off_t *lsm_offset) +{ + int i; + int rval = -1; + off_t seek_offset; + uint32_t linux_pg_size; + char *buf, *linux_swap_magic; + int sec_sz = fdisk_get_disk_geom(epp, PHYSGEOM, SSIZE); + /* + * Known linux kernel page sizes + * The linux swap magic is found as the last 10 bytes of a disk chunk + * at the beginning of the linux swap partition whose size is that of + * kernel page size. + */ + uint32_t linux_pg_size_arr[] = {4096, }; + + if ((buf = calloc(1, sec_sz)) == NULL) { + return (ENOMEM); + } + + linux_swap_magic = buf + sec_sz - LINUX_SWAP_MAGIC_LENGTH; + + for (i = 0; i < sizeof (linux_pg_size_arr)/sizeof (uint32_t); i++) { + linux_pg_size = linux_pg_size_arr[i]; + seek_offset = linux_pg_size/sec_sz - 1; + seek_offset += part_start; + seek_offset *= sec_sz; + + if ((rval = lseek(epp->dev_fd, seek_offset, SEEK_SET)) < 0) { + break; + } + + if ((rval = read(epp->dev_fd, buf, sec_sz)) < sec_sz) { + rval = EIO; + break; + } + + if ((strncmp(linux_swap_magic, "SWAP-SPACE", + LINUX_SWAP_MAGIC_LENGTH) == 0) || + (strncmp(linux_swap_magic, "SWAPSPACE2", + LINUX_SWAP_MAGIC_LENGTH) == 0)) { + /* Found a linux swap */ + rval = 0; + *lsm_offset = seek_offset; + break; + } + } + + free(buf); + return (rval); +} + +int +fdisk_get_solaris_part(ext_part_t *epp, int *pnum, uint32_t *begsec, + uint32_t *numsec) +{ + logical_drive_t *temp = fdisk_get_ld_head(epp); + uint32_t part_start; + int pno; + int rval = -1; + off_t lsmo = 0; + + for (pno = 5; temp != NULL; temp = temp->next, pno++) { + if (fdisk_is_solaris_part(LE_8(temp->parts[0].systid))) { + part_start = temp->abs_secnum + temp->logdrive_offset; + if (fdisk_is_linux_swap(epp, part_start, &lsmo) == 0) { + continue; + } + *pnum = pno; + *begsec = part_start; + *numsec = temp->numsect; + rval = FDISK_SUCCESS; + } + } + return (rval); +} + +int +fdisk_get_part_info(ext_part_t *epp, int pnum, uchar_t *sysid, uint32_t *begsec, + uint32_t *numsec) +{ + logical_drive_t *temp = fdisk_get_ld_head(epp); + int pno; + + if ((pnum < 5) || (pnum >= MAX_EXT_PARTS + 5)) { + return (EINVAL); + } + + for (pno = 5; (pno < pnum) && (temp != NULL); temp = temp->next, pno++) + ; + + if (temp == NULL) { + return (EINVAL); + } + + *sysid = LE_8(temp->parts[0].systid); + *begsec = temp->abs_secnum + temp->logdrive_offset; + *numsec = temp->numsect; + return (FDISK_SUCCESS); +} + +/* + * Allocate a node of type logical_drive_t and return the pointer to it + */ +static logical_drive_t * +fdisk_alloc_ld_node() +{ + logical_drive_t *temp; + + if ((temp = calloc(1, sizeof (logical_drive_t))) == NULL) { + return (NULL); + } + temp->next = NULL; + return (temp); +} + +/* + * Free all the logical_drive_t's allocated during the run + */ +static void +fdisk_free_ld_nodes(ext_part_t *epp) +{ + logical_drive_t *temp; + + for (temp = epp->ld_head; temp != NULL; ) { + temp = epp->ld_head -> next; + free(epp->ld_head); + epp->ld_head = temp; + } + epp->ld_head = NULL; + epp->sorted_ld_head = NULL; +} + +/* + * Find the first free sector within the extended partition + */ +int +fdisk_ext_find_first_free_sec(ext_part_t *epp, uint32_t *first_free_sec) +{ + logical_drive_t *temp; + uint32_t last_free_sec; + + *first_free_sec = epp->ext_beg_sec; + + if (epp->ld_head == NULL) { + return (FDISK_SUCCESS); + } + + /* + * When the first logical drive is out of order, we need to adjust + * first_free_sec accordingly. In this case, the first extended + * partition sector is not free even though the actual logical drive + * does not occupy space from the beginning of the extended partition. + * The next free sector would be the second sector of the extended + * partition. + */ + if (epp->ld_head->abs_secnum > epp->ext_beg_sec + + MAX_LOGDRIVE_OFFSET) { + (*first_free_sec)++; + } + + while (*first_free_sec <= epp->ext_end_sec) { + for (temp = epp->sorted_ld_head; temp != NULL; temp = + temp->sorted_next) { + if (temp->abs_secnum == *first_free_sec) { + *first_free_sec = temp->abs_secnum + + temp->logdrive_offset + temp->numsect; + } + } + + last_free_sec = fdisk_ext_find_last_free_sec(epp, + *first_free_sec); + + if ((last_free_sec - *first_free_sec) < MAX_LOGDRIVE_OFFSET) { + /* + * Minimum size of a partition assumed to be atleast one + * sector. + */ + *first_free_sec = last_free_sec + 1; + continue; + } + + break; + } + + if (*first_free_sec > epp->ext_end_sec) { + return (FDISK_EOOBOUND); + } + + return (FDISK_SUCCESS); +} + +/* + * Find the last free sector within the extended partition given, a beginning + * sector (so that the range - "begsec to last_free_sec" is contiguous) + */ +uint32_t +fdisk_ext_find_last_free_sec(ext_part_t *epp, uint32_t begsec) +{ + logical_drive_t *temp; + uint32_t last_free_sec; + + last_free_sec = epp->ext_end_sec; + for (temp = epp->sorted_ld_head; temp != NULL; + temp = temp->sorted_next) { + if (temp->abs_secnum > begsec) { + last_free_sec = temp->abs_secnum - 1; + break; + } + } + return (last_free_sec); +} + +/* + * Place the given ext_part_t structure in a sorted list, sorted in the + * ascending order of their beginning sectors. + */ +static void +fdisk_ext_place_in_sorted_list(ext_part_t *epp, logical_drive_t *newld) +{ + logical_drive_t *pre, *cur; + + if (newld->abs_secnum < epp->sorted_ld_head->abs_secnum) { + newld->sorted_next = epp->sorted_ld_head; + epp->sorted_ld_head = newld; + return; + } + pre = cur = epp->sorted_ld_head; + + for (; cur != NULL; pre = cur, cur = cur->sorted_next) { + if (newld->abs_secnum < cur->abs_secnum) { + break; + } + } + + newld->sorted_next = cur; + pre->sorted_next = newld; +} + +static void +fdisk_ext_remove_from_sorted_list(ext_part_t *epp, logical_drive_t *delld) +{ + logical_drive_t *pre, *cur; + + if (delld == epp->sorted_ld_head) { + epp->sorted_ld_head = delld->sorted_next; + return; + } + + pre = cur = epp->sorted_ld_head; + + for (; cur != NULL; pre = cur, cur = cur->sorted_next) { + if (cur->abs_secnum == delld->abs_secnum) { + /* Found */ + break; + } + } + + pre->sorted_next = cur->sorted_next; +} + +static int +fdisk_ext_overlapping_parts(ext_part_t *epp, uint32_t begsec, uint32_t endsec) +{ + logical_drive_t *temp; + uint32_t firstsec, lastsec, last_free_sec; + + for (temp = epp->ld_head; temp != NULL; temp = temp->next) { + firstsec = temp->abs_secnum; + lastsec = firstsec + temp->logdrive_offset + temp->numsect - 1; + if ((begsec >= firstsec) && + (begsec <= lastsec)) { + return (1); + } + } + + /* + * Find the maximum possible end sector value + * given a beginning sector value + */ + last_free_sec = fdisk_ext_find_last_free_sec(epp, begsec); + + if (endsec > last_free_sec) { + return (1); + } + return (0); +} + +/* + * Check if the logical drive boundaries are sane + */ +int +fdisk_validate_logical_drive(ext_part_t *epp, uint32_t begsec, + uint32_t offset, uint32_t numsec) +{ + uint32_t endsec; + + endsec = begsec + offset + numsec - 1; + if (begsec < epp->ext_beg_sec || + begsec > epp->ext_end_sec || + endsec < epp->ext_beg_sec || + endsec > epp->ext_end_sec || + endsec < begsec || + fdisk_ext_overlapping_parts(epp, begsec, endsec)) { + return (1); + } + + return (0); +} + +/* + * Procedure to walk through the extended partitions and build a Singly + * Linked List out of the data. + */ +int +fdisk_read_extpart(ext_part_t *epp) +{ + struct ipart *fdp, *ext_fdp; + int i = 0, j = 0, ext_part_found = 0, lpart = 5; + off_t secnum, offset; + logical_drive_t *temp, *ep_ptr; + unsigned char *ext_buf; + int sectsize = epp->disk_geom.sectsize; + + if ((ext_buf = (uchar_t *)malloc(sectsize)) == NULL) { + return (ENOMEM); + } + fdp = epp->mtable; + + for (i = 0; (i < FD_NUMPART) && (!ext_part_found); i++, fdp++) { + if (fdisk_is_dos_extended(LE_8(fdp->systid))) { + ext_part_found = 1; + secnum = LE_32(fdp->relsect); + offset = secnum * sectsize; + epp->ext_beg_sec = secnum; + epp->ext_end_sec = secnum + LE_32(fdp->numsect) - 1; + epp->ext_beg_cyl = + FDISK_SECT_TO_CYL(epp, epp->ext_beg_sec); + epp->ext_end_cyl = + FDISK_SECT_TO_CYL(epp, epp->ext_end_sec); + + /*LINTED*/ + while (B_TRUE) { + if (lseek(epp->dev_fd, offset, SEEK_SET) < 0) { + return (EIO); + } + if (read(epp->dev_fd, ext_buf, sectsize) < + sectsize) { + return (EIO); + } + /*LINTED*/ + ext_fdp = (struct ipart *) + (&ext_buf[FDISK_PART_TABLE_START]); + if ((LE_32(ext_fdp->relsect) == 0) && + (epp->logical_drive_count == 0)) { + /* No logical drives defined */ + epp->first_ebr_is_null = 0; + return (FDISK_ENOLOGDRIVE); + } + + temp = fdisk_alloc_ld_node(); + temp->abs_secnum = secnum; + temp->logdrive_offset = + LE_32(ext_fdp->relsect); + temp ->numsect = LE_32(ext_fdp->numsect); + if (epp->ld_head == NULL) { + /* adding first logical drive */ + if (temp->logdrive_offset > + MAX_LOGDRIVE_OFFSET) { + /* out of order */ + temp->abs_secnum += + temp->logdrive_offset; + temp->logdrive_offset = 0; + } + } + temp->begcyl = + FDISK_SECT_TO_CYL(epp, temp->abs_secnum); + temp->endcyl = FDISK_SECT_TO_CYL(epp, + temp->abs_secnum + + temp->logdrive_offset + + temp->numsect - 1); + + /* + * Check for sanity of logical drives + */ + if (fdisk_validate_logical_drive(epp, + temp->abs_secnum, temp->logdrive_offset, + temp->numsect)) { + epp->corrupt_logical_drives = 1; + free(temp); + return (FDISK_EBADLOGDRIVE); + } + + temp->parts[0] = *ext_fdp; + ext_fdp++; + temp->parts[1] = *ext_fdp; + + if (epp->ld_head == NULL) { + epp->ld_head = temp; + epp->sorted_ld_head = temp; + ep_ptr = temp; + epp->logical_drive_count = 1; + } else { + ep_ptr->next = temp; + ep_ptr = temp; + fdisk_ext_place_in_sorted_list(epp, + temp); + epp->logical_drive_count++; + } + + /*LINTED*/ + if (LE_16((*(uint16_t *)&ext_buf[510])) != + MBB_MAGIC) { + epp->invalid_bb_sig[j++] = lpart; + temp->modified = FDISK_MINOR_WRITE; + } + + if (LE_32(ext_fdp->relsect) == 0) + break; + else { + secnum = LE_32(fdp->relsect) + + LE_32(ext_fdp->relsect); + offset = secnum * sectsize; + } + lpart++; + } + } + } + return (FDISK_SUCCESS); +} + +static int +fdisk_init_master_part_table(ext_part_t *epp) +{ + int rval; + if ((epp->mtable = fdisk_alloc_part_table()) == NULL) { + return (ENOMEM); + } + rval = fdisk_read_master_part_table(epp); + if (rval) { + return (rval); + } + return (FDISK_SUCCESS); +} + +static struct ipart * +fdisk_alloc_part_table() +{ + int size = sizeof (struct ipart); + struct ipart *table; + + if ((table = calloc(4, size)) == NULL) { + return (NULL); + } + + return (table); +} + +/* + * Reads the master fdisk partition table from the device assuming that it has + * a valid table. + * MBR is supposed to be of 512 bytes no matter what the device block size is. + */ +static int +fdisk_read_master_part_table(ext_part_t *epp) +{ + uchar_t buf[512]; + int sectsize = 512; + int size = sizeof (struct ipart); + int cpcnt = FD_NUMPART * size; + + if (lseek(epp->dev_fd, 0, SEEK_SET) < 0) { + return (EIO); + } + if (read(epp->dev_fd, buf, sectsize) < sectsize) { + return (EIO); + } + bcopy(&buf[FDISK_PART_TABLE_START], epp->mtable, cpcnt); + + /*LINTED*/ + if (LE_16((*(uint16_t *)&buf[510])) != MBB_MAGIC) { + return (FDISK_EBADMAGIC); + } + + return (FDISK_SUCCESS); +} + +int +fdisk_ext_part_exists(ext_part_t *epp) +{ + int i; + struct ipart *part_table = epp->mtable; + + if (part_table == NULL) { + /* No extended partition found */ + return (0); + } + + for (i = 0; i < FD_NUMPART; i++) { + if (fdisk_is_dos_extended(LE_8(part_table[i].systid))) { + break; + } + } + + if (i == FD_NUMPART) { + /* No extended partition found */ + return (0); + } + return (1); +} + +int +fdisk_ext_validate_part_start(ext_part_t *epp, uint32_t begcyl, + uint32_t *begsec) +{ + logical_drive_t *temp; + uint32_t first_free_sec; + uint32_t first_free_cyl; + int rval; + + rval = fdisk_ext_find_first_free_sec(epp, &first_free_sec); + if (rval != FDISK_SUCCESS) { + return (rval); + } + + first_free_cyl = FDISK_SECT_TO_CYL(epp, first_free_sec); + if (begcyl == first_free_cyl) { + *begsec = first_free_sec; + return (FDISK_SUCCESS); + } + + /* Check if the cylinder number is beyond the extended partition */ + if ((begcyl < epp->ext_beg_cyl) || (begcyl > epp->ext_end_cyl)) { + return (FDISK_EOOBOUND); + } + + for (temp = epp->ld_head; temp != NULL; temp = temp->next) { + if ((begcyl >= temp->begcyl) && + (begcyl <= temp->endcyl)) { + return (FDISK_EOVERLAP); + } + } + *begsec = FDISK_CYL_TO_SECT(epp, begcyl); + + return (FDISK_SUCCESS); +} + +void +fdisk_change_logical_drive_id(ext_part_t *epp, int pno, uchar_t partid) +{ + logical_drive_t *temp; + int i; + + i = FD_NUMPART + 1; + for (temp = epp->ld_head; i < pno; temp = temp->next, i++) + ; + + temp->parts[0].systid = LE_8(partid); + temp->modified = FDISK_MAJOR_WRITE; +} + +/* + * A couple of special scenarios : + * 1. Since the first logical drive's EBR is always at the beginning of the + * extended partition, any specification that starts the first logical drive + * out of order will need to address the following issue : + * If the beginning of the drive is not coinciding with the beginning of the + * extended partition and : + * a) The start is within MAX_LOGDRIVE_OFFSET, the offset changes from the + * default of 63 to less than 63. + * logdrive_offset is updated to keep track of the space between + * the beginning of the logical drive and extended partition. abs_secnum + * points to the beginning of the extended partition. + * b) The start is greater than MAX_LOGDRIVE_OFFSET, the offset changes from + * the default of 63 to greater than 63. + * logdrive_offset is set to 0. abs_secnum points to the beginning of the + * logical drive, which is at an offset from the extended partition. + */ +void +fdisk_add_logical_drive(ext_part_t *epp, uint32_t begsec, uint32_t endsec, + uchar_t partid) +{ + logical_drive_t *temp, *pre, *cur; + struct ipart *part; + + temp = fdisk_alloc_ld_node(); + temp->abs_secnum = begsec; + temp->logdrive_offset = MAX_LOGDRIVE_OFFSET; + temp->numsect = endsec - begsec + 1 - MAX_LOGDRIVE_OFFSET; + temp->begcyl = FDISK_SECT_TO_CYL(epp, begsec); + temp->endcyl = FDISK_SECT_TO_CYL(epp, endsec); + temp->modified = FDISK_MAJOR_WRITE; + + part = &temp->parts[0]; + part->bootid = 0; + part->systid = LE_8(partid); + part->relsect = MAX_LOGDRIVE_OFFSET; + part->numsect = LE_32(temp->numsect); + + fdisk_set_CHS_values(epp, part); + + if (epp->ld_head == NULL) { + epp->corrupt_logical_drives = 0; + if (begsec != epp->ext_beg_sec) { + part->relsect = LE_32(begsec - epp->ext_beg_sec); + temp->numsect = endsec - begsec + 1; + part->numsect = LE_32(temp->numsect); + if (LE_32(part->relsect) > MAX_LOGDRIVE_OFFSET) { + temp->logdrive_offset = 0; + } else { + temp->abs_secnum = epp->ext_beg_sec; + temp->logdrive_offset = LE_32(part->relsect); + } + } + epp->first_ebr_is_null = 0; + epp->ld_head = temp; + epp->sorted_ld_head = temp; + epp->logical_drive_count = 1; + return; + } + + if (temp->abs_secnum == epp->ext_beg_sec) { + part->relsect = LE_32(LE_32(part->relsect) - 1); + temp->logdrive_offset--; + temp->abs_secnum++; + } + + for (pre = cur = epp->ld_head; cur != NULL; pre = cur, cur = cur->next) + ; + + part = &pre->parts[1]; + part->bootid = 0; + part->systid = LE_8(EXTDOS); + part->relsect = LE_32(temp->abs_secnum - epp->ext_beg_sec); + part->numsect = LE_32(temp->numsect + temp->logdrive_offset); + + fdisk_set_CHS_values(epp, part); + + pre->next = temp; + pre->modified = FDISK_MAJOR_WRITE; + epp->logical_drive_count++; + fdisk_ext_place_in_sorted_list(epp, temp); +} + +/* + * There are 2 cases that need to be handled. + * 1. Deleting the first extended partition : + * The peculiarity of this case is that the offset of the first extended + * partition is always indicated by the entry in the master boot record. + * (MBR). This never changes, unless the extended partition itself is + * deleted. Hence, the location of the first EBR is fixed. + * It is only the logical drive which is deleted. This first EBR now gives + * information of the next logical drive and the info about the subsequent + * extended partition. Hence the "relsect" of the first EBR is modified to + * point to the next logical drive. + * + * 2. Deleting an intermediate extended partition. + * This is quite normal and follows the semantics of a normal linked list + * delete operation. The node being deleted has the information about the + * logical drive that it houses and the location and the size of the next + * extended partition. This informationis transferred to the node previous + * to the node being deleted. + * + */ + +void +fdisk_delete_logical_drive(ext_part_t *epp, int pno) +{ + logical_drive_t *pre, *cur; + int i; + + i = FD_NUMPART + 1; + pre = cur = epp->ld_head; + for (; i < pno; i++) { + pre = cur; + cur = cur->next; + } + + if (cur == epp->ld_head) { + /* Deleting the first logical drive */ + if (cur->next == NULL) { + /* Deleting the only logical drive left */ + free(cur); + epp->ld_head = NULL; + epp->sorted_ld_head = NULL; + epp->logical_drive_count = 0; + epp->first_ebr_is_null = 1; + } else { + pre = epp->ld_head; + cur = pre->next; + cur->parts[0].relsect = + LE_32(LE_32(cur->parts[0].relsect) + + LE_32(pre->parts[1].relsect)); + /* Corner case when partitions are out of order */ + if ((pre->abs_secnum != epp->ext_beg_sec) && + (cur->abs_secnum == epp->ext_beg_sec + 1)) { + cur->logdrive_offset++; + cur->abs_secnum = epp->ext_beg_sec; + } else { + cur->abs_secnum = LE_32(cur->parts[0].relsect) + + epp->ext_beg_sec; + cur->logdrive_offset = 0; + } + fdisk_ext_remove_from_sorted_list(epp, pre); + epp->ld_head = cur; + epp->ld_head->modified = FDISK_MAJOR_WRITE; + epp->logical_drive_count--; + free(pre); + } + } else { + pre->parts[1] = cur->parts[1]; + pre->next = cur->next; + fdisk_ext_remove_from_sorted_list(epp, cur); + pre->modified = FDISK_MAJOR_WRITE; + free(cur); + epp->logical_drive_count--; + } +} + +static void +fdisk_set_CHS_values(ext_part_t *epp, struct ipart *part) +{ + uint32_t lba, cy, hd, sc; + uint32_t sectors = epp->disk_geom.virt_sec; + uint32_t heads = epp->disk_geom.virt_heads; + + lba = LE_32(part->relsect) + epp->ext_beg_sec; + if (lba >= heads * 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; + sc = MAX_SECT; + } else { + cy = lba / sectors / heads; + hd = lba / sectors % heads; + sc = lba % sectors + 1; + } + + part->begcyl = cy & 0xff; + part->beghead = (uchar_t)hd; + part->begsect = (uchar_t)(((cy >> 2) & 0xc0) | sc); + + /* + * This code is identical to the code above + * except that it works on ending CHS values + */ + lba += LE_32(part->numsect - 1); + if (lba >= heads * sectors * MAX_CYL) { + cy = MAX_CYL + 1; + hd = MAX_HEAD; + sc = MAX_SECT; + } else { + cy = lba / sectors / heads; + hd = lba / sectors % heads; + sc = lba % sectors + 1; + } + part->endcyl = cy & 0xff; + part->endhead = (uchar_t)hd; + part->endsect = (uchar_t)(((cy >> 2) & 0xc0) | sc); +} + +static int +read_modify_write_ebr(ext_part_t *epp, unsigned char *ebr_buf, + struct ipart *ebr_tab, uint32_t sec_offset) +{ + off_t seek_offset; + int sectsize = epp->disk_geom.sectsize; + + seek_offset = (off_t)sec_offset * sectsize; + + if (lseek(epp->dev_fd, seek_offset, SEEK_SET) < 0) { + return (EIO); + } + if (read(epp->dev_fd, ebr_buf, sectsize) < sectsize) { + return (EIO); + } + + bzero(&ebr_buf[FDISK_PART_TABLE_START], 4 * sizeof (struct ipart)); + if (ebr_tab != NULL) { + bcopy(ebr_tab, &ebr_buf[FDISK_PART_TABLE_START], + 2 * sizeof (struct ipart)); + } + ebr_buf[510] = 0x55; + ebr_buf[511] = 0xAA; + if (lseek(epp->dev_fd, seek_offset, SEEK_SET) < 0) { + return (EIO); + } + if (write(epp->dev_fd, ebr_buf, sectsize) < sectsize) { + return (EIO); + } + return (0); +} + +/* + * XXX - ZFS mounts not detected. Needs to come in as a feature. + * Currently only /etc/mnttab entries are being checked + */ +int +fdisk_mounted_logical_drives(ext_part_t *epp) +{ + char *part_str, *canonp; + char compare_pdev_str[PATH_MAX]; + char compare_sdev_str[PATH_MAX]; + FILE *fp; + struct mnttab mt; + int part; + int look_for_mounted_slices = 0; + uint32_t begsec, numsec; + + if ((fp = fopen(MNTTAB, "r")) == NULL) { + return (ENOENT); + } + + canonp = epp->device_name + strlen(DEFAULT_PATH_PREFIX); + (void) snprintf(compare_pdev_str, PATH_MAX, "%s%s", "/dev/dsk/", + canonp); + part_str = strrchr(compare_pdev_str, 'p'); + *(part_str + 1) = '\0'; + (void) strcpy(compare_sdev_str, compare_pdev_str); + part_str = strrchr(compare_sdev_str, 'p'); + *part_str = 's'; + + if (fdisk_get_solaris_part(epp, &part, &begsec, &numsec) == + FDISK_SUCCESS) { + if (part > FD_NUMPART) { + /* + * Solaris partition is on a logical drive. Look for + * mounted slices. + */ + look_for_mounted_slices = 1; + } + } + + while (getmntent(fp, &mt) == 0) { + if (strstr(mt.mnt_special, compare_pdev_str) == NULL) { + if (strstr(mt.mnt_special, compare_sdev_str) == NULL) { + continue; + } else { + if (look_for_mounted_slices) { + return (FDISK_EMOUNTED); + } + } + } + + /* + * Get the partition number that is mounted, which would be + * found just beyond the last 'p' in the device string. + * For example, in /dev/dsk/c0t0d0p12, partition number 12 + * is just beyond the last 'p'. + */ + part_str = strrchr(mt.mnt_special, 'p'); + if (part_str != NULL) { + part_str++; + part = atoi(part_str); + /* Extended partition numbers start from 5 */ + if (part >= 5) { + return (FDISK_EMOUNTED); + } + } + } + return (0); +} + +int +fdisk_commit_ext_part(ext_part_t *epp) +{ + logical_drive_t *temp; + int wflag = 0; /* write flag */ + int rval; + int sectsize = epp->disk_geom.sectsize; + unsigned char *ebr_buf; + int ld_count; + uint32_t abs_secnum; + int check_mounts = 0; + off_t lsmo; + char *lsm_buf; + + if ((ebr_buf = (unsigned char *)malloc(sectsize)) == NULL) { + return (ENOMEM); + } + if ((lsm_buf = calloc(1, sectsize)) == NULL) { + return (ENOMEM); + } + + if (epp->first_ebr_is_null) { + /* + * Indicator that the extended partition as a whole was + * modifies (either created or deleted. Must check for mounts + * and must commit + */ + check_mounts = 1; + } + + /* + * Pass1 through the logical drives to make sure that commit of minor + * written block dont get held up due to mounts. + */ + for (temp = epp->ld_head; temp != NULL; temp = temp->next) { + if (temp == epp->ld_head) { + abs_secnum = epp->ext_beg_sec; + } else { + abs_secnum = temp->abs_secnum; + } + if (temp->modified == FDISK_MINOR_WRITE) { + rval = read_modify_write_ebr(epp, ebr_buf, + temp->parts, abs_secnum); + if (rval) { + goto error; + } + temp->modified = 0; + } else if (temp->modified == FDISK_MAJOR_WRITE) { + check_mounts = 1; + } + } + + if (!check_mounts) { + goto skip_check_mounts; + } + + if ((rval = fdisk_mounted_logical_drives(epp)) != 0) { + /* One/more extended partitions are mounted */ + if (ebr_buf) { + free(ebr_buf); + } + if (lsm_buf) { + free(lsm_buf); + } + return (rval); + } + +skip_check_mounts: + + if (epp->first_ebr_is_null) { + rval = read_modify_write_ebr(epp, ebr_buf, NULL, + epp->ext_beg_sec); + if (rval) { + goto error; + } + wflag = 1; + ld_count = 0; + } else { + if (epp->logical_drive_count == 0) { + /* + * Can hit this case when there is just an extended + * partition with no logical drives, and the user + * committed without making any changes + * We dont have anything to commit. Return success + */ + if (ebr_buf) { + free(ebr_buf); + } + if (lsm_buf) { + free(lsm_buf); + } + return (FDISK_SUCCESS); + } + + /* + * Make sure that the first EBR is written with the first + * logical drive's data, which might not be the first in disk + * order. + */ + for (temp = epp->ld_head, ld_count = 0; temp != NULL; + temp = temp->next, ld_count++) { + /* + * Check if the current partition is a solaris old + * partition. In that case, check if it was previously + * a linux swap. If so, overwrite the linux swap magic. + */ + if (temp->parts[0].systid == SUNIXOS) { + uint32_t secnum = temp->abs_secnum + + temp->logdrive_offset; + if (fdisk_is_linux_swap(epp, secnum, + &lsmo) == 0) { + if ((rval = lseek(epp->dev_fd, lsmo, + SEEK_SET)) < 0) { + if (ld_count) { + break; + } + goto error; + } + + if (read(epp->dev_fd, lsm_buf, + sectsize) < sectsize) { + rval = EIO; + if (ld_count) { + break; + } + goto error; + } + + bzero(lsm_buf + sectsize - + LINUX_SWAP_MAGIC_LENGTH, + LINUX_SWAP_MAGIC_LENGTH); + + if ((rval = lseek(epp->dev_fd, lsmo, + SEEK_SET)) < 0) { + if (ld_count) { + break; + } + goto error; + } + + if ((rval = write(epp->dev_fd, lsm_buf, + sectsize)) < sectsize) { + rval = EIO; + if (ld_count) { + break; + } + goto error; + } + } + } + + if (ld_count == 0) { + abs_secnum = epp->ext_beg_sec; + } else { + abs_secnum = temp->abs_secnum; + } + if (temp->modified) { + rval = read_modify_write_ebr(epp, ebr_buf, + temp->parts, abs_secnum); + if (rval) { + if (ld_count) { + /* + * There was atleast one + * write to the disk before + * this failure. Make sure that + * the kernel is notified. + * Issue the ioctl. + */ + break; + } + goto error; + } + if ((!wflag) && (temp->modified == + FDISK_MAJOR_WRITE)) { + wflag = 1; + } + } + } + + if (wflag == 0) { + /* No changes made */ + rval = FDISK_SUCCESS; + goto error; + } + } + + /* Issue ioctl to the driver to update extended partition info */ + rval = ioctl(epp->dev_fd, DKIOCSETEXTPART); +error: + if (ebr_buf) { + free(ebr_buf); + } + if (lsm_buf) { + free(lsm_buf); + } + return (rval); +} + +int +fdisk_init_ext_part(ext_part_t *epp, uint32_t rsect, uint32_t nsect) +{ + epp->first_ebr_is_null = 1; + epp->corrupt_logical_drives = 0; + epp->logical_drive_count = 0; + epp->ext_beg_sec = rsect; + epp->ext_end_sec = rsect + nsect - 1; + epp->ext_beg_cyl = FDISK_SECT_TO_CYL(epp, epp->ext_beg_sec); + epp->ext_end_cyl = FDISK_SECT_TO_CYL(epp, epp->ext_end_sec); + epp->invalid_bb_sig[0] = 0; + return (0); +} + +int +fdisk_delete_ext_part(ext_part_t *epp) +{ + epp->first_ebr_is_null = 1; + /* Clear the logical drive information */ + fdisk_free_ld_nodes(epp); + epp->logical_drive_count = 0; + epp->corrupt_logical_drives = 0; + epp->invalid_bb_sig[0] = 0; + return (0); +} + +int +fdisk_get_disk_geom(ext_part_t *epp, int type, int what) +{ + switch (type) { + case PHYSGEOM: + switch (what) { + case NCYL: + return ((int)epp->disk_geom.phys_cyl); + case NHEADS: + return ((int)epp->disk_geom.phys_heads); + case NSECTPT: + return ((int)epp->disk_geom.phys_sec); + case SSIZE: + return ((int)epp->disk_geom.sectsize); + case ACYL: + return ((int)epp->disk_geom.alt_cyl); + default: + return (EINVAL); + } + case VIRTGEOM: + switch (what) { + case NCYL: + return ((int)epp->disk_geom.virt_cyl); + case NHEADS: + return ((int)epp->disk_geom.virt_heads); + case NSECTPT: + return ((int)epp->disk_geom.virt_sec); + case SSIZE: + return ((int)epp->disk_geom.sectsize); + case ACYL: + return ((int)epp->disk_geom.alt_cyl); + default: + return (EINVAL); + } + default: + return (EINVAL); + } +} + +int +fdisk_invalid_bb_sig(ext_part_t *epp, uchar_t **bbsig_arr) +{ + *bbsig_arr = &(epp->invalid_bb_sig[0]); + return (epp->invalid_bb_sig[0]); +} diff --git a/usr/src/lib/libfdisk/i386/libfdisk.h b/usr/src/lib/libfdisk/i386/libfdisk.h new file mode 100644 index 0000000000..46e16f4bf1 --- /dev/null +++ b/usr/src/lib/libfdisk/i386/libfdisk.h @@ -0,0 +1,304 @@ +/* + * 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 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#ifndef _LIBFDISK_H_ +#define _LIBFDISK_H_ + +#include <limits.h> +#include <sys/dktp/fdisk.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_LOGDRIVE_OFFSET 63 + +#define FDISK_ERRNO 200 +#define FDISK_ETOOLONG (FDISK_ERRNO + 0) +#define FDISK_EOOBOUND (FDISK_ERRNO + 1) +#define FDISK_EZERO (FDISK_ERRNO + 2) +#define FDISK_EOVERLAP (FDISK_ERRNO + 3) +#define FDISK_ENOVGEOM (FDISK_ERRNO + 4) +#define FDISK_ENOPGEOM (FDISK_ERRNO + 5) +#define FDISK_ENOLGEOM (FDISK_ERRNO + 6) +#define FDISK_ENOLOGDRIVE (FDISK_ERRNO + 7) +#define FDISK_EBADLOGDRIVE (FDISK_ERRNO + 8) +#define FDISK_ENOEXTPART (FDISK_ERRNO + 9) +#define FDISK_EBADMAGIC (FDISK_ERRNO + 10) +#define FDISK_EMOUNTED (FDISK_ERRNO + 11) + +#define FDISK_SUCCESS 0 + +#define FDISK_READ_DISK 0x00000001 + +#define LINUX_SWAP_MAGIC_LENGTH 10 +enum { + PHYSGEOM = 0, + VIRTGEOM, + NCYL, + NHEADS, + NSECTPT, + SSIZE, + ACYL +}; + +enum { + FDISK_MINOR_WRITE = 1, + FDISK_MAJOR_WRITE +}; + +#define FDISK_SECTS_PER_CYL(epp) \ + (epp->disk_geom.phys_heads * epp->disk_geom.phys_sec) +#define FDISK_SECT_TO_CYL(epp, x) ((x) / (FDISK_SECTS_PER_CYL(epp))) +#define FDISK_CYL_TO_SECT(epp, x) ((x) * (FDISK_SECTS_PER_CYL(epp))) +#define FDISK_ABS_CYL_NUM(epp, x) (FDISK_SECT_TO_CYL(x) +\ + epp->ext_beg_cyl) + +#define FDISK_CYL_BNDRY_ALIGN(epp, x) (((x) % (FDISK_SECTS_PER_CYL(epp))) ? \ + (((x)/(FDISK_SECTS_PER_CYL(epp))) + 1) :\ + ((x)/(FDISK_SECTS_PER_CYL(epp)))) + +/* + * Extended partition structure : + * +--------------+ + * |+--+ | + * || |----------+---> structure at the beginning of the extended partition + * ||--| | ( Lets call it the EBR - Extended Boot Record ) + * || | +---+---> + * |+--+ | | Logical drive within the extended partition + * |+---------+--+| ( We will plainly call this a logical drive ) + * || || + * || || + * || || + * |+------------+| + * +--------------+ + * + * + * EBR is effectively "struct ipart parts[2]". + * The picture below shows what the EBR contains. The EBR has + * two important pieces of information. The first is the offset and the size + * of the logical drive in this extended partition. The second is the offset + * and size of the next extended partition. The offsets are relative to + * beginning of the first extended partition. These extended partitions are + * arranged like a linked list. + * Note that (currently) only one extended partition can exist in the MBR. + * The system ID of a logical drive within the extended partition cannot be + * that of an extended partition. + * + * +------+ + * | | + * +--------------+ | +-v------------+ + * |+--+ | | |+--+ | + * || |---+ | | || | | + * ||--| | | | ||--| | + * || |---|------+-+ || | | + * |+--+ | | |+--+ | + * |+------v-----+| |+------------+| + * || || || || + * || || || || + * || || || || + * |+------------+| |+------------+| + * +--------------+ +--------------+ + * + */ + +/* + * Main structure used to record changes to the partitions made. + * Changes are not written to disk everytime, but maintained in this structure. + * This information is used when the user chooses to commit the changes. + * A linked list of this structure represents the ondisk partitions. + */ +typedef struct logical_drive { + + /* structure holding the EBR data */ + struct ipart parts[2]; + + /* + * Absolute beginning sector of the extended partition, and hence an + * indicator of where the EBR for this logical drive would go on disk. + * NOTE : In case the first logical drive in this extended partition is + * out of (disk) order, this indicates the beginning of the logical + * drive. The EBR will anyway be at the first sector of the extended + * partition, for the first logical drive. + */ + uint32_t abs_secnum; + + /* + * Offset of the logical drive from the beginning of its extended + * partition + */ + uint32_t logdrive_offset; + + /* Size of the logical drive in sectors */ + uint32_t numsect; + + /* Beginning and ending cylinders of the extended partition */ + uint32_t begcyl, endcyl; + + /* + * Flag to indicate if this record is to be sync'ed to disk. + * It takes two values : FDISK_MAJOR_WRITE and FDISK_MINOR_WRITE + * If it is a minor write, there is no need to update the information + * in the kernel structures. Example of a minor write is correction of + * a corrupt boot signature. + */ + int modified; + + /* + * This pointer points to the next extended partition in the order + * found on disk. + */ + struct logical_drive *next; + + /* + * This pointer points to the next extended partition in a sorted list + * sorted in the ascending order of their beginning cylinders. + */ + struct logical_drive *sorted_next; + +} logical_drive_t; + +typedef struct fdisk_disk_geom { + ushort_t phys_cyl; + ushort_t phys_sec; + ushort_t phys_heads; + ushort_t alt_cyl; + ushort_t virt_cyl; + ushort_t virt_sec; + ushort_t virt_heads; + ushort_t sectsize; +} fdisk_disk_geom_t; + +typedef struct ext_part +{ + /* Structure holding geometry information about the device */ + fdisk_disk_geom_t disk_geom; + + struct ipart *mtable; + + char device_name[PATH_MAX]; + + int dev_fd; + + int op_flag; + + /* + * Head of the in memory structure (singly linked list) of extended + * partition information. + */ + logical_drive_t *ld_head; + logical_drive_t *sorted_ld_head; + + /* Beginning cylinder of the extended partition */ + uint32_t ext_beg_cyl; + + /* Ending cylinder of the extended partition */ + uint32_t ext_end_cyl; + + /* Beginning sector of the extended partition */ + uint32_t ext_beg_sec; + + /* Ending sector of the extended partition */ + uint32_t ext_end_sec; + + /* Count of the number of logical drives in the extended partition */ + int logical_drive_count; + + /* + * Flag to keep track of the update to be made to the Extended Boot + * Record (EBR) when all logical drives are deleted. The EBR is filled + * with zeroes in such a case. + */ + int first_ebr_is_null; + + /* + * Flag to indicate corrupt logical drives. Can happen when a partition + * manager creates an extended partition and does not null the first EBR + * or when important ondisk structures are overwritten by a bad program + */ + int corrupt_logical_drives; + + /* + * The boot block signature 0xAA55 might not be found on some of the + * EBRs. ( Even though the rest of the data might be good ) + * The following array is used to store the list of such logical drive + * numbers. + */ + uchar_t invalid_bb_sig[MAX_EXT_PARTS]; + + /* + * Can add a "next" pointer here in case support for multiple + * extended partitions becomes the standard someday. + * + * struct ext_part *next; + */ +} ext_part_t; + +#define fdisk_get_logical_drive_count(epp) ((epp)->logical_drive_count) +#define fdisk_corrupt_logical_drives(epp) ((epp)->corrupt_logical_drives) +#define fdisk_get_ext_beg_cyl(epp) ((epp)->ext_beg_cyl) +#define fdisk_get_ext_end_cyl(epp) ((epp)->ext_end_cyl) +#define fdisk_get_ext_beg_sec(epp) ((epp)->ext_beg_sec) +#define fdisk_get_ext_end_sec(epp) ((epp)->ext_end_sec) +#define fdisk_get_ld_head(epp) ((epp)->ld_head) +#define fdisk_is_solaris_part(id) (((id) == SUNIXOS) || ((id) == SUNIXOS2)) +#define fdisk_is_dos_extended(id) (((id) == EXTDOS) || ((id) == FDISK_EXTLBA)) + +extern int fdisk_is_linux_swap(ext_part_t *epp, uint32_t part_start, + off_t *lsm_offset); +extern int libfdisk_init(ext_part_t **epp, char *devstr, struct ipart *parttab, + int opflag); +extern int libfdisk_reset(ext_part_t *epp); +extern void libfdisk_fini(ext_part_t **epp); +extern int fdisk_ext_find_first_free_sec(ext_part_t *epp, + uint32_t *first_free_sec); +extern uint32_t fdisk_ext_find_last_free_sec(ext_part_t *epp, uint32_t begsec); +extern int fdisk_ext_part_exists(ext_part_t *epp); +extern int fdisk_validate_logical_drive(ext_part_t *epp, uint32_t begsec, + uint32_t offset, uint32_t numsec); +extern int fdisk_ext_validate_part_start(ext_part_t *epp, uint32_t begcyl, + uint32_t *begsec); +extern int fdisk_get_solaris_part(ext_part_t *epp, int *pnum, uint32_t *begsec, + uint32_t *numsec); +extern int fdisk_get_part_info(ext_part_t *epp, int pnum, uchar_t *sysid, + uint32_t *begsec, uint32_t *numsec); +extern int fdisk_commit_ext_part(ext_part_t *epp); +extern void fdisk_change_logical_drive_id(ext_part_t *epp, int pno, + uchar_t partid); +extern void fdisk_add_logical_drive(ext_part_t *epp, uint32_t begsec, + uint32_t endsec, uchar_t partid); +extern void fdisk_delete_logical_drive(ext_part_t *epp, int pno); +extern int fdisk_init_ext_part(ext_part_t *epp, uint32_t rsect, uint32_t nsect); +extern int fdisk_delete_ext_part(ext_part_t *epp); +extern int fdisk_get_disk_geom(ext_part_t *epp, int type, int what); +extern int fdisk_invalid_bb_sig(ext_part_t *epp, uchar_t **bbsig_arr); +extern int fdisk_mounted_logical_drives(ext_part_t *epp); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBFDISK_H_ */ diff --git a/usr/src/lib/libfdisk/i386/llib-lfdisk b/usr/src/lib/libfdisk/i386/llib-lfdisk new file mode 100644 index 0000000000..ab95e0068d --- /dev/null +++ b/usr/src/lib/libfdisk/i386/llib-lfdisk @@ -0,0 +1,31 @@ +/* + * 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 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * usr/src/lib/libfdisk/llib-lfdisk + */ + +/*LINTLIBRARY*/ +/*PROTOLIB1*/ + +#include <libfdisk.h> diff --git a/usr/src/lib/libfdisk/i386/mapfile-vers b/usr/src/lib/libfdisk/i386/mapfile-vers new file mode 100644 index 0000000000..d135c15ffc --- /dev/null +++ b/usr/src/lib/libfdisk/i386/mapfile-vers @@ -0,0 +1,65 @@ +# +# 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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +SUNWprivate_1.1 { + global: + fdisk_ext_validate_part_start; + fdisk_commit_ext_part; + fdisk_ext_find_last_free_sec; + fdisk_ext_find_first_free_sec; + fdisk_delete_logical_drive; + fdisk_add_logical_drive; + fdisk_is_linux_swap; + fdisk_get_disk_geom; + fdisk_init_ext_part; + fdisk_get_solaris_part; + fdisk_get_part_info; + fdisk_invalid_bb_sig; + libfdisk_init; + libfdisk_reset; + libfdisk_fini; + fdisk_validate_logical_drive; + fdisk_change_logical_drive_id; + fdisk_delete_ext_part; + fdisk_ext_part_exists; + fdisk_mounted_logical_drives; + local: + *; +}; diff --git a/usr/src/pkgdefs/SUNWarc/prototype_i386 b/usr/src/pkgdefs/SUNWarc/prototype_i386 index f88c1eb966..3ed9cf42d8 100644 --- a/usr/src/pkgdefs/SUNWarc/prototype_i386 +++ b/usr/src/pkgdefs/SUNWarc/prototype_i386 @@ -18,7 +18,7 @@ # # CDDL HEADER END # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # @@ -167,3 +167,5 @@ s none usr/ccs/lib/amd64/values-xpg6.o=../../../lib/amd64/values-xpg6.o d none usr/lib/scsi/amd64 755 root bin f none usr/lib/scsi/amd64/llib-lscsi.ln 644 root bin f none usr/lib/scsi/amd64/llib-lses.ln 644 root bin +s none usr/lib/llib-lfdisk=../../lib/llib-lfdisk +s none usr/lib/llib-lfdisk.ln=../../lib/llib-lfdisk.ln diff --git a/usr/src/pkgdefs/SUNWarcr/prototype_i386 b/usr/src/pkgdefs/SUNWarcr/prototype_i386 index 205dccfc54..b1070ea2ac 100644 --- a/usr/src/pkgdefs/SUNWarcr/prototype_i386 +++ b/usr/src/pkgdefs/SUNWarcr/prototype_i386 @@ -18,7 +18,7 @@ # # CDDL HEADER END # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # @@ -100,3 +100,5 @@ f none lib/amd64/llib-lumem.ln 644 root bin f none lib/amd64/llib-luuid.ln 644 root bin f none lib/amd64/llib-luutil.ln 644 root bin f none lib/amd64/llib-lxnet.ln 644 root bin +f none lib/llib-lfdisk 644 root bin +f none lib/llib-lfdisk.ln 644 root bin diff --git a/usr/src/pkgdefs/SUNWcsl/prototype_i386 b/usr/src/pkgdefs/SUNWcsl/prototype_i386 index 0facb5caed..056f61982c 100644 --- a/usr/src/pkgdefs/SUNWcsl/prototype_i386 +++ b/usr/src/pkgdefs/SUNWcsl/prototype_i386 @@ -370,6 +370,8 @@ f none usr/lib/amd64/passwdutil.so.1 755 root bin f none usr/lib/amd64/straddr.so.2 755 root bin s none usr/lib/amd64/straddr.so=straddr.so.2 f none usr/lib/amd64/watchmalloc.so.1 755 root bin +s none usr/lib/libfdisk.so.1=../../lib/libfdisk.so.1 +s none usr/lib/libfdisk.so=../../lib/libfdisk.so.1 d none usr/xpg4/lib/amd64 755 root bin s none usr/xpg4/lib/64=amd64 f none usr/xpg4/lib/amd64/libcurses.so.1 755 root bin diff --git a/usr/src/pkgdefs/SUNWcslr/prototype_i386 b/usr/src/pkgdefs/SUNWcslr/prototype_i386 index 74a8ccbf30..f43e6202ad 100644 --- a/usr/src/pkgdefs/SUNWcslr/prototype_i386 +++ b/usr/src/pkgdefs/SUNWcslr/prototype_i386 @@ -18,7 +18,7 @@ # # CDDL HEADER END # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # This required package information file contains a list of package contents. @@ -172,6 +172,8 @@ s none lib/crypto/64=amd64 f none lib/crypto/amd64/kmf_nss.so.1 755 root bin f none lib/crypto/amd64/kmf_openssl.so.1 755 root bin f none lib/crypto/amd64/kmf_pkcs11.so.1 755 root bin +s none lib/libfdisk.so=libfdisk.so.1 +f none lib/libfdisk.so.1 755 root bin v none lib/libc.so.1 755 root bin d none lib/secure/amd64 755 root bin s none lib/secure/64=amd64 diff --git a/usr/src/pkgdefs/SUNWhea/prototype_i386 b/usr/src/pkgdefs/SUNWhea/prototype_i386 index 3a0e88aa20..bc18983d84 100644 --- a/usr/src/pkgdefs/SUNWhea/prototype_i386 +++ b/usr/src/pkgdefs/SUNWhea/prototype_i386 @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # This required package information file contains a list of package contents. @@ -46,6 +46,7 @@ # # SUNWhea # +f none usr/include/libfdisk.h 644 root bin f none usr/include/asm/atomic.h 644 root bin f none usr/include/asm/bitmap.h 644 root bin f none usr/include/asm/byteorder.h 644 root bin diff --git a/usr/src/uts/common/io/cmlb.c b/usr/src/uts/common/io/cmlb.c index 343b1b965c..040b8c56f9 100644 --- a/usr/src/uts/common/io/cmlb.c +++ b/usr/src/uts/common/io/cmlb.c @@ -39,6 +39,9 @@ #include <sys/efi_partition.h> #include <sys/cmlb.h> #include <sys/cmlb_impl.h> +#if defined(__i386) || defined(__amd64) +#include <sys/fs/dv_node.h> +#endif #include <sys/ddi_impldefs.h> /* @@ -104,6 +107,78 @@ static struct driver_minor_data dk_minor_data[] = { {0} }; +#if defined(__i386) || defined(__amd64) +#if defined(_FIRMWARE_NEEDS_FDISK) +static struct driver_minor_data dk_ext_minor_data[] = { + {"p5", 21, S_IFBLK}, + {"p6", 22, S_IFBLK}, + {"p7", 23, S_IFBLK}, + {"p8", 24, S_IFBLK}, + {"p9", 25, S_IFBLK}, + {"p10", 26, S_IFBLK}, + {"p11", 27, S_IFBLK}, + {"p12", 28, S_IFBLK}, + {"p13", 29, S_IFBLK}, + {"p14", 30, S_IFBLK}, + {"p15", 31, S_IFBLK}, + {"p16", 32, S_IFBLK}, + {"p17", 33, S_IFBLK}, + {"p18", 34, S_IFBLK}, + {"p19", 35, S_IFBLK}, + {"p20", 36, S_IFBLK}, + {"p21", 37, S_IFBLK}, + {"p22", 38, S_IFBLK}, + {"p23", 39, S_IFBLK}, + {"p24", 40, S_IFBLK}, + {"p25", 41, S_IFBLK}, + {"p26", 42, S_IFBLK}, + {"p27", 43, S_IFBLK}, + {"p28", 44, S_IFBLK}, + {"p29", 45, S_IFBLK}, + {"p30", 46, S_IFBLK}, + {"p31", 47, S_IFBLK}, + {"p32", 48, S_IFBLK}, + {"p33", 49, S_IFBLK}, + {"p34", 50, S_IFBLK}, + {"p35", 51, S_IFBLK}, + {"p36", 52, S_IFBLK}, + {"p5,raw", 21, S_IFCHR}, + {"p6,raw", 22, S_IFCHR}, + {"p7,raw", 23, S_IFCHR}, + {"p8,raw", 24, S_IFCHR}, + {"p9,raw", 25, S_IFCHR}, + {"p10,raw", 26, S_IFCHR}, + {"p11,raw", 27, S_IFCHR}, + {"p12,raw", 28, S_IFCHR}, + {"p13,raw", 29, S_IFCHR}, + {"p14,raw", 30, S_IFCHR}, + {"p15,raw", 31, S_IFCHR}, + {"p16,raw", 32, S_IFCHR}, + {"p17,raw", 33, S_IFCHR}, + {"p18,raw", 34, S_IFCHR}, + {"p19,raw", 35, S_IFCHR}, + {"p20,raw", 36, S_IFCHR}, + {"p21,raw", 37, S_IFCHR}, + {"p22,raw", 38, S_IFCHR}, + {"p23,raw", 39, S_IFCHR}, + {"p24,raw", 40, S_IFCHR}, + {"p25,raw", 41, S_IFCHR}, + {"p26,raw", 42, S_IFCHR}, + {"p27,raw", 43, S_IFCHR}, + {"p28,raw", 44, S_IFCHR}, + {"p29,raw", 45, S_IFCHR}, + {"p30,raw", 46, S_IFCHR}, + {"p31,raw", 47, S_IFCHR}, + {"p32,raw", 48, S_IFCHR}, + {"p33,raw", 49, S_IFCHR}, + {"p34,raw", 50, S_IFCHR}, + {"p35,raw", 51, S_IFCHR}, + {"p36,raw", 52, S_IFCHR}, + {0} +}; +#endif /* defined(_FIRMWARE_NEEDS_FDISK) */ +#endif /* if defined(__i386) || defined(__amd64) */ + static struct driver_minor_data dk_minor_data_efi[] = { {"a", 0, S_IFBLK}, {"b", 1, S_IFBLK}, @@ -267,6 +342,12 @@ static int cmlb_dkio_partition(struct cmlb_lun *cl, caddr_t arg, int flag, void *tg_cookie); #if defined(__i386) || defined(__amd64) +static int cmlb_dkio_set_ext_part(struct cmlb_lun *cl, caddr_t arg, int flag, + void *tg_cookie); +static int cmlb_validate_ext_part(struct cmlb_lun *cl, int part, int epart, + uint32_t start, uint32_t size); +static int cmlb_is_linux_swap(struct cmlb_lun *cl, uint32_t part_start, + void *tg_cookie); static int cmlb_dkio_get_virtgeom(struct cmlb_lun *cl, caddr_t arg, int flag); static int cmlb_dkio_get_phygeom(struct cmlb_lun *cl, caddr_t arg, int flag); static int cmlb_dkio_partinfo(struct cmlb_lun *cl, dev_t dev, caddr_t arg, @@ -614,6 +695,9 @@ cmlb_attach(dev_info_t *devi, cmlb_tg_ops_t *tgopsp, int device_type, cl->cl_alter_behavior = alter_behavior; cl->cl_reserved = -1; cl->cl_msglog_flag |= CMLB_ALLOW_2TB_WARN; +#if defined(__i386) || defined(__amd64) + cl->cl_logical_drive_count = 0; +#endif if (!is_removable) { mutex_exit(CMLB_MUTEX(cl)); @@ -945,6 +1029,9 @@ cmlb_partinfo(cmlb_handle_t cmlbhandle, int part, diskaddr_t *nblocksp, struct cmlb_lun *cl = (struct cmlb_lun *)cmlbhandle; int rval; +#if defined(__i386) || defined(__amd64) + int ext_part; +#endif ASSERT(cl != NULL); mutex_enter(CMLB_MUTEX(cl)); @@ -986,8 +1073,17 @@ cmlb_partinfo(cmlb_handle_t cmlbhandle, int part, diskaddr_t *nblocksp, } /* consistent with behavior of sd for getting minor name */ - if (partnamep != NULL) + if (partnamep != NULL) { +#if defined(__i386) || defined(__amd64) +#if defined(_FIRMWARE_NEEDS_FDISK) + if (part > FDISK_P4) { + ext_part = part-FDISK_P4-1; + *partnamep = dk_ext_minor_data[ext_part].name; + } else +#endif +#endif *partnamep = dk_minor_data[part].name; + } } @@ -1072,6 +1168,9 @@ cmlb_ioctl(cmlb_handle_t cmlbhandle, dev_t dev, int cmd, intptr_t arg, case DKIOCSGEOM: case DKIOCSETEFI: case DKIOCSMBOOT: +#if defined(__i386) || defined(__amd64) + case DKIOCSETEXTPART: +#endif break; case DKIOCSVTOC: #if defined(__i386) || defined(__amd64) @@ -1216,7 +1315,12 @@ cmlb_ioctl(cmlb_handle_t cmlbhandle, dev_t dev, int cmd, intptr_t arg, err = ENOTTY; #endif break; - +#if defined(__i386) || defined(__amd64) + case DKIOCSETEXTPART: + cmlb_dbg(CMLB_TRACE, cl, "DKIOCSETEXTPART"); + err = cmlb_dkio_set_ext_part(cl, (caddr_t)arg, flag, tg_cookie); + break; +#endif default: err = ENOTTY; @@ -1661,7 +1765,7 @@ no_solaris_partition: * Note that dkl_cylno is not used for the fdisk map entries, so * we set it to an entirely bogus value. */ - for (count = 0; count < FD_NUMPART; count++) { + for (count = 0; count < FDISK_PARTS; count++) { cl->cl_map[FDISK_P1 + count].dkl_cylno = UINT16_MAX; cl->cl_map[FDISK_P1 + count].dkl_nblk = cl->cl_fmap[count].fmap_nblk; @@ -1869,6 +1973,281 @@ cmlb_resync_geom_caches(struct cmlb_lun *cl, diskaddr_t capacity, } +#if defined(__i386) || defined(__amd64) +/* + * Function: cmlb_update_ext_minor_nodes + * + * Description: Routine to add/remove extended partition device nodes + * + * Arguments: + * cl driver soft state (unit) structure + * num_parts Number of logical drives found on the LUN + * + * Should be called with the mutex held + * + * Return Code: 0 for success + * + * Context: User and Kernel thread + * + */ +static int +cmlb_update_ext_minor_nodes(struct cmlb_lun *cl, int num_parts) +{ + int i, count; + char name[48]; + int instance; + struct driver_minor_data *demdp, *demdpr; + char *devnm; + dev_info_t *pdip; + boolean_t internal; + + ASSERT(mutex_owned(CMLB_MUTEX(cl))); + ASSERT(cl->cl_update_ext_minor_nodes == 1); + + internal = VOID2BOOLEAN( + (cl->cl_alter_behavior & (CMLB_INTERNAL_MINOR_NODES)) != 0); + instance = ddi_get_instance(CMLB_DEVINFO(cl)); + demdp = dk_ext_minor_data; + demdpr = &dk_ext_minor_data[MAX_EXT_PARTS]; + + + if (cl->cl_logical_drive_count) { + for (i = 0; i < cl->cl_logical_drive_count; i++) { + (void) sprintf(name, "%s", demdp->name); + ddi_remove_minor_node(CMLB_DEVINFO(cl), name); + (void) sprintf(name, "%s", demdpr->name); + ddi_remove_minor_node(CMLB_DEVINFO(cl), name); + demdp++; + demdpr++; + } + /* There are existing device nodes. Remove them */ + devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP); + (void) ddi_deviname(cl->cl_devi, devnm); + pdip = ddi_get_parent(cl->cl_devi); + (void) devfs_clean(pdip, devnm + 1, DV_CLEAN_FORCE); + kmem_free(devnm, MAXNAMELEN + 1); + } + + demdp = dk_ext_minor_data; + demdpr = &dk_ext_minor_data[MAX_EXT_PARTS]; + + for (i = 0; i < num_parts; i++) { + (void) sprintf(name, "%s", demdp->name); + if (cmlb_create_minor(CMLB_DEVINFO(cl), name, + demdp->type, + (instance << CMLBUNIT_SHIFT) | demdp->minor, + cl->cl_node_type, NULL, internal) == DDI_FAILURE) { + /* + * Clean up any nodes that may have been + * created, in case this fails in the middle + * of the loop. + */ + ddi_remove_minor_node(CMLB_DEVINFO(cl), NULL); + cl->cl_logical_drive_count = 0; + return (ENXIO); + } + (void) sprintf(name, "%s", demdpr->name); + if (ddi_create_minor_node(CMLB_DEVINFO(cl), name, + demdpr->type, + (instance << CMLBUNIT_SHIFT) | demdpr->minor, + cl->cl_node_type, NULL) == DDI_FAILURE) { + /* + * Clean up any nodes that may have been + * created, in case this fails in the middle + * of the loop. + */ + ddi_remove_minor_node(CMLB_DEVINFO(cl), NULL); + cl->cl_logical_drive_count = 0; + return (ENXIO); + } + demdp++; + demdpr++; + } + + /* Update the cl_map array for logical drives */ + for (count = 0; count < MAX_EXT_PARTS; count++) { + cl->cl_map[FDISK_P4 + 1 + count].dkl_cylno = UINT32_MAX; + cl->cl_map[FDISK_P4 + 1 + count].dkl_nblk = + cl->cl_fmap[FD_NUMPART + count].fmap_nblk; + cl->cl_offset[FDISK_P4 + 1 + count] = + cl->cl_fmap[FD_NUMPART + count].fmap_start; + } + + cl->cl_logical_drive_count = i; + cl->cl_update_ext_minor_nodes = 0; + return (0); +} +/* + * Function: cmlb_validate_ext_part + * + * Description: utility routine to validate an extended partition's + * metadata as found on disk + * + * Arguments: + * cl driver soft state (unit) structure + * part partition number of the extended partition + * epart partition number of the logical drive + * start absolute sector number of the start of the logical + * drive being validated + * size size of logical drive being validated + * + * Return Code: 0 for success + * + * Context: User and Kernel thread + * + * Algorithm : + * Error cases are : + * 1. If start block is lesser than or equal to the end block + * 2. If either start block or end block is beyond the bounadry + * of the extended partition. + * 3. start or end block overlap with existing partitions. + * To check this, first make sure that the start block doesnt + * overlap with existing partitions. Then, calculate the + * possible end block for the given start block that doesnt + * overlap with existing partitions. This can be calculated by + * first setting the possible end block to the end of the + * extended partition (optimistic) and then, checking if there + * is any other partition that lies after the start of the + * partition being validated. If so, set the possible end to + * one block less than the beginning of the next nearest partition + * If the actual end block is greater than the calculated end + * block, we have an overlap. + * + */ +static int +cmlb_validate_ext_part(struct cmlb_lun *cl, int part, int epart, uint32_t start, + uint32_t size) +{ + int i; + uint32_t end = start + size - 1; + uint32_t ext_start = cl->cl_fmap[part].fmap_start; + uint32_t ext_end = ext_start + cl->cl_fmap[part].fmap_nblk - 1; + uint32_t ts, te; + uint32_t poss_end = ext_end; + + if (end <= start) { + return (1); + } + + /* + * Check if the logical drive boundaries are within that of the + * extended partition. + */ + if (start <= ext_start || start > ext_end || end <= ext_start || + end > ext_end) { + return (1); + } + + /* + * epart will be equal to FD_NUMPART if it is the first logical drive. + * There is no need to check for overlaps with other logical drives, + * since it is the only logical drive that we have come across so far. + */ + if (epart == FD_NUMPART) { + return (0); + } + + /* Check for overlaps with existing logical drives */ + i = FD_NUMPART; + ts = cl->cl_fmap[FD_NUMPART].fmap_start; + te = ts + cl->cl_fmap[FD_NUMPART].fmap_nblk - 1; + + while ((i < epart) && ts && te) { + if (start >= ts && start <= te) { + return (1); + } + + if ((ts < poss_end) && (ts > start)) { + poss_end = ts - 1; + } + + i++; + ts = cl->cl_fmap[i].fmap_start; + te = ts + cl->cl_fmap[i].fmap_nblk - 1; + } + + if (end > poss_end) { + return (1); + } + + return (0); +} + + +/* + * Function: cmlb_is_linux_swap + * + * Description: utility routine to verify if a partition is a linux swap + * partition or not. + * + * Arguments: + * cl driver soft state (unit) structure + * part_start absolute sector number of the start of the partition + * being verified + * tg_cookie cookie from target driver to be passed back to target + * driver when we call back to it through tg_ops. + * + * Return Code: 0 for success + * + * Context: User and Kernel thread + * + * Notes: + * The linux swap magic "SWAP-SPACE" or "SWAPSPACE2" is found as the + * last 10 bytes of a disk block whose size is that of the linux page + * size. This disk block is found at the beginning of the swap partition. + */ +static int +cmlb_is_linux_swap(struct cmlb_lun *cl, uint32_t part_start, void *tg_cookie) +{ + int i; + int rval = -1; + uint32_t seek_offset; + uint32_t linux_pg_size; + char *buf, *linux_swap_magic; + int sec_sz = cl->cl_sys_blocksize; + /* Known linux kernel page sizes */ + uint32_t linux_pg_size_arr[] = {4096, }; + + ASSERT(cl != NULL); + ASSERT(mutex_owned(CMLB_MUTEX(cl))); + + if ((buf = kmem_zalloc(sec_sz, KM_NOSLEEP)) == NULL) { + return (ENOMEM); + } + + linux_swap_magic = buf + sec_sz - 10; + + for (i = 0; i < sizeof (linux_pg_size_arr)/sizeof (uint32_t); i++) { + linux_pg_size = linux_pg_size_arr[i]; + seek_offset = linux_pg_size/sec_sz - 1; + seek_offset += part_start; + + mutex_exit(CMLB_MUTEX(cl)); + rval = DK_TG_READ(cl, buf, seek_offset, sec_sz, tg_cookie); + mutex_enter(CMLB_MUTEX(cl)); + + if (rval != 0) { + cmlb_dbg(CMLB_ERROR, cl, + "cmlb_is_linux_swap: disk read err\n"); + rval = EIO; + break; + } + + rval = -1; + + if ((strncmp(linux_swap_magic, "SWAP-SPACE", 10) == 0) || + (strncmp(linux_swap_magic, "SWAPSPACE2", 10) == 0)) { + /* Found a linux swap */ + rval = 0; + break; + } + } + + kmem_free(buf, sec_sz); + return (rval); +} +#endif + /* * Function: cmlb_read_fdisk * @@ -1901,7 +2280,7 @@ cmlb_read_fdisk(struct cmlb_lun *cl, diskaddr_t capacity, void *tg_cookie) struct ipart *fdp; struct mboot *mbp; struct ipart fdisk[FD_NUMPART]; - int i; + int i, k; char sigbuf[2]; caddr_t bufp; int uidx; @@ -1910,6 +2289,13 @@ cmlb_read_fdisk(struct cmlb_lun *cl, diskaddr_t capacity, void *tg_cookie) uint_t solaris_offset; /* offset to solaris part. */ daddr_t solaris_size; /* size of solaris partition */ uint32_t blocksize; +#if defined(__i386) || defined(__amd64) + struct ipart eparts[2]; + struct ipart *efdp1 = &eparts[0]; + struct ipart *efdp2 = &eparts[1]; + int ext_part_exists = 0; + int ld_count = 0; +#endif ASSERT(cl != NULL); ASSERT(mutex_owned(CMLB_MUTEX(cl))); @@ -2024,6 +2410,14 @@ cmlb_read_fdisk(struct cmlb_lun *cl, diskaddr_t capacity, void *tg_cookie) for (fdp = fdisk, i = 0; i < FD_NUMPART; i++, fdp++) { uint32_t relsect; uint32_t numsect; + uchar_t systid; +#if defined(__i386) || defined(__amd64) + /* + * Stores relative block offset from the beginning of the + * Extended Partition. + */ + int ext_relsect = 0; +#endif if (fdp->numsect == 0) { cl->cl_fmap[i].fmap_start = 0; @@ -2039,6 +2433,96 @@ cmlb_read_fdisk(struct cmlb_lun *cl, diskaddr_t capacity, void *tg_cookie) cl->cl_fmap[i].fmap_start = relsect; cl->cl_fmap[i].fmap_nblk = numsect; + cl->cl_fmap[i].fmap_systid = LE_8(fdp->systid); + +#if defined(__i386) || defined(__amd64) + /* Support only one extended partition per LUN */ + if ((fdp->systid == EXTDOS || fdp->systid == FDISK_EXTLBA) && + (ext_part_exists == 0)) { + int j; + uint32_t logdrive_offset; + uint32_t ext_numsect; + uint32_t abs_secnum; + int is_linux_swap; + + ext_part_exists = 1; + + for (j = FD_NUMPART; j < FDISK_PARTS; j++) { + mutex_exit(CMLB_MUTEX(cl)); + rval = DK_TG_READ(cl, bufp, + (relsect + ext_relsect), blocksize, + tg_cookie); + mutex_enter(CMLB_MUTEX(cl)); + + if (rval != 0) { + cmlb_dbg(CMLB_ERROR, cl, + "cmlb_read_fdisk: Extended " + "partition read err\n"); + goto done; + } + /* + * The first ipart entry provides the offset + * at which the logical drive starts off from + * the beginning of the container partition + * and the size of the logical drive. + * The second ipart entry provides the offset + * of the next container partition from the + * beginning of the extended partition. + */ + bcopy(&bufp[FDISK_PART_TABLE_START], eparts, + sizeof (eparts)); + logdrive_offset = LE_32(efdp1->relsect); + ext_numsect = LE_32(efdp1->numsect); + systid = LE_8(efdp1->systid); + if (logdrive_offset <= 0 || ext_numsect <= 0) + break; + abs_secnum = relsect + ext_relsect + + logdrive_offset; + + /* Boundary condition and overlap checking */ + if (cmlb_validate_ext_part(cl, i, j, abs_secnum, + ext_numsect)) { + break; + } + + if ((cl->cl_fmap[j].fmap_start != abs_secnum) || + (cl->cl_fmap[j].fmap_nblk != ext_numsect) || + (cl->cl_fmap[j].fmap_systid != systid)) { + /* + * Indicates change from previous + * partinfo. Need to recreate + * logical device nodes. + */ + cl->cl_update_ext_minor_nodes = 1; + } + cl->cl_fmap[j].fmap_start = abs_secnum; + cl->cl_fmap[j].fmap_nblk = ext_numsect; + cl->cl_fmap[j].fmap_systid = systid; + ld_count++; + + is_linux_swap = 0; + if (efdp1->systid == SUNIXOS) { + if (cmlb_is_linux_swap(cl, abs_secnum, + tg_cookie) == 0) { + is_linux_swap = 1; + } + } + + if ((efdp1->systid == SUNIXOS) || + (efdp1->systid == SUNIXOS2)) { + if ((uidx == -1) && (!is_linux_swap)) { + uidx = 0; + solaris_offset = abs_secnum; + solaris_size = ext_numsect; + } + } + + if ((ext_relsect = LE_32(efdp2->relsect)) == 0) + break; + } + } + +#endif if (fdp->systid != SUNIXOS && fdp->systid != SUNIXOS2 && @@ -2054,12 +2538,38 @@ cmlb_read_fdisk(struct cmlb_lun *cl, diskaddr_t capacity, void *tg_cookie) * then use the first inactive solaris partition id */ if ((uidx == -1) || (fdp->bootid == ACTIVE)) { - uidx = i; - solaris_offset = relsect; - solaris_size = numsect; +#if defined(__i386) || defined(__amd64) + if (cmlb_is_linux_swap(cl, relsect, tg_cookie) != 0) { +#endif + uidx = i; + solaris_offset = relsect; + solaris_size = numsect; +#if defined(__i386) || defined(__amd64) + } +#endif } } - +#if defined(__i386) || defined(__amd64) + if (ld_count < cl->cl_logical_drive_count) { + /* + * Some/all logical drives were deleted. Clear out + * the fmap entries correspoding to those deleted drives. + */ + for (k = ld_count + FD_NUMPART; + k < cl->cl_logical_drive_count + FD_NUMPART; k++) { + cl->cl_fmap[k].fmap_start = 0; + cl->cl_fmap[k].fmap_nblk = 0; + cl->cl_fmap[k].fmap_systid = 0; + } + cl->cl_update_ext_minor_nodes = 1; + } + if (cl->cl_update_ext_minor_nodes) { + rval = cmlb_update_ext_minor_nodes(cl, ld_count); + if (rval != 0) { + goto done; + } + } +#endif cmlb_dbg(CMLB_INFO, cl, "fdisk 0x%x 0x%lx", cl->cl_solaris_offset, cl->cl_solaris_size); done: @@ -4532,6 +5042,30 @@ cmlb_dkio_set_mboot(struct cmlb_lun *cl, caddr_t arg, int flag, void *tg_cookie) } +#if defined(__i386) || defined(__amd64) +/*ARGSUSED*/ +static int +cmlb_dkio_set_ext_part(struct cmlb_lun *cl, caddr_t arg, int flag, + void *tg_cookie) +{ + int fdisk_rval; + diskaddr_t capacity; + + ASSERT(!mutex_owned(CMLB_MUTEX(cl))); + + mutex_enter(CMLB_MUTEX(cl)); + capacity = cl->cl_blockcount; + fdisk_rval = cmlb_read_fdisk(cl, capacity, tg_cookie); + if (fdisk_rval != 0) { + mutex_exit(CMLB_MUTEX(cl)); + return (fdisk_rval); + } + + mutex_exit(CMLB_MUTEX(cl)); + return (fdisk_rval); +} +#endif + /* * Function: cmlb_setup_default_geometry * @@ -4770,7 +5304,7 @@ no_solaris_partition: * Note that dkl_cylno is not used for the fdisk map entries, so * we set it to an entirely bogus value. */ - for (count = 0; count < FD_NUMPART; count++) { + for (count = 0; count < FDISK_PARTS; count++) { cl->cl_map[FDISK_P1 + count].dkl_cylno = UINT32_MAX; cl->cl_map[FDISK_P1 + count].dkl_nblk = cl->cl_fmap[count].fmap_nblk; diff --git a/usr/src/uts/common/io/scsi/targets/sd.c b/usr/src/uts/common/io/scsi/targets/sd.c index 8cbc1310a3..eaba50c764 100644 --- a/usr/src/uts/common/io/scsi/targets/sd.c +++ b/usr/src/uts/common/io/scsi/targets/sd.c @@ -21762,6 +21762,9 @@ sdioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred_p, int *rval_p) case DKIOCSMBOOT: case DKIOCG_PHYGEOM: case DKIOCG_VIRTGEOM: +#if defined(__i386) || defined(__amd64) + case DKIOCSETEXTPART: +#endif /* let cmlb handle it */ goto skip_ready_valid; @@ -21910,6 +21913,9 @@ skip_ready_valid: case DKIOCSMBOOT: case DKIOCG_PHYGEOM: case DKIOCG_VIRTGEOM: +#if defined(__i386) || defined(__amd64) + case DKIOCSETEXTPART: +#endif SD_TRACE(SD_LOG_IOCTL, un, "DKIOC %d\n", cmd); /* TUR should spin up */ diff --git a/usr/src/uts/common/sys/cmlb_impl.h b/usr/src/uts/common/sys/cmlb_impl.h index b77f6c0239..1927123457 100644 --- a/usr/src/uts/common/sys/cmlb_impl.h +++ b/usr/src/uts/common/sys/cmlb_impl.h @@ -35,10 +35,16 @@ extern "C" { #include <sys/ddi.h> #include <sys/sunddi.h> +/* + * FDISK partitions - 4 primary and MAX_EXT_PARTS number of Extended + * Partitions. + */ +#define FDISK_PARTS (FD_NUMPART + MAX_EXT_PARTS) + #if defined(_SUNOS_VTOC_8) #define NSDMAP NDKMAP #elif defined(_SUNOS_VTOC_16) -#define NSDMAP (NDKMAP + FD_NUMPART + 1) +#define NSDMAP (NDKMAP + FDISK_PARTS + 1) #else #error "No VTOC format defined." #endif @@ -111,6 +117,7 @@ extern "C" { struct fmap { ulong_t fmap_start; /* starting block number */ ulong_t fmap_nblk; /* number of blocks */ + uchar_t fmap_systid; /* systid of the partition */ }; /* for cm_state */ @@ -146,7 +153,7 @@ typedef struct cmlb_lun { diskaddr_t cl_offset[MAXPART]; /* partition start blocks */ - struct fmap cl_fmap[FD_NUMPART]; /* fdisk partitions */ + struct fmap cl_fmap[FDISK_PARTS]; /* fdisk partitions */ uchar_t cl_asciilabel[LEN_DKL_ASCII]; /* Disk ASCII label */ @@ -190,6 +197,15 @@ typedef struct cmlb_lun { int cl_device_type; /* DTYPE_DIRECT,.. */ int cl_reserved; /* reserved efi partition # */ cmlb_tg_ops_t *cmlb_tg_ops; +#if defined(__i386) || defined(__amd64) + /* + * Flag indicating whether extended partition nodes should be created + * or not. Is set in cmlb_attach. After creating nodes in + * cmlb_read_fdisk, it will be unset. + */ + int cl_update_ext_minor_nodes; + int cl_logical_drive_count; +#endif /* __i386 || __amd64 */ uint8_t cl_msglog_flag; /* used to enable/suppress */ /* certain log messages */ } cmlb_lun_t; diff --git a/usr/src/uts/common/sys/dkio.h b/usr/src/uts/common/sys/dkio.h index caf7d7976d..8d913d8974 100644 --- a/usr/src/uts/common/sys/dkio.h +++ b/usr/src/uts/common/sys/dkio.h @@ -230,6 +230,11 @@ struct dk_callback { */ #define DKIOCHOTPLUGGABLE (DKIOC|35) /* is hotpluggable */ +#if defined(__i386) || defined(__amd64) +/* ioctl to write extended partition structure into the disk */ +#define DKIOCSETEXTPART (DKIOC|46) +#endif + /* * Ioctl to force driver to re-read the alternate partition and rebuild * the internal defect map. diff --git a/usr/src/uts/common/sys/dktp/fdisk.h b/usr/src/uts/common/sys/dktp/fdisk.h index ea0c1ef3ce..e90135f362 100644 --- a/usr/src/uts/common/sys/dktp/fdisk.h +++ b/usr/src/uts/common/sys/dktp/fdisk.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988 AT&T */ @@ -44,6 +44,18 @@ extern "C" { */ /* + * the MAX values are the maximum usable values for BIOS chs values + * The MAX_CYL value of 1022 is the maximum usable value + * the value of 1023 is a fence value, + * indicating no CHS geometry exists for the corresponding LBA value. + * HEAD range [ 0 .. MAX_HEAD ], so number of heads is (MAX_HEAD + 1) + * SECT range [ 1 .. MAX_SECT ], so number of sectors is (MAX_SECT) + */ +#define MAX_SECT (63) +#define MAX_CYL (1022) +#define MAX_HEAD (254) + +/* * BOOTSZ was reduced from 446 to 440 bytes to NOT overwrite the Windows * Vista DISKID. Otherwise Vista won't boot from Solaris GRUB in a dual-boot * setup. @@ -80,6 +92,7 @@ struct ipart { /* * Values for systid. */ +#define UNUSED 0 /* Empty Partition */ #define DOSOS12 1 /* DOS partition, 12-bit FAT */ #define PCIXOS 2 /* PC/IX partition */ #define DOSOS16 4 /* DOS partition, 16-bit FAT */ @@ -103,7 +116,7 @@ struct ipart { /* raw partition. ID was 0 but conflicted */ /* with DOS 3.3 fdisk */ #define UNIXOS 99 /* UNIX V.x partition */ -#define UNUSED 100 /* unassigned partition */ +#define FDISK_NOVELL2 100 /* Novell Netware 286 */ #define FDISK_NOVELL3 101 /* Novell Netware 3.x and later */ #define FDISK_QNX4 119 /* QNX 4.x */ #define FDISK_QNX42 120 /* QNX 4.x 2nd part */ @@ -139,6 +152,20 @@ struct mboot { /* master boot block */ ushort_t signature; }; +#if defined(__i386) || defined(__amd64) + +/* Byte offset of the start of the partition table within the sector */ +#define FDISK_PART_TABLE_START 446 + +/* Maximum number of valid partitions assumed as 32 */ +#define MAX_EXT_PARTS 32 + +#else + +#define MAX_EXT_PARTS 0 + +#endif /* if defined(__i386) || defined(__amd64) */ + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/common/sys/scsi/targets/sddef.h b/usr/src/uts/common/sys/scsi/targets/sddef.h index 90129e40c3..93705cc42b 100644 --- a/usr/src/uts/common/sys/scsi/targets/sddef.h +++ b/usr/src/uts/common/sys/scsi/targets/sddef.h @@ -93,9 +93,17 @@ extern "C" { #elif defined(_SUNOS_VTOC_16) +/* + * XXX - NSDMAP has multiple definitions, one more in cmlb_impl.h + * If they are coalesced into one, this definition will follow suit. + * FDISK partitions - 4 primary and MAX_EXT_PARTS number of Extended + * Partitions. + */ +#define FDISK_PARTS (FD_NUMPART + MAX_EXT_PARTS) + #define SDUNIT_SHIFT 6 #define SDPART_MASK 63 -#define NSDMAP (NDKMAP + FD_NUMPART + 1) +#define NSDMAP (NDKMAP + FDISK_PARTS + 1) #else #error "No VTOC format defined." diff --git a/usr/src/uts/common/xen/io/xdf.c b/usr/src/uts/common/xen/io/xdf.c index 3e9ccd0b4a..77419266c8 100644 --- a/usr/src/uts/common/xen/io/xdf.c +++ b/usr/src/uts/common/xen/io/xdf.c @@ -2578,6 +2578,7 @@ xdf_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, case DKIOCSMBOOT: case DKIOCGETEFI: case DKIOCSETEFI: + case DKIOCSETEXTPART: case DKIOCPARTITION: return (cmlb_ioctl(vdp->xdf_vd_lbl, dev, cmd, arg, mode, credp, rvalp, NULL)); diff --git a/usr/src/uts/i86pc/i86hvm/io/xdf_shell.c b/usr/src/uts/i86pc/i86hvm/io/xdf_shell.c index d9557a07cb..6255c5ddaf 100644 --- a/usr/src/uts/i86pc/i86hvm/io/xdf_shell.c +++ b/usr/src/uts/i86pc/i86hvm/io/xdf_shell.c @@ -794,8 +794,13 @@ xdfs_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp, rv = xdfs_c_ioctl(xsp, dev, part, cmd, arg, flag, credp, rvalp, &done); if (done) return (rv); - return (ldi_ioctl(xsp->xdfss_tgt_lh[part], - cmd, arg, flag, credp, rvalp)); + rv = ldi_ioctl(xsp->xdfss_tgt_lh[part], cmd, arg, flag, credp, rvalp); + if (rv == 0) { + /* Force Geometry Validation */ + (void) cmlb_invalidate(xsp->xdfss_cmlbhandle, 0); + (void) cmlb_validate(xsp->xdfss_cmlbhandle, 0, 0); + } + return (rv); } static int diff --git a/usr/src/uts/intel/io/dktp/disk/cmdk.c b/usr/src/uts/intel/io/dktp/disk/cmdk.c index 36dddd4a7b..8e144b05a9 100644 --- a/usr/src/uts/intel/io/dktp/disk/cmdk.c +++ b/usr/src/uts/intel/io/dktp/disk/cmdk.c @@ -1002,6 +1002,7 @@ cmdkioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp, int *rvalp) case DKIOCGETEFI: case DKIOCSETEFI: case DKIOCPARTITION: + case DKIOCSETEXTPART: { int rc; |