1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or https://opensource.org/licenses/CDDL-1.0. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright (c) 2012, 2017 by Delphix. All rights reserved. 25 * Copyright 2015 RackTop Systems. 26 * Copyright 2016 Nexenta Systems, Inc. 27 */ 28 29 /* 30 * Pool import support functions. 31 * 32 * To import a pool, we rely on reading the configuration information from the 33 * ZFS label of each device. If we successfully read the label, then we 34 * organize the configuration information in the following hierarchy: 35 * 36 * pool guid -> toplevel vdev guid -> label txg 37 * 38 * Duplicate entries matching this same tuple will be discarded. Once we have 39 * examined every device, we pick the best label txg config for each toplevel 40 * vdev. We then arrange these toplevel vdevs into a complete pool config, and 41 * update any paths that have changed. Finally, we attempt to import the pool 42 * using our derived config, and record the results. 43 */ 44 45 #include <sys/types.h> 46 #include <sys/disk.h> 47 #include <sys/ioctl.h> 48 #include <sys/stat.h> 49 #include <sys/sysctl.h> 50 51 #include <aio.h> 52 #include <ctype.h> 53 #include <dirent.h> 54 #include <errno.h> 55 #include <libintl.h> 56 #include <libgen.h> 57 #include <stddef.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <unistd.h> 61 #include <fcntl.h> 62 63 #include <sys/efi_partition.h> 64 #include <thread_pool.h> 65 #include <libgeom.h> 66 67 #include <sys/vdev_impl.h> 68 69 #include <libzutil.h> 70 71 #include "zutil_import.h" 72 73 /* 74 * Update a leaf vdev's persistent device strings 75 * 76 * - only applies for a dedicated leaf vdev (aka whole disk) 77 * - updated during pool create|add|attach|import 78 * - used for matching device matching during auto-{online,expand,replace} 79 * - stored in a leaf disk config label (i.e. alongside 'path' NVP) 80 * - these strings are currently not used in kernel (i.e. for vdev_disk_open) 81 * 82 * On FreeBSD we currently just strip devid and phys_path to avoid confusion. 83 */ 84 void 85 update_vdev_config_dev_strs(nvlist_t *nv) 86 { 87 (void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID); 88 (void) nvlist_remove_all(nv, ZPOOL_CONFIG_PHYS_PATH); 89 } 90 91 /* 92 * Do not even look at these devices. 93 */ 94 static const char * const excluded_devs[] = { 95 "nfslock", 96 "sequencer", 97 "zfs", 98 }; 99 #define EXCLUDED_DIR "/dev/" 100 #define EXCLUDED_DIR_LEN 5 101 102 void 103 zpool_open_func(void *arg) 104 { 105 rdsk_node_t *rn = arg; 106 struct stat64 statbuf; 107 nvlist_t *config; 108 size_t i; 109 int num_labels; 110 int fd; 111 off_t mediasize = 0; 112 113 /* 114 * Do not even look at excluded devices. 115 */ 116 if (strncmp(rn->rn_name, EXCLUDED_DIR, EXCLUDED_DIR_LEN) == 0) { 117 char *name = rn->rn_name + EXCLUDED_DIR_LEN; 118 for (i = 0; i < nitems(excluded_devs); ++i) { 119 const char *excluded_name = excluded_devs[i]; 120 size_t len = strlen(excluded_name); 121 if (strncmp(name, excluded_name, len) == 0) { 122 return; 123 } 124 } 125 } 126 127 /* 128 * O_NONBLOCK so we don't hang trying to open things like serial ports. 129 */ 130 if ((fd = open(rn->rn_name, O_RDONLY|O_NONBLOCK|O_CLOEXEC)) < 0) 131 return; 132 133 /* 134 * Ignore failed stats. 135 */ 136 if (fstat64(fd, &statbuf) != 0) 137 goto out; 138 /* 139 * We only want regular files, character devs and block devs. 140 */ 141 if (S_ISREG(statbuf.st_mode)) { 142 /* Check if this file is too small to hold a zpool. */ 143 if (statbuf.st_size < SPA_MINDEVSIZE) { 144 goto out; 145 } 146 } else if (S_ISCHR(statbuf.st_mode) || S_ISBLK(statbuf.st_mode)) { 147 /* Check if this device is too small to hold a zpool. */ 148 if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) != 0 || 149 mediasize < SPA_MINDEVSIZE) { 150 goto out; 151 } 152 } else { 153 goto out; 154 } 155 156 if (zpool_read_label(fd, &config, &num_labels) != 0) 157 goto out; 158 if (num_labels == 0) { 159 nvlist_free(config); 160 goto out; 161 } 162 163 rn->rn_config = config; 164 rn->rn_num_labels = num_labels; 165 166 /* TODO: Reuse labelpaths logic from Linux? */ 167 out: 168 (void) close(fd); 169 } 170 171 static const char * const 172 zpool_default_import_path[] = { 173 "/dev" 174 }; 175 176 const char * const * 177 zpool_default_search_paths(size_t *count) 178 { 179 *count = nitems(zpool_default_import_path); 180 return (zpool_default_import_path); 181 } 182 183 int 184 zpool_find_import_blkid(libpc_handle_t *hdl, pthread_mutex_t *lock, 185 avl_tree_t **slice_cache) 186 { 187 const char *oid = "vfs.zfs.vol.recursive"; 188 char *end, path[MAXPATHLEN]; 189 rdsk_node_t *slice; 190 struct gmesh mesh; 191 struct gclass *mp; 192 struct ggeom *gp; 193 struct gprovider *pp; 194 avl_index_t where; 195 int error, value; 196 size_t pathleft, size = sizeof (value); 197 boolean_t skip_zvols = B_FALSE; 198 199 end = stpcpy(path, "/dev/"); 200 pathleft = &path[sizeof (path)] - end; 201 202 error = geom_gettree(&mesh); 203 if (error != 0) 204 return (error); 205 206 if (sysctlbyname(oid, &value, &size, NULL, 0) == 0 && value == 0) 207 skip_zvols = B_TRUE; 208 209 *slice_cache = zutil_alloc(hdl, sizeof (avl_tree_t)); 210 avl_create(*slice_cache, slice_cache_compare, sizeof (rdsk_node_t), 211 offsetof(rdsk_node_t, rn_node)); 212 213 LIST_FOREACH(mp, &mesh.lg_class, lg_class) { 214 if (skip_zvols && strcmp(mp->lg_name, "ZFS::ZVOL") == 0) 215 continue; 216 LIST_FOREACH(gp, &mp->lg_geom, lg_geom) { 217 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 218 strlcpy(end, pp->lg_name, pathleft); 219 slice = zutil_alloc(hdl, sizeof (rdsk_node_t)); 220 slice->rn_name = zutil_strdup(hdl, path); 221 slice->rn_vdev_guid = 0; 222 slice->rn_lock = lock; 223 slice->rn_avl = *slice_cache; 224 slice->rn_hdl = hdl; 225 slice->rn_labelpaths = B_FALSE; 226 slice->rn_order = IMPORT_ORDER_DEFAULT; 227 228 pthread_mutex_lock(lock); 229 if (avl_find(*slice_cache, slice, &where)) { 230 free(slice->rn_name); 231 free(slice); 232 } else { 233 avl_insert(*slice_cache, slice, where); 234 } 235 pthread_mutex_unlock(lock); 236 } 237 } 238 } 239 240 geom_deletetree(&mesh); 241 242 return (0); 243 } 244 245 int 246 zfs_dev_flush(int fd) 247 { 248 (void) fd; 249 return (0); 250 } 251 252 void 253 update_vdevs_config_dev_sysfs_path(nvlist_t *config) 254 { 255 (void) config; 256 } 257