xref: /freebsd/sys/contrib/openzfs/lib/libzutil/os/freebsd/zutil_import_os.c (revision 22649d4dba730d46244fd2dff4fd174903c8379f)
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