/* * Clear the errors for the pool, or the particular device if specified. */ int zpool_clear(zpool_handle_t *zhp, const char *path) { zfs_cmd_t zc = { 0 }; char msg[1024]; nvlist_t *tgt; boolean_t avail_spare; libzfs_handle_t *hdl = zhp->zpool_hdl; if (path) (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot clear errors for %s"), zc.zc_prop_value); else (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot clear errors for %s"), zhp->zpool_name); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if (path) { if ((tgt = zpool_find_vdev(zhp, path, &avail_spare)) == 0) return (zfs_error(hdl, EZFS_NODEVICE, msg)); if (avail_spare) return (zfs_error(hdl, EZFS_ISSPARE, msg)); verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); } if (ioctl(hdl->libzfs_fd, ZFS_IOC_CLEAR, &zc) == 0) return (0); return (zpool_standard_error(hdl, errno, msg)); }
/* * Bring the specified vdev online */ int zpool_vdev_online(zpool_handle_t *zhp, const char *path) { zfs_cmd_t zc = { 0 }; char msg[1024]; nvlist_t *tgt; boolean_t avail_spare; libzfs_handle_t *hdl = zhp->zpool_hdl; (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot online %s"), path); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, path, &avail_spare)) == NULL) return (zfs_error(hdl, EZFS_NODEVICE, msg)); verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); if (avail_spare || is_spare(zhp, zc.zc_guid) == B_TRUE) return (zfs_error(hdl, EZFS_ISSPARE, msg)); if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_ONLINE, &zc) == 0) return (0); return (zpool_standard_error(hdl, errno, msg)); }
/* * Remove the given device. Currently, this is supported only for hot spares. */ int zpool_vdev_remove(zpool_handle_t *zhp, const char *path) { zfs_cmd_t zc = { 0 }; char msg[1024]; nvlist_t *tgt; boolean_t avail_spare; libzfs_handle_t *hdl = zhp->zpool_hdl; (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot remove %s"), path); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, path, &avail_spare)) == 0) return (zfs_error(hdl, EZFS_NODEVICE, msg)); if (!avail_spare) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "only hot spares can be removed")); return (zfs_error(hdl, EZFS_NODEVICE, msg)); } verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); if (ioctl(hdl->libzfs_fd, ZFS_IOC_VDEV_REMOVE, &zc) == 0) return (0); return (zpool_standard_error(hdl, errno, msg)); }
/** * Attach a vdev to a given zpool * @return 0 in case of success, the error code overwise */ int libzfs_zpool_vdev_attach(zpool_handle_t *p_zpool, const char *psz_current_dev, nvlist_t *pnv_root, int i_replacing, const char **ppsz_error) { spa_t *p_spa; nvlist_t *pnv_tgt; boolean_t avail_spare, l2cache; uint64_t guid; int i_error; if((pnv_tgt = zpool_find_vdev(p_zpool, psz_current_dev, &avail_spare, &l2cache, NULL)) == 0) { *ppsz_error = "no vdev corresponding to the one given"; return ENOENT; } assert(nvlist_lookup_uint64(pnv_tgt, ZPOOL_CONFIG_GUID, &guid) == 0); // Do not attach hot spares or L2 cache if(avail_spare) { *ppsz_error = "could not attach hot spares"; return EINVAL; } if(l2cache) { *ppsz_error = "could not attach to a device actually used as a cache"; return EINVAL; } if((i_error = spa_open(p_zpool->zpool_name, &p_spa, FTAG))) return i_error; i_error = spa_vdev_attach(p_spa, guid, pnv_root, i_replacing); spa_close(p_spa, FTAG); switch(i_error) { case ENOTSUP: *ppsz_error = "can only attach to mirror and top-level disks"; break; case EINVAL: *ppsz_error = "new device must be a single disk"; break; case EBUSY: *ppsz_error = "the device is busy"; break; case EOVERFLOW: *ppsz_error = "devices is too small"; break; case EDOM: *ppsz_error = "devices have different sector alignment"; break; default: *ppsz_error ="unable to attach the new device"; } return i_error; }
/** * Detach the given vdev from the given pool * @param p_zpool: the zpool handler * @param psz_device: the device name * @param ppsz_error: the error message if any * @return 0 in case of success, the error code overwise */ int libzfs_zpool_vdev_detach(zpool_handle_t *p_zpool, const char *psz_device, const char **ppsz_error) { spa_t *p_spa; nvlist_t *pnv_tgt; boolean_t avail_spare, l2cache; uint64_t guid; int i_error; if((pnv_tgt = zpool_find_vdev(p_zpool, psz_device, &avail_spare, &l2cache, NULL)) == 0) { *ppsz_error = "no vdev corresponding to the one given"; return ENOENT; } // Do not detach hot spares or L2 cache if(avail_spare) { *ppsz_error = "could not detach hot spares"; return EINVAL; } if(l2cache) { *ppsz_error = "could not detach device actually used as a cache"; return EINVAL; } assert(nvlist_lookup_uint64(pnv_tgt, ZPOOL_CONFIG_GUID, &guid) == 0); if((i_error = spa_open(p_zpool->zpool_name, &p_spa, FTAG))) { *ppsz_error = "unable to open the given zpool"; return i_error; } if((i_error = spa_vdev_detach(p_spa, guid, 0, 0))) { switch(i_error) { case ENOTSUP: *ppsz_error = "'detach' is only applicable to mirror and to replace vdevs"; break; case EBUSY: *ppsz_error = "the device is actually in use"; break; default: *ppsz_error = "unable to detach the given vdev"; } } spa_close(p_spa, FTAG); return i_error; }
/* * Detach the specified device. */ int zpool_vdev_detach(zpool_handle_t *zhp, const char *path) { zfs_cmd_t zc = { 0 }; char msg[1024]; nvlist_t *tgt; boolean_t avail_spare; libzfs_handle_t *hdl = zhp->zpool_hdl; (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot detach %s"), path); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, path, &avail_spare)) == 0) return (zfs_error(hdl, EZFS_NODEVICE, msg)); if (avail_spare) return (zfs_error(hdl, EZFS_ISSPARE, msg)); verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); if (ioctl(hdl->libzfs_fd, ZFS_IOC_VDEV_DETACH, &zc) == 0) return (0); switch (errno) { case ENOTSUP: /* * Can't detach from this type of vdev. */ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "only " "applicable to mirror and replacing vdevs")); (void) zfs_error(zhp->zpool_hdl, EZFS_BADTARGET, msg); break; case EBUSY: /* * There are no other replicas of this device. */ (void) zfs_error(hdl, EZFS_NOREPLICAS, msg); break; default: (void) zpool_standard_error(hdl, errno, msg); } return (-1); }
/** * Remove the given vdev from the pool * @param p_zpool: the zpool handler * @param psz_name: the name of the device to remove * @param ppsz_error: the error message if any * @return 0 in case of success, the error code overwise */ int libzfs_zpool_vdev_remove(zpool_handle_t *p_zpool, const char *psz_name, const char **ppsz_error) { nvlist_t *pnv_tgt; spa_t *p_spa; boolean_t avail_spare, l2cache, islog; uint64_t guid; int i_error; if((pnv_tgt = zpool_find_vdev(p_zpool, psz_name, &avail_spare, &l2cache, &islog)) == 0) { *ppsz_error = "no vdev corresponding to the one given"; return ENOENT; } assert(nvlist_lookup_uint64(pnv_tgt, ZPOOL_CONFIG_GUID, &guid) == 0); if((i_error = spa_open(p_zpool->zpool_name, &p_spa, FTAG))) { *ppsz_error = "unable to open the spa"; return i_error; } i_error = spa_vdev_remove(p_spa, guid, B_FALSE); spa_close(p_spa, FTAG); switch(i_error) { case 0: return 0; case ENOTSUP: *ppsz_error = "only spares, slogs, and level 2 ARC devices can be removed"; break; case ENOENT: *ppsz_error = "no vdev corresponding to the one given"; break; } return i_error; }
/* * Take the specified vdev offline */ int zpool_vdev_offline(zpool_handle_t *zhp, const char *path, int istmp) { zfs_cmd_t zc = { 0 }; char msg[1024]; nvlist_t *tgt; boolean_t avail_spare; libzfs_handle_t *hdl = zhp->zpool_hdl; (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot offline %s"), path); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, path, &avail_spare)) == NULL) return (zfs_error(hdl, EZFS_NODEVICE, msg)); verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); if (avail_spare || is_spare(zhp, zc.zc_guid) == B_TRUE) return (zfs_error(hdl, EZFS_ISSPARE, msg)); zc.zc_cookie = istmp; if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_OFFLINE, &zc) == 0) return (0); switch (errno) { case EBUSY: /* * There are no other replicas of this device. */ return (zfs_error(hdl, EZFS_NOREPLICAS, msg)); default: return (zpool_standard_error(hdl, errno, msg)); } }
/* * Attach new_disk (fully described by nvroot) to old_disk. * If 'replacing' is specified, tne new disk will replace the old one. */ int zpool_vdev_attach(zpool_handle_t *zhp, const char *old_disk, const char *new_disk, nvlist_t *nvroot, int replacing) { zfs_cmd_t zc = { 0 }; char msg[1024]; char *packed; int ret; size_t len; nvlist_t *tgt; boolean_t avail_spare; uint64_t val; char *path; nvlist_t **child; uint_t children; nvlist_t *config_root; libzfs_handle_t *hdl = zhp->zpool_hdl; if (replacing) (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot replace %s with %s"), old_disk, new_disk); else (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot attach %s to %s"), new_disk, old_disk); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, old_disk, &avail_spare)) == 0) return (zfs_error(hdl, EZFS_NODEVICE, msg)); if (avail_spare) return (zfs_error(hdl, EZFS_ISSPARE, msg)); verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); zc.zc_cookie = replacing; if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, &child, &children) != 0 || children != 1) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "new device must be a single disk")); return (zfs_error(hdl, EZFS_INVALCONFIG, msg)); } verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL), ZPOOL_CONFIG_VDEV_TREE, &config_root) == 0); /* * If the target is a hot spare that has been swapped in, we can only * replace it with another hot spare. */ if (replacing && nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_IS_SPARE, &val) == 0 && nvlist_lookup_string(child[0], ZPOOL_CONFIG_PATH, &path) == 0 && (zpool_find_vdev(zhp, path, &avail_spare) == NULL || !avail_spare) && is_replacing_spare(config_root, tgt, 1)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "can only be replaced by another hot spare")); return (zfs_error(hdl, EZFS_BADTARGET, msg)); } /* * If we are attempting to replace a spare, it canot be applied to an * already spared device. */ if (replacing && nvlist_lookup_string(child[0], ZPOOL_CONFIG_PATH, &path) == 0 && zpool_find_vdev(zhp, path, &avail_spare) != NULL && avail_spare && is_replacing_spare(config_root, tgt, 0)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "device has already been replaced with a spare")); return (zfs_error(hdl, EZFS_BADTARGET, msg)); } verify(nvlist_size(nvroot, &len, NV_ENCODE_NATIVE) == 0); if ((packed = zfs_alloc(zhp->zpool_hdl, len)) == NULL) return (-1); verify(nvlist_pack(nvroot, &packed, &len, NV_ENCODE_NATIVE, 0) == 0); zc.zc_config_src = (uint64_t)(uintptr_t)packed; zc.zc_config_src_size = len; ret = ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_ATTACH, &zc); free(packed); if (ret == 0) return (0); switch (errno) { case ENOTSUP: /* * Can't attach to or replace this type of vdev. */ if (replacing) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot replace a replacing device")); else zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "can only attach to mirrors and top-level " "disks")); (void) zfs_error(hdl, EZFS_BADTARGET, msg); break; case EINVAL: /* * The new device must be a single disk. */ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "new device must be a single disk")); (void) zfs_error(hdl, EZFS_INVALCONFIG, msg); break; case EBUSY: zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "%s is busy"), new_disk); (void) zfs_error(hdl, EZFS_BADDEV, msg); break; case EOVERFLOW: /* * The new device is too small. */ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "device is too small")); (void) zfs_error(hdl, EZFS_BADDEV, msg); break; case EDOM: /* * The new device has a different alignment requirement. */ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "devices have different sector alignment")); (void) zfs_error(hdl, EZFS_BADDEV, msg); break; case ENAMETOOLONG: /* * The resulting top-level vdev spec won't fit in the label. */ (void) zfs_error(hdl, EZFS_DEVOVERFLOW, msg); break; default: (void) zpool_standard_error(hdl, errno, msg); } return (-1); }