diff options
| author | cg149915 <none@none> | 2006-11-09 19:05:43 -0800 |
|---|---|---|
| committer | cg149915 <none@none> | 2006-11-09 19:05:43 -0800 |
| commit | 86bbaf93bd5702bf6546e5d43c3049056db7cb22 (patch) | |
| tree | 15c016147aaf6422d5c7efb0b9ec5916c6caa9fc /usr/src | |
| parent | 7d968cb8b4b6274092771b93e94bf88d1ee31c6c (diff) | |
| download | illumos-joyent-86bbaf93bd5702bf6546e5d43c3049056db7cb22.tar.gz | |
6452730 format core dumps if we have a disk with corrupt primary GPT label created pre fix to 6342431
6461930 fix for 6342431 can be improved
Diffstat (limited to 'usr/src')
| -rw-r--r-- | usr/src/lib/libefi/common/rdwr_efi.c | 98 | ||||
| -rw-r--r-- | usr/src/uts/common/io/scsi/targets/sd.c | 47 |
2 files changed, 96 insertions, 49 deletions
diff --git a/usr/src/lib/libefi/common/rdwr_efi.c b/usr/src/lib/libefi/common/rdwr_efi.c index 0c57001d87..cea5ca0f68 100644 --- a/usr/src/lib/libefi/common/rdwr_efi.c +++ b/usr/src/lib/libefi/common/rdwr_efi.c @@ -316,6 +316,7 @@ efi_read(int fd, struct dk_gpt *vtoc) efi_gpe_t *efi_parts; struct dk_cinfo dki_info; uint32_t user_length; + boolean_t legacy_label = B_FALSE; /* * get the partition number for this file descriptor. @@ -386,51 +387,76 @@ efi_read(int fd, struct dk_gpt *vtoc) } } } else if ((rval = check_label(fd, &dk_ioc)) == VT_EINVAL) { - /* no valid label here; try the alternate */ - dk_ioc.dki_lba = disk_info.dki_capacity - 1; + /* + * No valid label here; try the alternate. Note that here + * we just read GPT header and save it into dk_ioc.data, + * Later, we will read GUID partition entry array if we + * can get valid GPT header. + */ + + /* + * This is a workaround for legacy systems. In the past, the + * last sector of SCSI disk was invisible on x86 platform. At + * that time, backup label was saved on the next to the last + * sector. It is possible for users to move a disk from previous + * solaris system to present system. Here, we attempt to search + * legacy backup EFI label first. + */ + dk_ioc.dki_lba = disk_info.dki_capacity - 2; dk_ioc.dki_length = disk_info.dki_lbsize; rval = check_label(fd, &dk_ioc); - if (rval != 0) { + if (rval == VT_EINVAL) { /* - * This is a workaround for legacy systems. - * - * In the past, the last sector of SCSI disk was - * invisible on x86 platform. At that time, backup - * label was saved on the next to the last sector. - * It is possible for users to move a disk from - * previous solaris system to present system. + * we didn't find legacy backup EFI label, try to + * search backup EFI label in the last block. */ - dk_ioc.dki_lba = disk_info.dki_capacity - 2; + dk_ioc.dki_lba = disk_info.dki_capacity - 1; dk_ioc.dki_length = disk_info.dki_lbsize; rval = check_label(fd, &dk_ioc); - if (efi_debug && (rval == 0)) { - (void) fprintf(stderr, - "efi_read: primary label corrupt; " - "using legacy EFI backup label\n"); + if (rval == 0) { + legacy_label = B_TRUE; + if (efi_debug) + (void) fprintf(stderr, + "efi_read: primary label corrupt; " + "using EFI backup label located on" + " the last block\n"); } + } else { + if ((efi_debug) && (rval == 0)) + (void) fprintf(stderr, "efi_read: primary label" + " corrupt; using legacy EFI backup label " + " located on the next to last block\n"); } if (rval == 0) { - if (efi_debug) { - (void) fprintf(stderr, - "efi_read: primary label corrupt; " - "using backup\n"); - } dk_ioc.dki_lba = LE_64(efi->efi_gpt_PartitionEntryLBA); vtoc->efi_flags |= EFI_GPT_PRIMARY_CORRUPT; vtoc->efi_nparts = LE_32(efi->efi_gpt_NumberOfPartitionEntries); + /* - * partitions are between last usable LBA and - * backup partition header + * Partition tables are between backup GPT header + * table and ParitionEntryLBA (the starting LBA of + * the GUID partition entries array). Now that we + * already got valid GPT header and saved it in + * dk_ioc.dki_data, we try to get GUID partition + * entry array here. */ dk_ioc.dki_data++; - dk_ioc.dki_length = disk_info.dki_capacity - - dk_ioc.dki_lba - 1; + if (legacy_label) + dk_ioc.dki_length = disk_info.dki_capacity - 1 - + dk_ioc.dki_lba; + else + dk_ioc.dki_length = disk_info.dki_capacity - 2 - + dk_ioc.dki_lba; dk_ioc.dki_length *= disk_info.dki_lbsize; - if (dk_ioc.dki_length > (len_t)label_len) { + if (dk_ioc.dki_length > + ((len_t)label_len - sizeof (*dk_ioc.dki_data))) { rval = VT_EINVAL; } else { + /* + * read GUID partition entry array + */ rval = efi_ioctl(fd, DKIOCGETEFI, &dk_ioc); } } @@ -679,6 +705,8 @@ efi_write(int fd, struct dk_gpt *vtoc) int i, j; struct dk_cinfo dki_info; int md_flag = 0; + int nblocks; + diskaddr_t lba_backup_gpt_hdr; if (ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) { if (efi_debug) @@ -718,6 +746,18 @@ efi_write(int fd, struct dk_gpt *vtoc) vtoc->efi_lbasize; } + /* + * the number of blocks occupied by GUID partition entry array + */ + nblocks = dk_ioc.dki_length / vtoc->efi_lbasize - 1; + + /* + * Backup GPT header is located on the block after GUID + * partition entry array. Here, we calculate the address + * for backup GPT header. + */ + lba_backup_gpt_hdr = vtoc->efi_last_u_lba + 1 + nblocks; + if ((dk_ioc.dki_data = calloc(dk_ioc.dki_length, 1)) == NULL) return (VT_ERROR); @@ -729,7 +769,7 @@ efi_write(int fd, struct dk_gpt *vtoc) efi->efi_gpt_HeaderSize = LE_32(sizeof (struct efi_gpt)); efi->efi_gpt_Reserved1 = 0; efi->efi_gpt_MyLBA = LE_64(1ULL); - efi->efi_gpt_AlternateLBA = LE_64(vtoc->efi_last_lba); + efi->efi_gpt_AlternateLBA = LE_64(lba_backup_gpt_hdr); efi->efi_gpt_FirstUsableLBA = LE_64(vtoc->efi_first_u_lba); efi->efi_gpt_LastUsableLBA = LE_64(vtoc->efi_last_u_lba); efi->efi_gpt_PartitionEntryLBA = LE_64(2ULL); @@ -826,11 +866,11 @@ efi_write(int fd, struct dk_gpt *vtoc) * now swap MyLBA and AlternateLBA fields and write backup * partition table header */ - dk_ioc.dki_lba = vtoc->efi_last_lba; + dk_ioc.dki_lba = lba_backup_gpt_hdr; dk_ioc.dki_length = vtoc->efi_lbasize; dk_ioc.dki_data--; efi->efi_gpt_AlternateLBA = LE_64(1ULL); - efi->efi_gpt_MyLBA = LE_64(vtoc->efi_last_lba); + efi->efi_gpt_MyLBA = LE_64(lba_backup_gpt_hdr); efi->efi_gpt_PartitionEntryLBA = LE_64(vtoc->efi_last_u_lba + 1); efi->efi_gpt_HeaderCRC32 = 0; efi->efi_gpt_HeaderCRC32 = @@ -842,7 +882,7 @@ efi_write(int fd, struct dk_gpt *vtoc) (void) fprintf(stderr, "write of backup header to block %llu failed, " "errno %d\n", - vtoc->efi_last_lba, + lba_backup_gpt_hdr, errno); } } diff --git a/usr/src/uts/common/io/scsi/targets/sd.c b/usr/src/uts/common/io/scsi/targets/sd.c index cb709be9d3..7034efc6f5 100644 --- a/usr/src/uts/common/io/scsi/targets/sd.c +++ b/usr/src/uts/common/io/scsi/targets/sd.c @@ -117,10 +117,13 @@ char _depends_on[] = "misc/scsi"; * driver to work with disks which are labeled/partitioned * via previous sd, we add workaround as follows: * - * 1) Locate backup EFI label: sd searchs the next to last - * block for backup EFI label if it can't find it on the - * last block; - * 2) Calculate geometry: refer to sd_convert_geometry(), If + * 1) Locate backup EFI label: sd searches the next to last + * block for legacy backup EFI label. If fails, it will + * turn to the last block for backup EFI label; + * 2) Clear backup EFI label: sd first search the last block + * for backup EFI label, and will search the next to last + * block only if failed for the last block. + * 3) Calculate geometry: refer to sd_convert_geometry(), If * capacity increasing by 1 causes disks' capacity to cross * over the limits in table CHS_values, geometry info will * change. This will raise an issue: In case that primary @@ -130,12 +133,13 @@ char _depends_on[] = "misc/scsi"; * geometry will prevent format from finding backup VTOC * labels. To eliminate this side effect for compatibility, * sd uses (capacity -1) to calculate geometry; - * 3) 1TB disks: VTOC uses 32-bit signed int, thus sd doesn't - * support VTOC for a disk which has more than DK_MAX_BLOCKS - * LBAs. However, for exactly 1TB disk, it was treated as - * (1T - 512)B in the past, and could have VTOC. To overcome - * this, if an exactly 1TB disk has solaris fdisk partition, - * it will be allowed to work with sd. + * 4) 1TB disks: some important data structures use 32-bit + * signed long/int (for example, daddr_t), so that sd doesn't + * support a disk with capacity larger than 1TB on 32-bit + * platform. However, for exactly 1TB disk, it was treated as + * (1T - 512)B in the past, and could have valid solaris + * partitions. To workaround this, if an exactly 1TB disk has + * solaris fdisk partition, it will be allowed to work with sd. */ #if (defined(__fibre)) #define SD_DEFAULT_INTERCONNECT_TYPE SD_INTERCONNECT_FIBRE @@ -4345,6 +4349,7 @@ sd_validate_geometry(struct sd_lun *un, int path_flag) int lbasize; uint_t capacity; int count; + int forced_under_1t = 0; ASSERT(un != NULL); ASSERT(mutex_owned(SD_MUTEX(un))); @@ -4431,6 +4436,7 @@ sd_validate_geometry(struct sd_lun *un, int path_flag) "is >1TB and has a VTOC label: use format(1M) to either decrease the"); scsi_log(SD_DEVINFO(un), sd_label, CE_CONT, "size to be < 1TB or relabel the disk with an EFI label"); + forced_under_1t = 1; } else { /* unlabeled disk over 1TB */ #if defined(__i386) || defined(__amd64) @@ -4505,7 +4511,8 @@ sd_validate_geometry(struct sd_lun *un, int path_flag) * must be created by previous sd driver, we have to * treat it as (1T-512)B. */ - if (un->un_blockcount > DK_MAX_BLOCKS) { + if ((un->un_blockcount > DK_MAX_BLOCKS) && + (forced_under_1t != 1)) { un->un_f_capacity_adjusted = 1; un->un_blockcount = DK_MAX_BLOCKS; un->un_map[P0_RAW_DISK].dkl_nblk = DK_MAX_BLOCKS; @@ -5552,23 +5559,23 @@ sd_use_efi(struct sd_lun *un, int path_flag) * data). Depending on the per-Vendor/drive Sense data, * the failed READ can cause many (unnecessary) retries. */ + + /* + * Refer to comments related to off-by-1 at the + * header of this file. Search the next to last + * block for backup EFI label. + */ if ((rval = sd_send_scsi_READ(un, buf, lbasize, - cap - 1, (ISCD(un)) ? SD_PATH_DIRECT_PRIORITY : + cap - 2, (ISCD(un)) ? SD_PATH_DIRECT_PRIORITY : path_flag)) != 0) { goto done_err; } sd_swap_efi_gpt((efi_gpt_t *)buf); if ((rval = sd_validate_efi((efi_gpt_t *)buf)) != 0) { - - /* - * Refer to comments related to off-by-1 at the - * header of this file. Search the next to last - * block for backup EFI label. - */ if ((rval = sd_send_scsi_READ(un, buf, lbasize, - cap - 2, (ISCD(un)) ? SD_PATH_DIRECT_PRIORITY : - path_flag)) != 0) { + cap - 1, (ISCD(un)) ? SD_PATH_DIRECT_PRIORITY : + path_flag)) != 0) { goto done_err; } sd_swap_efi_gpt((efi_gpt_t *)buf); |
