diff options
author | Brian Behlendorf <[email protected]> | 2009-10-23 12:28:12 -0700 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2009-10-23 12:28:12 -0700 |
commit | e8e3a8ae708dafa39dadbc70092eef7a46a0d83e (patch) | |
tree | 6281cc8efffd3ac87d746de1232fae062d230857 | |
parent | 576d515d079dfdc0cba8b3fa0c5b33a49afa722e (diff) | |
parent | 8a34963bec4b390200928482377697fc9af6a1fa (diff) |
Merge branch 'linux-user-disk' into refs/top-bases/linux-zfs-branch
-rw-r--r-- | cmd/zpool/zpool_vdev.c | 53 | ||||
-rw-r--r-- | lib/libefi/rdwr_efi.c | 62 | ||||
-rw-r--r-- | lib/libzfs/libzfs_pool.c | 98 |
3 files changed, 126 insertions, 87 deletions
diff --git a/cmd/zpool/zpool_vdev.c b/cmd/zpool/zpool_vdev.c index 7d4d87ede..113abb608 100644 --- a/cmd/zpool/zpool_vdev.c +++ b/cmd/zpool/zpool_vdev.c @@ -237,14 +237,9 @@ check_disk(const char *path, blkid_cache cache, int force, int err = 0; int fd, i; - /* Check the device as given */ - err = check_slice(path, cache, force, isspare); - if (err) - return (err); - - /* Additional checking is only required for whole disks */ + /* This is not a wholedisk we only check the given partition */ if (!iswholedisk) - return 0; + return check_slice(path, cache, force, isspare); /* * When the device is a whole disk try to read the efi partition @@ -252,9 +247,9 @@ check_disk(const char *path, blkid_cache cache, int force, * partitions. However, when it fails it may simply be because * the disk is partitioned via the MBR. Since we currently can * not easily decode the MBR return a failure and prompt to the - * user to use --force since we cannot check the partitions. + * user to use force option since we cannot check the partitions. */ - if ((fd = open(path, O_RDONLY|O_NDELAY)) < 0) { + if ((fd = open(path, O_RDWR|O_DIRECT)) < 0) { check_error(errno); return -1; } @@ -265,11 +260,9 @@ check_disk(const char *path, blkid_cache cache, int force, if (force) { return 0; } else { - vdev_error(gettext( - "%s may contain a non-efi partition table " - "describing existing\nfilesystems. If you are " - "sure you want to use this device use the\n" - "force command line option.\n"), path); + vdev_error(gettext("%s does not contain an EFI " + "label but it may contain partition\n" + "information in the MBR.\n"), path); return -1; } } @@ -279,14 +272,18 @@ check_disk(const char *path, blkid_cache cache, int force, * label at the end of the device is intact. Rather than use this * label we should play it safe and treat this as a non efi device. */ - if (!force && vtoc->efi_flags & EFI_GPT_PRIMARY_CORRUPT) { - vdev_error(gettext( - "%s contains a corrupt primary efi partition table. " - "If you are\nsure you want to use this device use " - "the force command line option.\n"), path); + if (vtoc->efi_flags & EFI_GPT_PRIMARY_CORRUPT) { efi_free(vtoc); (void) close(fd); - return -1; + + if (force) { + /* Partitions will no be created using the backup */ + return 0; + } else { + vdev_error(gettext("%s contains a corrupt primary " + "EFI label.\n"), path); + return -1; + } } for (i = 0; i < vtoc->efi_nparts; i++) { @@ -295,15 +292,13 @@ check_disk(const char *path, blkid_cache cache, int force, uuid_is_null((uchar_t *)&vtoc->efi_parts[i].p_guid)) continue; - /* Resolve possible symlink to safely append partition */ - if (realpath(path, slice_path) == NULL) { - (void) fprintf(stderr, - gettext("cannot resolve path '%s'\n"), slice_path); - err = errno; - break; - } + if (strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0) + (void) snprintf(slice_path, sizeof (slice_path), + "%s%s%d", path, "-part", i+1); + else + (void) snprintf(slice_path, sizeof (slice_path), + "%s%d", path, i+1); - sprintf(slice_path, "%s%d", slice_path, i+1); err = check_slice(slice_path, cache, force, isspare); if (err) break; @@ -369,7 +364,7 @@ is_whole_disk(const char *arg) (void) snprintf(path, sizeof (path), "%s%s%s", RDISK_ROOT, strrchr(arg, '/'), BACKUP_SLICE); - if ((fd = open(path, O_RDWR | O_NDELAY)) < 0) + if ((fd = open(path, O_RDWR|O_DIRECT)) < 0) return (B_FALSE); if (efi_alloc_and_init(fd, EFI_NUMPAR, &label) != 0) { (void) close(fd); diff --git a/lib/libefi/rdwr_efi.c b/lib/libefi/rdwr_efi.c index ae6138db8..8df64149e 100644 --- a/lib/libefi/rdwr_efi.c +++ b/lib/libefi/rdwr_efi.c @@ -111,7 +111,7 @@ int efi_debug = 1; int efi_debug = 0; #endif -static int efi_read(int, struct dk_gpt *); +static int efi_read(int, struct dk_gpt *); /* * Return a 32-bit CRC of the contents of the buffer. Pre-and-post @@ -394,12 +394,20 @@ efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc) } error = lseek(fd, dk_ioc->dki_lba * lbsize, SEEK_SET); - if (error == -1) + if (error == -1) { + if (efi_debug) + (void) fprintf(stderr, "DKIOCGETEFI lseek " + "error: %d\n", errno); return error; + } error = read(fd, data, dk_ioc->dki_length); - if (error == -1) + if (error == -1) { + if (efi_debug) + (void) fprintf(stderr, "DKIOCGETEFI read " + "error: %d\n", errno); return error; + } if (error != dk_ioc->dki_length) { if (efi_debug) @@ -421,12 +429,20 @@ efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc) } error = lseek(fd, dk_ioc->dki_lba * lbsize, SEEK_SET); - if (error == -1) + if (error == -1) { + if (efi_debug) + (void) fprintf(stderr, "DKIOCSETEFI lseek " + "error: %d\n", errno); return error; + } error = write(fd, data, dk_ioc->dki_length); - if (error == -1) + if (error == -1) { + if (efi_debug) + (void) fprintf(stderr, "DKIOCSETEFI write " + "error: %d\n", errno); return error; + } if (error != dk_ioc->dki_length) { if (efi_debug) @@ -436,10 +452,15 @@ efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc) return -1; } - error = fdatasync(fd); + /* Sync the new EFI table to disk */ + error = fsync(fd); if (error == -1) return error; + /* Ensure any local disk cache is also flushed */ + if (ioctl(fd, BLKFLSBUF, 0) == -1) + return error; + error = 0; break; @@ -597,9 +618,11 @@ efi_read(int fd, struct dk_gpt *vtoc) } } - if ((dk_ioc.dki_data = calloc(label_len, 1)) == NULL) + if (posix_memalign((void **)&dk_ioc.dki_data, + disk_info.dki_lbsize, label_len)) return (VT_ERROR); + memset(dk_ioc.dki_data, 0, label_len); dk_ioc.dki_length = disk_info.dki_lbsize; user_length = vtoc->efi_nparts; efi = dk_ioc.dki_data; @@ -795,12 +818,14 @@ write_pmbr(int fd, struct dk_gpt *vtoc) int len; len = (vtoc->efi_lbasize == 0) ? sizeof (mb) : vtoc->efi_lbasize; - buf = calloc(len, 1); + if (posix_memalign((void **)&buf, len, len)) + return (VT_ERROR); /* * Preserve any boot code and disk signature if the first block is * already an MBR. */ + memset(buf, 0, len); dk_ioc.dki_lba = 0; dk_ioc.dki_length = len; /* LINTED -- always longlong aligned */ @@ -886,10 +911,9 @@ check_input(struct dk_gpt *vtoc) if ((vtoc->efi_parts[i].p_tag == V_UNASSIGNED) && (vtoc->efi_parts[i].p_size != 0)) { if (efi_debug) { - (void) fprintf(stderr, -"partition %d is \"unassigned\" but has a size of %llu", - i, - vtoc->efi_parts[i].p_size); + (void) fprintf(stderr, "partition %d is " + "\"unassigned\" but has a size of %llu", + i, vtoc->efi_parts[i].p_size); } return (VT_EINVAL); } @@ -902,9 +926,9 @@ check_input(struct dk_gpt *vtoc) if (vtoc->efi_parts[i].p_tag == V_RESERVED) { if (resv_part != -1) { if (efi_debug) { - (void) fprintf(stderr, -"found duplicate reserved partition at %d\n", - i); + (void) fprintf(stderr, "found " + "duplicate reserved partition " + "at %d\n", i); } return (VT_EINVAL); } @@ -955,8 +979,8 @@ check_input(struct dk_gpt *vtoc) (istart <= endsect)) { if (efi_debug) { (void) fprintf(stderr, -"Partition %d overlaps partition %d.", - i, j); + "Partition %d overlaps " + "partition %d.", i, j); } return (VT_EINVAL); } @@ -1106,9 +1130,11 @@ efi_write(int fd, struct dk_gpt *vtoc) * 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) + if (posix_memalign((void **)&dk_ioc.dki_data, + vtoc->efi_lbasize, dk_ioc.dki_length)) return (VT_ERROR); + memset(dk_ioc.dki_data, 0, dk_ioc.dki_length); efi = dk_ioc.dki_data; /* stuff user's input into EFI struct */ diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index 12c4f7704..4c196a7f9 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -1759,23 +1759,14 @@ is_guid_type(zpool_handle_t *zhp, uint64_t guid, const char *type) * the disk to use the new unallocated space. */ static int -zpool_relabel_disk(libzfs_handle_t *hdl, const char *name) +zpool_relabel_disk(libzfs_handle_t *hdl, const char *path) { -#if 0 - char path[MAXPATHLEN]; char errbuf[1024]; int fd, error; - int (*_efi_use_whole_disk)(int); - - if ((_efi_use_whole_disk = (int (*)(int))dlsym(RTLD_DEFAULT, - "efi_use_whole_disk")) == NULL) - return (-1); - (void) snprintf(path, sizeof (path), "%s/%s", RDISK_ROOT, name); - - if ((fd = open(path, O_RDWR | O_NDELAY)) < 0) { + if ((fd = open(path, O_RDWR|O_DIRECT)) < 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot " - "relabel '%s': unable to open device"), name); + "relabel '%s': unable to open device"), path); return (zfs_error(hdl, EZFS_OPENFAILED, errbuf)); } @@ -1784,20 +1775,14 @@ zpool_relabel_disk(libzfs_handle_t *hdl, const char *name) * does not have any unallocated space left. If so, we simply * ignore that error and continue on. */ - error = _efi_use_whole_disk(fd); + error = efi_use_whole_disk(fd); (void) close(fd); if (error && error != VT_ENOSPC) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot " - "relabel '%s': unable to read disk capacity"), name); + "relabel '%s': unable to read disk capacity"), path); return (zfs_error(hdl, EZFS_NOCAP, errbuf)); } return (0); -#else - char errbuf[1024]; - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot " - "relabel '%s/%s': libefi is unsupported"), DISK_ROOT, name); - return (zfs_error(hdl, EZFS_NOTSUP, errbuf)); -#endif } /* @@ -1853,7 +1838,6 @@ zpool_vdev_online(zpool_handle_t *zhp, const char *path, int flags, } if (wholedisk) { - pathname += strlen(DISK_ROOT) + 1; (void) zpool_relabel_disk(zhp->zpool_hdl, pathname); } } @@ -3072,7 +3056,7 @@ read_efi_label(nvlist_t *config, diskaddr_t *sb) (void) snprintf(diskname, sizeof (diskname), "%s%s", RDISK_ROOT, strrchr(path, '/')); - if ((fd = open(diskname, O_RDONLY|O_NDELAY)) >= 0) { + if ((fd = open(diskname, O_RDWR|O_DIRECT)) >= 0) { struct dk_gpt *vtoc; if ((err = efi_alloc_and_read(fd, &vtoc)) >= 0) { @@ -3121,7 +3105,6 @@ find_start_block(nvlist_t *config) int zpool_label_disk_wait(char *path, int timeout) { -#if defined(__linux__) struct stat64 statbuf; int i; @@ -3140,9 +3123,31 @@ zpool_label_disk_wait(char *path, int timeout) } return (ENOENT); -#else - return (0); -#endif +} + +int +zpool_label_disk_check(char *path) +{ + struct dk_gpt *vtoc; + int fd, err; + + if ((fd = open(path, O_RDWR|O_DIRECT)) < 0) + return errno; + + if ((err = efi_alloc_and_read(fd, &vtoc)) != 0) { + (void) close(fd); + return err; + } + + if (vtoc->efi_flags & EFI_GPT_PRIMARY_CORRUPT) { + efi_free(vtoc); + (void) close(fd); + return EIDRM; + } + + efi_free(vtoc); + (void) close(fd); + return 0; } /* @@ -3154,7 +3159,7 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name) { char path[MAXPATHLEN]; struct dk_gpt *vtoc; - int fd; + int rval, fd; size_t resv = EFI_MIN_RESV_SIZE; uint64_t slice_size; diskaddr_t start_block; @@ -3190,14 +3195,13 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name) (void) snprintf(path, sizeof (path), "%s/%s%s", RDISK_ROOT, name, BACKUP_SLICE); - if ((fd = open(path, O_RDWR | O_NDELAY)) < 0) { + if ((fd = open(path, O_RDWR|O_DIRECT)) < 0) { /* * This shouldn't happen. We've long since verified that this * is a valid device. */ - printf("errno =%d\n", errno); - zfs_error_aux(hdl, - dgettext(TEXT_DOMAIN, "unable to open device")); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "unable to open device '%s': %d"), path, errno); return (zfs_error(hdl, EZFS_OPENFAILED, errbuf)); } @@ -3240,7 +3244,7 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name) vtoc->efi_parts[8].p_size = resv; vtoc->efi_parts[8].p_tag = V_RESERVED; - if (efi_write(fd, vtoc) != 0) { + if ((rval = efi_write(fd, vtoc)) != 0) { /* * Some block drivers (like pcata) may not support EFI * GPT labels. Print out a helpful error message dir- @@ -3250,22 +3254,36 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name) (void) close(fd); efi_free(vtoc); - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "try using fdisk(1M) and then provide a specific slice")); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "try using " + "parted(8) and then provide a specific slice: %d"), rval); return (zfs_error(hdl, EZFS_LABELFAILED, errbuf)); } (void) close(fd); efi_free(vtoc); -#if defined(__linux__) - /* Wait for the first expected slice to appear */ + /* Wait for the first expected slice to appear. */ (void) snprintf(path, sizeof (path), "%s/%s%s", DISK_ROOT, name, FIRST_SLICE); - return zpool_label_disk_wait(path, 3000); -#else - return (0); -#endif + rval = zpool_label_disk_wait(path, 3000); + if (rval) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "failed to " + "detect device partitions on '%s': %d"), path, rval); + return (zfs_error(hdl, EZFS_LABELFAILED, errbuf)); + } + + /* We can't be to paranoid. Read the label back and verify it. */ + (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name); + rval = zpool_label_disk_check(path); + if (rval) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "freshly written " + "EFI label on '%s' is damaged. Ensure\nthis device " + "is not in in use, and is functioning properly: %d"), + path, rval); + return (zfs_error(hdl, EZFS_LABELFAILED, errbuf)); + } + + return 0; } static boolean_t |