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 http://www.opensolaris.org/os/licensing.
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 <aio.h>
46 #include <ctype.h>
47 #include <dirent.h>
48 #include <errno.h>
49 #include <libintl.h>
50 #include <libgen.h>
51 #include <stddef.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <sys/disk.h>
55 #include <sys/ioctl.h>
56 #include <sys/stat.h>
57 #include <unistd.h>
58 #include <fcntl.h>
59
60 #include <sys/efi_partition.h>
61 #include <thread_pool.h>
62 #include <libgeom.h>
63
64 #include <sys/vdev_impl.h>
65
66 #include <libzutil.h>
67
68 #include "zutil_import.h"
69
70 /*
71 * Update a leaf vdev's persistent device strings
72 *
73 * - only applies for a dedicated leaf vdev (aka whole disk)
74 * - updated during pool create|add|attach|import
75 * - used for matching device matching during auto-{online,expand,replace}
76 * - stored in a leaf disk config label (i.e. alongside 'path' NVP)
77 * - these strings are currently not used in kernel (i.e. for vdev_disk_open)
78 *
79 * On FreeBSD we currently just strip devid and phys_path to avoid confusion.
80 */
81 void
update_vdev_config_dev_strs(nvlist_t * nv)82 update_vdev_config_dev_strs(nvlist_t *nv)
83 {
84 (void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID);
85 (void) nvlist_remove_all(nv, ZPOOL_CONFIG_PHYS_PATH);
86 }
87
88 /*
89 * Do not even look at these devices.
90 */
91 static const char * const excluded_devs[] = {
92 "nfslock",
93 "sequencer",
94 "zfs",
95 };
96 #define EXCLUDED_DIR "/dev/"
97 #define EXCLUDED_DIR_LEN 5
98
99 void
zpool_open_func(void * arg)100 zpool_open_func(void *arg)
101 {
102 rdsk_node_t *rn = arg;
103 struct stat64 statbuf;
104 nvlist_t *config;
105 size_t i;
106 int num_labels;
107 int fd;
108 off_t mediasize = 0;
109
110 /*
111 * Do not even look at excluded devices.
112 */
113 if (strncmp(rn->rn_name, EXCLUDED_DIR, EXCLUDED_DIR_LEN) == 0) {
114 char *name = rn->rn_name + EXCLUDED_DIR_LEN;
115 for (i = 0; i < nitems(excluded_devs); ++i) {
116 const char *excluded_name = excluded_devs[i];
117 size_t len = strlen(excluded_name);
118 if (strncmp(name, excluded_name, len) == 0) {
119 return;
120 }
121 }
122 }
123
124 /*
125 * O_NONBLOCK so we don't hang trying to open things like serial ports.
126 */
127 if ((fd = open(rn->rn_name, O_RDONLY|O_NONBLOCK)) < 0)
128 return;
129
130 /*
131 * Ignore failed stats.
132 */
133 if (fstat64(fd, &statbuf) != 0)
134 goto out;
135 /*
136 * We only want regular files, character devs and block devs.
137 */
138 if (S_ISREG(statbuf.st_mode)) {
139 /* Check if this file is too small to hold a zpool. */
140 if (statbuf.st_size < SPA_MINDEVSIZE) {
141 goto out;
142 }
143 } else if (S_ISCHR(statbuf.st_mode) || S_ISBLK(statbuf.st_mode)) {
144 /* Check if this device is too small to hold a zpool. */
145 if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) != 0 ||
146 mediasize < SPA_MINDEVSIZE) {
147 goto out;
148 }
149 } else {
150 goto out;
151 }
152
153 if (zpool_read_label(fd, &config, &num_labels) != 0)
154 goto out;
155 if (num_labels == 0) {
156 nvlist_free(config);
157 goto out;
158 }
159
160 rn->rn_config = config;
161 rn->rn_num_labels = num_labels;
162
163 /* TODO: Reuse labelpaths logic from Linux? */
164 out:
165 (void) close(fd);
166 }
167
168 static const char *
169 zpool_default_import_path[] = {
170 "/dev"
171 };
172
173 const char * const *
zpool_default_search_paths(size_t * count)174 zpool_default_search_paths(size_t *count)
175 {
176 *count = nitems(zpool_default_import_path);
177 return (zpool_default_import_path);
178 }
179
180 int
zpool_find_import_blkid(libpc_handle_t * hdl,pthread_mutex_t * lock,avl_tree_t ** slice_cache)181 zpool_find_import_blkid(libpc_handle_t *hdl, pthread_mutex_t *lock,
182 avl_tree_t **slice_cache)
183 {
184 char *end, path[MAXPATHLEN];
185 rdsk_node_t *slice;
186 struct gmesh mesh;
187 struct gclass *mp;
188 struct ggeom *gp;
189 struct gprovider *pp;
190 avl_index_t where;
191 size_t pathleft;
192 int error;
193
194 end = stpcpy(path, "/dev/");
195 pathleft = &path[sizeof (path)] - end;
196
197 error = geom_gettree(&mesh);
198 if (error != 0)
199 return (error);
200
201 *slice_cache = zutil_alloc(hdl, sizeof (avl_tree_t));
202 avl_create(*slice_cache, slice_cache_compare, sizeof (rdsk_node_t),
203 offsetof(rdsk_node_t, rn_node));
204
205 LIST_FOREACH(mp, &mesh.lg_class, lg_class) {
206 LIST_FOREACH(gp, &mp->lg_geom, lg_geom) {
207 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
208 strlcpy(end, pp->lg_name, pathleft);
209 slice = zutil_alloc(hdl, sizeof (rdsk_node_t));
210 slice->rn_name = zutil_strdup(hdl, path);
211 slice->rn_vdev_guid = 0;
212 slice->rn_lock = lock;
213 slice->rn_avl = *slice_cache;
214 slice->rn_hdl = hdl;
215 slice->rn_labelpaths = B_FALSE;
216 slice->rn_order = IMPORT_ORDER_DEFAULT;
217
218 pthread_mutex_lock(lock);
219 if (avl_find(*slice_cache, slice, &where)) {
220 free(slice->rn_name);
221 free(slice);
222 } else {
223 avl_insert(*slice_cache, slice, where);
224 }
225 pthread_mutex_unlock(lock);
226 }
227 }
228 }
229
230 geom_deletetree(&mesh);
231
232 return (0);
233 }
234
235 int
zfs_dev_flush(int fd __unused)236 zfs_dev_flush(int fd __unused)
237 {
238 return (0);
239 }
240