| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| |
| /* |
| * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 2012, 2017 by Delphix. All rights reserved. |
| * Copyright 2015 RackTop Systems. |
| * Copyright 2016 Nexenta Systems, Inc. |
| */ |
| |
| /* |
| * Pool import support functions. |
| * |
| * To import a pool, we rely on reading the configuration information from the |
| * ZFS label of each device. If we successfully read the label, then we |
| * organize the configuration information in the following hierarchy: |
| * |
| * pool guid -> toplevel vdev guid -> label txg |
| * |
| * Duplicate entries matching this same tuple will be discarded. Once we have |
| * examined every device, we pick the best label txg config for each toplevel |
| * vdev. We then arrange these toplevel vdevs into a complete pool config, and |
| * update any paths that have changed. Finally, we attempt to import the pool |
| * using our derived config, and record the results. |
| */ |
| |
| #include <sys/types.h> |
| #include <sys/disk.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <sys/sysctl.h> |
| |
| #include <aio.h> |
| #include <ctype.h> |
| #include <dirent.h> |
| #include <errno.h> |
| #include <libintl.h> |
| #include <libgen.h> |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| |
| #include <sys/efi_partition.h> |
| #include <thread_pool.h> |
| #include <libgeom.h> |
| |
| #include <sys/vdev_impl.h> |
| |
| #include <libzutil.h> |
| |
| #include "zutil_import.h" |
| |
| /* |
| * Update a leaf vdev's persistent device strings |
| * |
| * - only applies for a dedicated leaf vdev (aka whole disk) |
| * - updated during pool create|add|attach|import |
| * - used for matching device matching during auto-{online,expand,replace} |
| * - stored in a leaf disk config label (i.e. alongside 'path' NVP) |
| * - these strings are currently not used in kernel (i.e. for vdev_disk_open) |
| * |
| * On FreeBSD we currently just strip devid and phys_path to avoid confusion. |
| */ |
| void |
| update_vdev_config_dev_strs(nvlist_t *nv) |
| { |
| (void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID); |
| (void) nvlist_remove_all(nv, ZPOOL_CONFIG_PHYS_PATH); |
| } |
| |
| /* |
| * Do not even look at these devices. |
| */ |
| static const char * const excluded_devs[] = { |
| "nfslock", |
| "sequencer", |
| "zfs", |
| }; |
| #define EXCLUDED_DIR "/dev/" |
| #define EXCLUDED_DIR_LEN 5 |
| |
| void |
| zpool_open_func(void *arg) |
| { |
| rdsk_node_t *rn = arg; |
| struct stat64 statbuf; |
| nvlist_t *config; |
| size_t i; |
| int num_labels; |
| int fd; |
| off_t mediasize = 0; |
| |
| /* |
| * Do not even look at excluded devices. |
| */ |
| if (strncmp(rn->rn_name, EXCLUDED_DIR, EXCLUDED_DIR_LEN) == 0) { |
| char *name = rn->rn_name + EXCLUDED_DIR_LEN; |
| for (i = 0; i < nitems(excluded_devs); ++i) { |
| const char *excluded_name = excluded_devs[i]; |
| size_t len = strlen(excluded_name); |
| if (strncmp(name, excluded_name, len) == 0) { |
| return; |
| } |
| } |
| } |
| |
| /* |
| * O_NONBLOCK so we don't hang trying to open things like serial ports. |
| */ |
| if ((fd = open(rn->rn_name, O_RDONLY|O_NONBLOCK|O_CLOEXEC)) < 0) |
| return; |
| |
| /* |
| * Ignore failed stats. |
| */ |
| if (fstat64(fd, &statbuf) != 0) |
| goto out; |
| /* |
| * We only want regular files, character devs and block devs. |
| */ |
| if (S_ISREG(statbuf.st_mode)) { |
| /* Check if this file is too small to hold a zpool. */ |
| if (statbuf.st_size < SPA_MINDEVSIZE) { |
| goto out; |
| } |
| } else if (S_ISCHR(statbuf.st_mode) || S_ISBLK(statbuf.st_mode)) { |
| /* Check if this device is too small to hold a zpool. */ |
| if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) != 0 || |
| mediasize < SPA_MINDEVSIZE) { |
| goto out; |
| } |
| } else { |
| goto out; |
| } |
| |
| if (zpool_read_label(fd, &config, &num_labels) != 0) |
| goto out; |
| if (num_labels == 0) { |
| nvlist_free(config); |
| goto out; |
| } |
| |
| rn->rn_config = config; |
| rn->rn_num_labels = num_labels; |
| |
| /* TODO: Reuse labelpaths logic from Linux? */ |
| out: |
| (void) close(fd); |
| } |
| |
| static const char * |
| zpool_default_import_path[] = { |
| "/dev" |
| }; |
| |
| const char * const * |
| zpool_default_search_paths(size_t *count) |
| { |
| *count = nitems(zpool_default_import_path); |
| return (zpool_default_import_path); |
| } |
| |
| int |
| zpool_find_import_blkid(libpc_handle_t *hdl, pthread_mutex_t *lock, |
| avl_tree_t **slice_cache) |
| { |
| const char *oid = "vfs.zfs.vol.recursive"; |
| char *end, path[MAXPATHLEN]; |
| rdsk_node_t *slice; |
| struct gmesh mesh; |
| struct gclass *mp; |
| struct ggeom *gp; |
| struct gprovider *pp; |
| avl_index_t where; |
| int error, value; |
| size_t pathleft, size = sizeof (value); |
| boolean_t skip_zvols = B_FALSE; |
| |
| end = stpcpy(path, "/dev/"); |
| pathleft = &path[sizeof (path)] - end; |
| |
| error = geom_gettree(&mesh); |
| if (error != 0) |
| return (error); |
| |
| if (sysctlbyname(oid, &value, &size, NULL, 0) == 0 && value == 0) |
| skip_zvols = B_TRUE; |
| |
| *slice_cache = zutil_alloc(hdl, sizeof (avl_tree_t)); |
| avl_create(*slice_cache, slice_cache_compare, sizeof (rdsk_node_t), |
| offsetof(rdsk_node_t, rn_node)); |
| |
| LIST_FOREACH(mp, &mesh.lg_class, lg_class) { |
| if (skip_zvols && strcmp(mp->lg_name, "ZFS::ZVOL") == 0) |
| continue; |
| LIST_FOREACH(gp, &mp->lg_geom, lg_geom) { |
| LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { |
| strlcpy(end, pp->lg_name, pathleft); |
| slice = zutil_alloc(hdl, sizeof (rdsk_node_t)); |
| slice->rn_name = zutil_strdup(hdl, path); |
| slice->rn_vdev_guid = 0; |
| slice->rn_lock = lock; |
| slice->rn_avl = *slice_cache; |
| slice->rn_hdl = hdl; |
| slice->rn_labelpaths = B_FALSE; |
| slice->rn_order = IMPORT_ORDER_DEFAULT; |
| |
| pthread_mutex_lock(lock); |
| if (avl_find(*slice_cache, slice, &where)) { |
| free(slice->rn_name); |
| free(slice); |
| } else { |
| avl_insert(*slice_cache, slice, where); |
| } |
| pthread_mutex_unlock(lock); |
| } |
| } |
| } |
| |
| geom_deletetree(&mesh); |
| |
| return (0); |
| } |
| |
| int |
| zfs_dev_flush(int fd __unused) |
| { |
| return (0); |
| } |
| |
| void |
| update_vdevs_config_dev_sysfs_path(nvlist_t *config) |
| { |
| } |