summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorcg149915 <none@none>2006-11-09 19:05:43 -0800
committercg149915 <none@none>2006-11-09 19:05:43 -0800
commit86bbaf93bd5702bf6546e5d43c3049056db7cb22 (patch)
tree15c016147aaf6422d5c7efb0b9ec5916c6caa9fc /usr/src
parent7d968cb8b4b6274092771b93e94bf88d1ee31c6c (diff)
downloadillumos-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.c98
-rw-r--r--usr/src/uts/common/io/scsi/targets/sd.c47
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);