diff --git a/cmd/zdb/Makefile.am b/cmd/zdb/Makefile.am index 469ff7f9ab..6f0b0cb87d 100644 --- a/cmd/zdb/Makefile.am +++ b/cmd/zdb/Makefile.am @@ -28,4 +28,4 @@ zdb_LDADD = \ $(top_builddir)/lib/libzpool/libzpool.la \ $(top_builddir)/lib/libzfs/libzfs.la -zdb_LDFLAGS = -pthread -lm $(ZLIB) -lrt $(LIBUUID) +zdb_LDFLAGS = -pthread -lm $(ZLIB) -lrt $(LIBUUID) $(LIBBLKID) diff --git a/cmd/zfs/Makefile.am b/cmd/zfs/Makefile.am index 1d21a905a5..4d66e37a22 100644 --- a/cmd/zfs/Makefile.am +++ b/cmd/zfs/Makefile.am @@ -29,4 +29,4 @@ zfs_LDADD = \ $(top_builddir)/lib/libzpool/libzpool.la \ $(top_builddir)/lib/libzfs/libzfs.la -zfs_LDFLAGS = -pthread -lm $(ZLIB) -lrt $(LIBUUID) +zfs_LDFLAGS = -pthread -lm $(ZLIB) -lrt $(LIBUUID) $(LIBBLKID) diff --git a/cmd/zinject/Makefile.am b/cmd/zinject/Makefile.am index 3dc95532c7..7166ed9b4b 100644 --- a/cmd/zinject/Makefile.am +++ b/cmd/zinject/Makefile.am @@ -31,4 +31,4 @@ zinject_LDADD = \ $(top_builddir)/lib/libzpool/libzpool.la \ $(top_builddir)/lib/libzfs/libzfs.la -zinject_LDFLAGS = -pthread -lm $(ZLIB) -lrt $(LIBUUID) +zinject_LDFLAGS = -pthread -lm $(ZLIB) -lrt $(LIBUUID) $(LIBBLKID) diff --git a/cmd/zpool/Makefile.am b/cmd/zpool/Makefile.am index 2900abb2f8..3f30eff47d 100644 --- a/cmd/zpool/Makefile.am +++ b/cmd/zpool/Makefile.am @@ -30,4 +30,4 @@ zpool_LDADD = \ $(top_builddir)/lib/libzpool/libzpool.la \ $(top_builddir)/lib/libzfs/libzfs.la -zpool_LDFLAGS = -pthread -lm $(ZLIB) -lrt $(LIBUUID) +zpool_LDFLAGS = -pthread -lm $(ZLIB) -lrt $(LIBUUID) $(LIBBLKID) diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index 391926b215..ba065f2716 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -1684,12 +1684,6 @@ zpool_do_import(int argc, char **argv) usage(B_FALSE); } - if (searchdirs == NULL) { - searchdirs = safe_malloc(sizeof (char *)); - searchdirs[0] = DISK_ROOT; - nsearch = 1; - } - /* check argument count */ if (do_all) { if (argc != 0) { @@ -1710,7 +1704,8 @@ zpool_do_import(int argc, char **argv) if (argc == 0 && !priv_ineffect(PRIV_SYS_CONFIG)) { (void) fprintf(stderr, gettext("cannot " "discover pools: permission denied\n")); - free(searchdirs); + if (searchdirs != NULL) + free(searchdirs); return (1); } } @@ -1757,7 +1752,8 @@ zpool_do_import(int argc, char **argv) (void) fprintf(stderr, gettext("cannot import '%s': " "no such pool available\n"), argv[0]); } - free(searchdirs); + if (searchdirs != NULL) + free(searchdirs); return (1); } @@ -1852,7 +1848,8 @@ zpool_do_import(int argc, char **argv) error: nvlist_free(props); nvlist_free(pools); - free(searchdirs); + if (searchdirs != NULL) + free(searchdirs); return (err ? 1 : 0); } diff --git a/cmd/zpool/zpool_vdev.c b/cmd/zpool/zpool_vdev.c index 986863ae5d..8dfce7ef8f 100644 --- a/cmd/zpool/zpool_vdev.c +++ b/cmd/zpool/zpool_vdev.c @@ -51,7 +51,7 @@ * * 1. Construct the vdev specification. Performs syntax validation and * makes sure each device is valid. - * 2. Check for devices in use. Using libdiskmgt, makes sure that no + * 2. Check for devices in use. Using libblkid to make sure that no * devices are also in use. Some can be overridden using the 'force' * flag, others cannot. * 3. Check for replication errors if the 'force' flag is not specified. @@ -64,7 +64,6 @@ #include #include #include -#include #include #include #include @@ -75,6 +74,10 @@ #include #include #include +#include +#ifdef HAVE_LIBBLKID +#include +#endif #include "zpool_util.h" @@ -108,168 +111,6 @@ vdev_error(const char *fmt, ...) va_end(ap); } -static void -libdiskmgt_error(int error) -{ - /* - * ENXIO/ENODEV is a valid error message if the device doesn't live in - * /dev/dsk. Don't bother printing an error message in this case. - */ - if (error == ENXIO || error == ENODEV) - return; - - (void) fprintf(stderr, gettext("warning: device in use checking " - "failed: %s\n"), strerror(error)); -} - -/* - * Validate a device, passing the bulk of the work off to libdiskmgt. - */ -static int -check_slice(const char *path, int force, boolean_t wholedisk, boolean_t isspare) -{ - char *msg; - int error = 0; - dm_who_type_t who; - - if (force) - who = DM_WHO_ZPOOL_FORCE; - else if (isspare) - who = DM_WHO_ZPOOL_SPARE; - else - who = DM_WHO_ZPOOL; - - if (dm_inuse((char *)path, &msg, who, &error) || error) { - if (error != 0) { - libdiskmgt_error(error); - return (0); - } else { - vdev_error("%s", msg); - free(msg); - return (-1); - } - } - - /* - * If we're given a whole disk, ignore overlapping slices since we're - * about to label it anyway. - */ - error = 0; - if (!wholedisk && !force && - (dm_isoverlapping((char *)path, &msg, &error) || error)) { - if (error == 0) { - /* dm_isoverlapping returned -1 */ - vdev_error(gettext("%s overlaps with %s\n"), path, msg); - free(msg); - return (-1); - } else if (error != ENODEV) { - /* libdiskmgt's devcache only handles physical drives */ - libdiskmgt_error(error); - return (0); - } - } - - return (0); -} - - -/* - * Validate a whole disk. Iterate over all slices on the disk and make sure - * that none is in use by calling check_slice(). - */ -static int -check_disk(const char *name, dm_descriptor_t disk, int force, int isspare) -{ - dm_descriptor_t *drive, *media, *slice; - int err = 0; - int i; - int ret; - - /* - * Get the drive associated with this disk. This should never fail, - * because we already have an alias handle open for the device. - */ - if ((drive = dm_get_associated_descriptors(disk, DM_DRIVE, - &err)) == NULL || *drive == NULL) { - if (err) - libdiskmgt_error(err); - return (0); - } - - if ((media = dm_get_associated_descriptors(*drive, DM_MEDIA, - &err)) == NULL) { - dm_free_descriptors(drive); - if (err) - libdiskmgt_error(err); - return (0); - } - - dm_free_descriptors(drive); - - /* - * It is possible that the user has specified a removable media drive, - * and the media is not present. - */ - if (*media == NULL) { - dm_free_descriptors(media); - vdev_error(gettext("'%s' has no media in drive\n"), name); - return (-1); - } - - if ((slice = dm_get_associated_descriptors(*media, DM_SLICE, - &err)) == NULL) { - dm_free_descriptors(media); - if (err) - libdiskmgt_error(err); - return (0); - } - - dm_free_descriptors(media); - - ret = 0; - - /* - * Iterate over all slices and report any errors. We don't care about - * overlapping slices because we are using the whole disk. - */ - for (i = 0; slice[i] != NULL; i++) { - char *name = dm_get_name(slice[i], &err); - - if (check_slice(name, force, B_TRUE, isspare) != 0) - ret = -1; - - dm_free_name(name); - } - - dm_free_descriptors(slice); - return (ret); -} - -/* - * Validate a device. - */ -static int -check_device(const char *path, boolean_t force, boolean_t isspare) -{ - dm_descriptor_t desc; - int err; - char *dev; - - /* - * For whole disks, libdiskmgt does not include the leading dev path. - */ - dev = strrchr(path, '/'); - assert(dev != NULL); - dev++; - if ((desc = dm_get_descriptor_by_name(DM_ALIAS, dev, &err)) != NULL) { - err = check_disk(path, desc, force, isspare); - dm_free_descriptor(desc); - return (err); - } - - return (check_slice(path, force, B_FALSE, isspare)); -} - /* * Check that a file is valid. All we can do in this case is check that it's * not in use by another pool, and not in use by swap. @@ -280,19 +121,9 @@ check_file(const char *file, boolean_t force, boolean_t isspare) char *name; int fd; int ret = 0; - int err; pool_state_t state; boolean_t inuse; - if (dm_inuse_swap(file, &err)) { - if (err) - libdiskmgt_error(err); - else - vdev_error(gettext("%s is currently used by swap. " - "Please see swap(1M).\n"), file); - return (-1); - } - if ((fd = open(file, O_RDONLY)) < 0) return (0); @@ -345,6 +176,173 @@ check_file(const char *file, boolean_t force, boolean_t isspare) return (ret); } +#ifdef HAVE_LIBBLKID +static void +check_error(int err) +{ + (void) fprintf(stderr, gettext("warning: device in use checking " + "failed: %s\n"), strerror(err)); +} + +static int +check_slice(const char *path, blkid_cache cache, int force, boolean_t isspare) +{ + struct stat64 statbuf; + char *value; + int err; + + if (stat64(path, &statbuf) != 0) { + vdev_error(gettext("cannot open %s: %s\n"), + path, strerror(errno)); + return (-1); + } + + /* No valid type detected device is safe to use */ + value = blkid_get_tag_value(cache, "TYPE", path); + if (value == NULL) + return (0); + + /* + * If libblkid detects a ZFS device, we check the device + * using check_file() to see if it's safe. The one safe + * case is a spare device shared between multiple pools. + */ + if (strcmp(value, "zfs") == 0) { + err = check_file(path, force, isspare); + } else { + if (force) { + err = 0; + } else { + err = -1; + vdev_error(gettext("%s contains a filesystem of " + "type '%s'\n"), path, value); + } + } + + free(value); + + return (err); +} + +/* + * Validate a whole disk. Iterate over all slices on the disk and make sure + * that none is in use by calling check_slice(). + */ +static int +check_disk(const char *path, blkid_cache cache, int force, + boolean_t isspare, boolean_t iswholedisk) +{ + struct dk_gpt *vtoc; + char slice_path[MAXPATHLEN]; + 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 */ + if (!iswholedisk) + return 0; + + /* + * When the device is a whole disk try to read the efi partition + * label. If this is successful we safely check the all of the + * 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. + */ + if ((fd = open(path, O_RDONLY|O_NDELAY)) < 0) { + check_error(errno); + return -1; + } + + if ((err = efi_alloc_and_read(fd, &vtoc)) != 0) { + (void) close(fd); + + 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); + return -1; + } + } + + /* + * The primary efi partition label is damaged however the secondary + * 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 (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); + efi_free(vtoc); + (void) close(fd); + return -1; + } + + for (i = 0; i < vtoc->efi_nparts; i++) { + + if (vtoc->efi_parts[i].p_tag == V_UNASSIGNED || + uuid_is_null((uchar_t *)&vtoc->efi_parts[i].p_guid)) + continue; + + sprintf(slice_path, "%s%d", path, i+1); + err = check_slice(slice_path, cache, force, isspare); + if (err) + break; + } + + efi_free(vtoc); + (void) close(fd); + + return (err); +} + +static int +check_device(const char *path, boolean_t force, + boolean_t isspare, boolean_t iswholedisk) +{ + static blkid_cache cache = NULL; + int err; + + /* + * There is no easy way to add a correct blkid_put_cache() call, + * memory will be reclaimed when the command exits. + */ + if (cache == NULL) { + if ((err = blkid_get_cache(&cache, NULL)) != 0) { + check_error(err); + return -1; + } + + if ((err = blkid_probe_all(cache)) != 0) { + blkid_put_cache(cache); + check_error(err); + return -1; + } + } + + return check_disk(path, cache, force, isspare, iswholedisk); +} + +#else /* HAVE_LIBBLKID */ + +static int +check_device(const char *path, boolean_t force, + boolean_t isspare, boolean_t iswholedisk) +{ + return check_file(path, force, isspare); +} +#endif /* HAVE_LIBBLKID */ /* * By "whole disk" we mean an entire physical disk (something we can @@ -874,6 +872,39 @@ check_replication(nvlist_t *config, nvlist_t *newroot) return (ret); } +static int +zero_label(char *path) +{ + const int size = 4096; + char buf[size]; + int err, fd; + + if ((fd = open(path, O_WRONLY)) < 0) { + (void) fprintf(stderr, gettext("cannot open '%s': %s\n"), + path, strerror(errno)); + return (-1); + } + + memset(buf, 0, size); + err = write(fd, buf, size); + (void) fdatasync(fd); + (void) close(fd); + + if (err == -1) { + (void) fprintf(stderr, gettext("cannot zero first %d bytes " + "of '%s': %s\n"), size, path, strerror(errno)); + return (-1); + } + + if (err != size) { + (void) fprintf(stderr, gettext("could only zero %d/%d bytes " + "of '%s'\n"), err, size, path); + return (-1); + } + + return 0; +} + /* * Go through and find any whole disks in the vdev specification, labelling them * as appropriate. When constructing the vdev spec, we were unable to open this @@ -908,14 +939,20 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv) return (0); /* - * We have a disk device. Get the path to the device - * and see if it's a whole disk by appending the backup - * slice and stat()ing the device. + * We have a disk device. If this is a whole disk write + * out the efi partition table, otherwise write zero's to + * the first 4k of the partition. This is to ensure that + * libblkid will not misidentify the partition due to a + * magic value left by the previous filesystem. */ - verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0); - if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, - &wholedisk) != 0 || !wholedisk) - return (0); + verify(!nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path)); + verify(!nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, + &wholedisk)); + + if (!wholedisk) { + ret = zero_label(path); + return (ret); + } diskname = strrchr(path, '/'); assert(diskname != NULL); @@ -960,6 +997,9 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv) (void) close(fd); + /* Just in case this partition already existed. */ + (void) zero_label(buf); + return (0); } @@ -1041,7 +1081,7 @@ check_in_use(nvlist_t *config, nvlist_t *nv, int force, int isreplacing, nvlist_t **child; uint_t c, children; char *type, *path; - int ret; + int ret = 0; char buf[MAXPATHLEN]; uint64_t wholedisk; @@ -1050,16 +1090,17 @@ check_in_use(nvlist_t *config, nvlist_t *nv, int force, int isreplacing, if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child, &children) != 0) { - verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0); + verify(!nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path)); + verify(!nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, + &wholedisk)); /* * As a generic check, we look to see if this is a replace of a * hot spare within the same pool. If so, we allow it - * regardless of what libdiskmgt or zpool_in_use() says. + * regardless of what libblkid or zpool_in_use() says. */ if (isreplacing) { - if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, - &wholedisk) == 0 && wholedisk) + if (wholedisk) (void) snprintf(buf, sizeof (buf), "%ss0", path); else @@ -1069,7 +1110,7 @@ check_in_use(nvlist_t *config, nvlist_t *nv, int force, int isreplacing, } if (strcmp(type, VDEV_TYPE_DISK) == 0) - ret = check_device(path, force, isspare); + ret = check_device(path, force, isspare, wholedisk); if (strcmp(type, VDEV_TYPE_FILE) == 0) ret = check_file(path, force, isspare); diff --git a/cmd/ztest/Makefile.am b/cmd/ztest/Makefile.am index 5ed0890dea..8f5ab24932 100644 --- a/cmd/ztest/Makefile.am +++ b/cmd/ztest/Makefile.am @@ -29,4 +29,4 @@ ztest_LDADD = \ $(top_builddir)/lib/libzpool/libzpool.la \ $(top_builddir)/lib/libzfs/libzfs.la -ztest_LDFLAGS = -pthread -lm $(ZLIB) -lrt -ldl $(LIBUUID) +ztest_LDFLAGS = -pthread -lm $(ZLIB) -lrt -ldl $(LIBUUID) $(LIBBLKID) diff --git a/config/user-libdiskmgt.m4 b/config/user-libdiskmgt.m4 deleted file mode 100644 index 53e043bda4..0000000000 --- a/config/user-libdiskmgt.m4 +++ /dev/null @@ -1,8 +0,0 @@ -dnl # -dnl # Check for libdiskmgt -dnl # -AC_DEFUN([ZFS_AC_CONFIG_USER_LIBDISKMGT], [ - AC_CHECK_LIB([diskmgt], [libdiskmgt_error], - [AC_DEFINE([HAVE_LIBDISKMGT], 1, - [Define to 1 if 'libdiskmgt' library available])]) -]) diff --git a/config/user-zlib.m4 b/config/user-zlib.m4 index 5f2a545823..a48361662e 100644 --- a/config/user-zlib.m4 +++ b/config/user-zlib.m4 @@ -13,6 +13,9 @@ AC_DEFUN([ZFS_AC_CONFIG_USER_ZLIB], [ AC_CHECK_LIB([z], [uncompress], [], [AC_MSG_FAILURE([ *** uncompress() missing, zlib-devel package required])]) + AC_CHECK_LIB([z], [crc32], [], [AC_MSG_FAILURE([ + *** crc32() missing, zlib-devel package required])]) + AC_SUBST([ZLIB], ["-lz"]) AC_DEFINE([HAVE_ZLIB], 1, [Define if you have zlib]) ]) diff --git a/config/user.m4 b/config/user.m4 index f8bda8e0ff..be59e7b4fb 100644 --- a/config/user.m4 +++ b/config/user.m4 @@ -3,7 +3,6 @@ dnl # Default ZFS user configuration dnl # AC_DEFUN([ZFS_AC_CONFIG_USER], [ ZFS_AC_CONFIG_USER_ARCH - ZFS_AC_CONFIG_USER_LIBDISKMGT ZFS_AC_CONFIG_USER_LIBSHARE ZFS_AC_CONFIG_USER_IOCTL ZFS_AC_CONFIG_USER_ZLIB diff --git a/lib/libzfs/libzfs_import.c b/lib/libzfs/libzfs_import.c index 90bd4d80a6..b28f793bb5 100644 --- a/lib/libzfs/libzfs_import.c +++ b/lib/libzfs/libzfs_import.c @@ -52,6 +52,9 @@ #include #include +#ifdef HAVE_LIBBLKID +#include +#endif #include "libzfs.h" #include "libzfs_impl.h" @@ -777,6 +780,77 @@ zpool_read_label(int fd, nvlist_t **config) return (0); } +#ifdef HAVE_LIBBLKID +/* + * Use libblkid to quickly search for zfs devices + */ +static int +zpool_find_import_blkid(libzfs_handle_t *hdl, pool_list_t *pools) +{ + blkid_cache cache; + blkid_dev_iterate iter; + blkid_dev dev; + const char *devname; + nvlist_t *config; + int fd, err; + + err = blkid_get_cache(&cache, NULL); + if (err != 0) { + (void) zfs_error_fmt(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, "blkid_get_cache() %d"), err); + goto err_blkid1; + } + + err = blkid_probe_all(cache); + if (err != 0) { + (void) zfs_error_fmt(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, "blkid_probe_all() %d"), err); + goto err_blkid2; + } + + iter = blkid_dev_iterate_begin(cache); + if (iter == NULL) { + (void) zfs_error_fmt(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, "blkid_dev_iterate_begin()")); + goto err_blkid2; + } + + err = blkid_dev_set_search(iter, "TYPE", "zfs"); + if (err != 0) { + (void) zfs_error_fmt(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, "blkid_dev_set_search() %d"), err); + goto err_blkid3; + } + + while (blkid_dev_next(iter, &dev) == 0) { + devname = blkid_dev_devname(dev); + if ((fd = open64(devname, O_RDONLY)) < 0) + continue; + + err = zpool_read_label(fd, &config); + (void) close(fd); + + if (err != 0) { + (void) no_memory(hdl); + goto err_blkid3; + } + + if (config != NULL) { + err = add_config(hdl, pools, devname, config); + if (err != 0) + goto err_blkid3; + } + } + +err_blkid3: + blkid_dev_iterate_end(iter); +err_blkid2: + blkid_put_cache(cache); +err_blkid1: + return err; +} +#endif /* HAVE_LIBBLKID */ + /* * Given a list of directories to search, find all pools stored on disk. This * includes partial pools which are not available to import. If no args are @@ -807,6 +881,15 @@ zpool_find_import_impl(libzfs_handle_t *hdl, int argc, char **argv, verify(poolname == NULL || guid == 0); if (argc == 0) { +#ifdef HAVE_LIBBLKID + /* Use libblkid to scan all device for their type */ + if (zpool_find_import_blkid(hdl, &pools) == 0) + goto skip_scanning; + + (void) zfs_error_fmt(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, "blkid failure falling back " + "to manual probing")); +#endif /* HAVE_LIBBLKID */ argc = 1; argv = &default_dir; } @@ -912,6 +995,9 @@ zpool_find_import_impl(libzfs_handle_t *hdl, int argc, char **argv, dirp = NULL; } +#ifdef HAVE_LIBBLKID +skip_scanning: +#endif ret = get_configs(hdl, &pools, active_ok); error: diff --git a/zfs.spec.in b/zfs.spec.in index 34c32a47f0..f397333987 100644 --- a/zfs.spec.in +++ b/zfs.spec.in @@ -42,7 +42,7 @@ various system profiling tools to facilitate an in depth analysis. %prep %setup %build -%configure --with-config=user +%configure --with-config=user --with-blkid make %install