diff options
author | Sheshadri Vasudevan <Sheshadri.Vasudevan@Sun.COM> | 2009-07-02 08:59:40 +0530 |
---|---|---|
committer | Sheshadri Vasudevan <Sheshadri.Vasudevan@Sun.COM> | 2009-07-02 08:59:40 +0530 |
commit | aa1b14e7d68925d80eebf0ce8ed0b9cc55246546 (patch) | |
tree | e9988a3de95ef72f4372375a5b7f951c4b4b44d7 /usr/src | |
parent | 01ef659d9b1ead333ef0adc346e7051f7eae7520 (diff) | |
download | illumos-joyent-aa1b14e7d68925d80eebf0ce8ed0b9cc55246546.tar.gz |
PSARC/2006/379 Solaris on Extended partition
6644364 Extended partitions need to be supported on Solaris
6713308 Macro UNUSED in fdisk.h needs to be changed since id 100 is Novell Netware 286's partition ID
6713318 Need to differentiate between solaris old partition and Linux swap
6745175 Partitions can be created using fdisk table with invalid partition line by "fdisk -F"
6745740 Multiple extended partition can be created by "fdisk -A"
6824622 Logical device node can't be created in HVM host
Diffstat (limited to 'usr/src')
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; |