+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;
+ int err;
+#ifdef HAVE_LIBBLKID
+ char *value;
+#endif /* HAVE_LIBBLKID */
+
+ if (stat64(path, &statbuf) != 0) {
+ vdev_error(gettext("cannot stat %s: %s\n"),
+ path, strerror(errno));
+ return (-1);
+ }
+
+#ifdef HAVE_LIBBLKID
+ /* 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);
+#else
+ err = check_file(path, force, isspare);
+#endif /* HAVE_LIBBLKID */
+
+ 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;
+
+ /* This is not a wholedisk we only check the given partition */
+ if (!iswholedisk)
+ return check_slice(path, cache, force, isspare);
+
+ /*
+ * 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 option since we cannot check the partitions.
+ */
+ if ((fd = open(path, O_RDWR|O_DIRECT|O_EXCL)) < 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 does not contain an EFI "
+ "label but it may contain partition\n"
+ "information in the MBR.\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) {
+ efi_free(vtoc);
+ (void) close(fd);
+
+ 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++) {
+
+ if (vtoc->efi_parts[i].p_tag == V_UNASSIGNED ||
+ uuid_is_null((uchar_t *)&vtoc->efi_parts[i].p_guid))
+ continue;
+
+ 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%s%d", path, isdigit(path[strlen(path)-1]) ?
+ "p" : "", 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;
+
+#ifdef HAVE_LIBBLKID
+ /*
+ * There is no easy way to add a correct blkid_put_cache() call,
+ * memory will be reclaimed when the command exits.
+ */
+ if (cache == NULL) {
+ int err;
+
+ 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;
+ }
+ }
+#endif /* HAVE_LIBBLKID */
+
+ return check_disk(path, cache, force, isspare, iswholedisk);
+}