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_vdev_config_dev_sysfs_path(nvlist_t *nv, const char *path,
254     const char *key)
255 {
256 	(void) nv;
257 	(void) path;
258 	(void) key;
259 }
260 
261 void
262 update_vdevs_config_dev_sysfs_path(nvlist_t *config)
263 {
264 	(void) config;
265 }
266 
267 int
268 zpool_disk_wait(const char *path)
269 {
270 
271 	(void) path;
272 	return (ENOTSUP);
273 }
274