1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3 * This file and its contents are supplied under the terms of the
4 * Common Development and Distribution License ("CDDL"), version 1.0.
5 * You may only use this file in accordance with the terms of version
6 * 1.0 of the CDDL.
7 *
8 * A full copy of the text of the CDDL should have accompanied this
9 * source. A copy of the CDDL is also available via the Internet at
10 * https://opensource.org/license/CDDL-1.0.
11 */
12
13 /*
14 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
15 * Copyright (c) 2012, 2017 by Delphix. All rights reserved.
16 * Copyright 2015 RackTop Systems.
17 * Copyright 2016 Nexenta Systems, Inc.
18 */
19
20 /*
21 * Pool import support functions.
22 *
23 * To import a pool, we rely on reading the configuration information from the
24 * ZFS label of each device. If we successfully read the label, then we
25 * organize the configuration information in the following hierarchy:
26 *
27 * pool guid -> toplevel vdev guid -> label txg
28 *
29 * Duplicate entries matching this same tuple will be discarded. Once we have
30 * examined every device, we pick the best label txg config for each toplevel
31 * vdev. We then arrange these toplevel vdevs into a complete pool config, and
32 * update any paths that have changed. Finally, we attempt to import the pool
33 * using our derived config, and record the results.
34 */
35
36 #include <sys/types.h>
37 #include <sys/disk.h>
38 #include <sys/ioctl.h>
39 #include <sys/stat.h>
40 #include <sys/sysctl.h>
41
42 #include <aio.h>
43 #include <ctype.h>
44 #include <dirent.h>
45 #include <errno.h>
46 #include <libintl.h>
47 #include <libgen.h>
48 #include <stddef.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include <fcntl.h>
53
54 #include <sys/efi_partition.h>
55 #include <libgeom.h>
56
57 #include <sys/vdev_impl.h>
58
59 #include <libzutil.h>
60
61 #include "zutil_import.h"
62
63 /*
64 * Update a leaf vdev's persistent device strings
65 *
66 * - only applies for a dedicated leaf vdev (aka whole disk)
67 * - updated during pool create|add|attach|import
68 * - used for matching device matching during auto-{online,expand,replace}
69 * - stored in a leaf disk config label (i.e. alongside 'path' NVP)
70 * - these strings are currently not used in kernel (i.e. for vdev_disk_open)
71 *
72 * On FreeBSD we currently just strip devid and phys_path to avoid confusion.
73 */
74 void
update_vdev_config_dev_strs(nvlist_t * nv)75 update_vdev_config_dev_strs(nvlist_t *nv)
76 {
77 (void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID);
78 (void) nvlist_remove_all(nv, ZPOOL_CONFIG_PHYS_PATH);
79 }
80
81 /*
82 * Do not even look at these devices.
83 */
84 static const char * const excluded_devs[] = {
85 "nfslock",
86 "sequencer",
87 "zfs",
88 };
89 #define EXCLUDED_DIR "/dev/"
90 #define EXCLUDED_DIR_LEN 5
91
92 static boolean_t
excluded_dev(const char * name)93 excluded_dev(const char *name)
94 {
95 size_t i;
96
97 if (strncmp(name, EXCLUDED_DIR, EXCLUDED_DIR_LEN) != 0)
98 return (B_FALSE);
99
100 name += EXCLUDED_DIR_LEN;
101 for (i = 0; i < nitems(excluded_devs); ++i) {
102 const char *excluded_name = excluded_devs[i];
103 size_t len = strlen(excluded_name);
104 if (strncmp(name, excluded_name, len) == 0)
105 return (B_TRUE);
106 }
107
108 return (B_FALSE);
109 }
110
111 /*
112 * Common predicate for zpool_dev_probe_ok() and its fd variant: only a
113 * disk device (character or block), or a regular file large enough to
114 * hold a label, may be probed. Anything else is refused.
115 */
116 static boolean_t
dev_stat_probe_ok(const struct stat64 * statbuf)117 dev_stat_probe_ok(const struct stat64 *statbuf)
118 {
119 if (S_ISREG(statbuf->st_mode))
120 return (statbuf->st_size >= SPA_MINDEVSIZE);
121
122 return (S_ISCHR(statbuf->st_mode) || S_ISBLK(statbuf->st_mode));
123 }
124
125 /*
126 * Determine if a path may be safely opened to probe for a vdev label.
127 * Only regular files large enough to hold a label and disk devices
128 * (character or block) are acceptable. Anything else is refused,
129 * opening other nodes can have side effects. stat64() never blocks,
130 * even on a FIFO.
131 */
132 boolean_t
zpool_dev_probe_ok(const char * path)133 zpool_dev_probe_ok(const char *path)
134 {
135 struct stat64 statbuf;
136
137 if (excluded_dev(path))
138 return (B_FALSE);
139
140 if (stat64(path, &statbuf) != 0)
141 return (B_FALSE);
142
143 return (dev_stat_probe_ok(&statbuf));
144 }
145
146 /*
147 * As zpool_dev_probe_ok(), but re-check the type of an object already
148 * opened. A path naming a symlink may have been repointed at a different
149 * node between the stat64() above and the open(), so only trust a
150 * descriptor which is still a disk device or a large enough regular file.
151 */
152 boolean_t
zpool_dev_probe_ok_fd(int fd)153 zpool_dev_probe_ok_fd(int fd)
154 {
155 struct stat64 statbuf;
156
157 if (fstat64(fd, &statbuf) != 0)
158 return (B_FALSE);
159
160 return (dev_stat_probe_ok(&statbuf));
161 }
162
163 void
zpool_open_func(void * arg)164 zpool_open_func(void *arg)
165 {
166 rdsk_node_t *rn = arg;
167 struct stat64 statbuf;
168 nvlist_t *config;
169 int num_labels;
170 int fd;
171 off_t mediasize = 0;
172
173 /*
174 * Do not even look at excluded devices.
175 */
176 if (excluded_dev(rn->rn_name))
177 return;
178
179 /*
180 * O_NONBLOCK so we don't hang trying to open things like serial ports.
181 */
182 if ((fd = open(rn->rn_name, O_RDONLY|O_NONBLOCK|O_CLOEXEC)) < 0)
183 return;
184
185 /*
186 * Ignore failed stats.
187 */
188 if (fstat64(fd, &statbuf) != 0)
189 goto out;
190 /*
191 * We only want regular files, character devs and block devs.
192 */
193 if (S_ISREG(statbuf.st_mode)) {
194 /* Check if this file is too small to hold a zpool. */
195 if (statbuf.st_size < SPA_MINDEVSIZE) {
196 goto out;
197 }
198 } else if (S_ISCHR(statbuf.st_mode) || S_ISBLK(statbuf.st_mode)) {
199 /* Check if this device is too small to hold a zpool. */
200 if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) != 0 ||
201 mediasize < SPA_MINDEVSIZE) {
202 goto out;
203 }
204 } else {
205 goto out;
206 }
207
208 if (zpool_read_label(fd, &config, &num_labels) != 0)
209 goto out;
210 if (num_labels == 0) {
211 nvlist_free(config);
212 goto out;
213 }
214
215 rn->rn_config = config;
216 rn->rn_num_labels = num_labels;
217
218 /* TODO: Reuse labelpaths logic from Linux? */
219 out:
220 (void) close(fd);
221 }
222
223 static const char * const
224 zpool_default_import_path[] = {
225 "/dev"
226 };
227
228 const char * const *
zpool_default_search_paths(size_t * count)229 zpool_default_search_paths(size_t *count)
230 {
231 *count = nitems(zpool_default_import_path);
232 return (zpool_default_import_path);
233 }
234
235 int
zpool_find_import_blkid(libpc_handle_t * hdl,pthread_mutex_t * lock,avl_tree_t ** slice_cache)236 zpool_find_import_blkid(libpc_handle_t *hdl, pthread_mutex_t *lock,
237 avl_tree_t **slice_cache)
238 {
239 const char *oid = "vfs.zfs.vol.recursive";
240 char *end, path[MAXPATHLEN];
241 rdsk_node_t *slice;
242 struct gmesh mesh;
243 struct gclass *mp;
244 struct ggeom *gp;
245 struct gprovider *pp;
246 avl_index_t where;
247 int error, value;
248 size_t pathleft, size = sizeof (value);
249 boolean_t skip_zvols = B_FALSE;
250
251 end = stpcpy(path, "/dev/");
252 pathleft = &path[sizeof (path)] - end;
253
254 error = geom_gettree(&mesh);
255 if (error != 0)
256 return (error);
257
258 if (sysctlbyname(oid, &value, &size, NULL, 0) == 0 && value == 0)
259 skip_zvols = B_TRUE;
260
261 *slice_cache = zutil_alloc(hdl, sizeof (avl_tree_t));
262 avl_create(*slice_cache, slice_cache_compare, sizeof (rdsk_node_t),
263 offsetof(rdsk_node_t, rn_node));
264
265 LIST_FOREACH(mp, &mesh.lg_class, lg_class) {
266 if (skip_zvols && strcmp(mp->lg_name, "ZFS::ZVOL") == 0)
267 continue;
268 LIST_FOREACH(gp, &mp->lg_geom, lg_geom) {
269 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
270 strlcpy(end, pp->lg_name, pathleft);
271 slice = zutil_alloc(hdl, sizeof (rdsk_node_t));
272 slice->rn_name = zutil_strdup(hdl, path);
273 slice->rn_vdev_guid = 0;
274 slice->rn_lock = lock;
275 slice->rn_avl = *slice_cache;
276 slice->rn_hdl = hdl;
277 slice->rn_labelpaths = B_FALSE;
278 slice->rn_order = IMPORT_ORDER_DEFAULT;
279
280 pthread_mutex_lock(lock);
281 if (avl_find(*slice_cache, slice, &where)) {
282 free(slice->rn_name);
283 free(slice);
284 } else {
285 avl_insert(*slice_cache, slice, where);
286 }
287 pthread_mutex_unlock(lock);
288 }
289 }
290 }
291
292 geom_deletetree(&mesh);
293
294 return (0);
295 }
296
297 int
zfs_dev_flush(int fd)298 zfs_dev_flush(int fd)
299 {
300 (void) fd;
301 return (0);
302 }
303
304 void
update_vdev_config_dev_sysfs_path(nvlist_t * nv,const char * path,const char * key)305 update_vdev_config_dev_sysfs_path(nvlist_t *nv, const char *path,
306 const char *key)
307 {
308 (void) nv;
309 (void) path;
310 (void) key;
311 }
312
313 void
update_vdevs_config_dev_sysfs_path(nvlist_t * config)314 update_vdevs_config_dev_sysfs_path(nvlist_t *config)
315 {
316 (void) config;
317 }
318
319 int
zpool_disk_wait(const char * path)320 zpool_disk_wait(const char *path)
321 {
322
323 (void) path;
324 return (ENOTSUP);
325 }
326