xref: /freebsd/sys/contrib/openzfs/lib/libzfs/os/freebsd/libzfs_compat.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) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
15  */
16 #include "../../libzfs_impl.h"
17 #include <libzfs.h>
18 #include <libzutil.h>
19 #include <sys/sysctl.h>
20 #include <libintl.h>
21 #include <sys/linker.h>
22 #include <sys/module.h>
23 #include <sys/stat.h>
24 #include <sys/param.h>
25 
26 #ifdef IN_BASE
27 #define	ZFS_KMOD	"zfs"
28 #else
29 #define	ZFS_KMOD	"openzfs"
30 #endif
31 
32 #ifndef HAVE_EXECVPE
33 /* FreeBSD prior to 15 lacks execvpe */
34 static int
execvPe(const char * name,const char * path,char * const * argv,char * const * envp)35 execvPe(const char *name, const char *path, char * const *argv,
36     char * const *envp)
37 {
38 	const char **memp;
39 	size_t cnt, lp, ln;
40 	int eacces, save_errno;
41 	char buf[MAXPATHLEN];
42 	const char *bp, *np, *op, *p;
43 	struct stat sb;
44 
45 	eacces = 0;
46 
47 	/* If it's an absolute or relative path name, it's easy. */
48 	if (strchr(name, '/')) {
49 		bp = name;
50 		op = NULL;
51 		goto retry;
52 	}
53 	bp = buf;
54 
55 	/* If it's an empty path name, fail in the usual POSIX way. */
56 	if (*name == '\0') {
57 		errno = ENOENT;
58 		return (-1);
59 	}
60 
61 	op = path;
62 	ln = strlen(name);
63 	while (op != NULL) {
64 		np = strchrnul(op, ':');
65 
66 		/*
67 		 * It's a SHELL path -- double, leading and trailing colons
68 		 * mean the current directory.
69 		 */
70 		if (np == op) {
71 			/* Empty component. */
72 			p = ".";
73 			lp = 1;
74 		} else {
75 			/* Non-empty component. */
76 			p = op;
77 			lp = np - op;
78 		}
79 
80 		/* Advance to the next component or terminate after this. */
81 		if (*np == '\0')
82 			op = NULL;
83 		else
84 			op = np + 1;
85 
86 		/*
87 		 * If the path is too long complain.  This is a possible
88 		 * security issue; given a way to make the path too long
89 		 * the user may execute the wrong program.
90 		 */
91 		if (lp + ln + 2 > sizeof (buf)) {
92 			(void) write(STDERR_FILENO, "execvP: ", 8);
93 			(void) write(STDERR_FILENO, p, lp);
94 			(void) write(STDERR_FILENO, ": path too long\n",
95 			    16);
96 			continue;
97 		}
98 		memcpy(buf, p, lp);
99 		buf[lp] = '/';
100 		memcpy(buf + lp + 1, name, ln);
101 		buf[lp + ln + 1] = '\0';
102 
103 retry:		(void) execve(bp, argv, envp);
104 		switch (errno) {
105 		case E2BIG:
106 			goto done;
107 		case ELOOP:
108 		case ENAMETOOLONG:
109 		case ENOENT:
110 			break;
111 		case ENOEXEC:
112 			for (cnt = 0; argv[cnt]; ++cnt)
113 				;
114 
115 			/*
116 			 * cnt may be 0 above; always allocate at least
117 			 * 3 entries so that we can at least fit "sh", bp, and
118 			 * the NULL terminator.  We can rely on cnt to take into
119 			 * account the NULL terminator in all other scenarios,
120 			 * as we drop argv[0].
121 			 */
122 			memp = alloca(MAX(3, cnt + 2) * sizeof (char *));
123 			if (memp == NULL) {
124 				/* errno = ENOMEM; XXX override ENOEXEC? */
125 				goto done;
126 			}
127 			if (cnt > 0) {
128 				memp[0] = argv[0];
129 				memp[1] = bp;
130 				memcpy(memp + 2, argv + 1,
131 				    cnt * sizeof (char *));
132 			} else {
133 				memp[0] = "sh";
134 				memp[1] = bp;
135 				memp[2] = NULL;
136 			}
137 			(void) execve(_PATH_BSHELL,
138 			    __DECONST(char **, memp), envp);
139 			goto done;
140 		case ENOMEM:
141 			goto done;
142 		case ENOTDIR:
143 			break;
144 		case ETXTBSY:
145 			/*
146 			 * We used to retry here, but sh(1) doesn't.
147 			 */
148 			goto done;
149 		default:
150 			/*
151 			 * EACCES may be for an inaccessible directory or
152 			 * a non-executable file.  Call stat() to decide
153 			 * which.  This also handles ambiguities for EFAULT
154 			 * and EIO, and undocumented errors like ESTALE.
155 			 * We hope that the race for a stat() is unimportant.
156 			 */
157 			save_errno = errno;
158 			if (stat(bp, &sb) != 0)
159 				break;
160 			if (save_errno == EACCES) {
161 				eacces = 1;
162 				continue;
163 			}
164 			errno = save_errno;
165 			goto done;
166 		}
167 	}
168 	if (eacces)
169 		errno = EACCES;
170 	else
171 		errno = ENOENT;
172 done:
173 	return (-1);
174 }
175 
176 int
execvpe(const char * name,char * const argv[],char * const envp[])177 execvpe(const char *name, char * const argv[], char * const envp[])
178 {
179 	const char *path;
180 
181 	/* Get the path we're searching. */
182 	if ((path = getenv("PATH")) == NULL)
183 		path = _PATH_DEFPATH;
184 
185 	return (execvPe(name, path, argv, envp));
186 }
187 #endif /* !HAVE_EXECVPE */
188 
189 static __thread char errbuf[ERRBUFLEN];
190 
191 const char *
libzfs_error_init(int error)192 libzfs_error_init(int error)
193 {
194 	char *msg = errbuf;
195 	size_t msglen = sizeof (errbuf);
196 
197 	if (modfind("zfs") < 0) {
198 		size_t len = snprintf(msg, msglen, dgettext(TEXT_DOMAIN,
199 		    "Failed to load %s module: "), ZFS_KMOD);
200 		if (len >= msglen)
201 			len = msglen - 1;
202 		msg += len;
203 		msglen -= len;
204 	}
205 
206 	(void) snprintf(msg, msglen, "%s", zfs_strerror(error));
207 
208 	return (errbuf);
209 }
210 
211 /*
212  * Verify the required ZFS_DEV device is available and optionally attempt
213  * to load the ZFS modules.  Under normal circumstances the modules
214  * should already have been loaded by some external mechanism.
215  */
216 int
libzfs_load_module(void)217 libzfs_load_module(void)
218 {
219 	/*
220 	 * XXX: kldfind(ZFS_KMOD) would be nice here, but we retain
221 	 * modfind("zfs") so out-of-base openzfs userland works with the
222 	 * in-base module.
223 	 */
224 	if (modfind("zfs") < 0) {
225 		/* Not present in kernel, try loading it. */
226 		if (kldload(ZFS_KMOD) < 0 && errno != EEXIST) {
227 			return (errno);
228 		}
229 	}
230 	return (0);
231 }
232 
233 int
zpool_relabel_disk(libzfs_handle_t * hdl,const char * path,const char * msg)234 zpool_relabel_disk(libzfs_handle_t *hdl, const char *path, const char *msg)
235 {
236 	(void) hdl, (void) path, (void) msg;
237 	return (0);
238 }
239 
240 int
zpool_label_disk(libzfs_handle_t * hdl,zpool_handle_t * zhp,const char * name)241 zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, const char *name)
242 {
243 	(void) hdl, (void) zhp, (void) name;
244 	return (0);
245 }
246 
247 int
find_shares_object(differ_info_t * di)248 find_shares_object(differ_info_t *di)
249 {
250 	(void) di;
251 	return (0);
252 }
253 
254 int
zfs_destroy_snaps_nvl_os(libzfs_handle_t * hdl,nvlist_t * snaps)255 zfs_destroy_snaps_nvl_os(libzfs_handle_t *hdl, nvlist_t *snaps)
256 {
257 	(void) hdl, (void) snaps;
258 	return (0);
259 }
260 
261 /*
262  * Attach/detach the given filesystem to/from the given jail.
263  */
264 int
zfs_jail(zfs_handle_t * zhp,int jailid,int attach)265 zfs_jail(zfs_handle_t *zhp, int jailid, int attach)
266 {
267 	libzfs_handle_t *hdl = zhp->zfs_hdl;
268 	zfs_cmd_t zc = {"\0"};
269 	unsigned long cmd;
270 	int ret;
271 
272 	if (attach) {
273 		(void) snprintf(errbuf, sizeof (errbuf),
274 		    dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name);
275 	} else {
276 		(void) snprintf(errbuf, sizeof (errbuf),
277 		    dgettext(TEXT_DOMAIN, "cannot unjail '%s'"), zhp->zfs_name);
278 	}
279 
280 	switch (zhp->zfs_type) {
281 	case ZFS_TYPE_VOLUME:
282 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
283 		    "volumes can not be jailed"));
284 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
285 	case ZFS_TYPE_SNAPSHOT:
286 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
287 		    "snapshots can not be jailed"));
288 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
289 	case ZFS_TYPE_BOOKMARK:
290 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
291 		    "bookmarks can not be jailed"));
292 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
293 	case ZFS_TYPE_VDEV:
294 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
295 		    "vdevs can not be jailed"));
296 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
297 	case ZFS_TYPE_INVALID:
298 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
299 		    "invalid zfs_type_t: ZFS_TYPE_INVALID"));
300 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
301 	case ZFS_TYPE_POOL:
302 	case ZFS_TYPE_FILESYSTEM:
303 		/* OK */
304 		;
305 	}
306 	assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
307 
308 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
309 	zc.zc_objset_type = DMU_OST_ZFS;
310 	zc.zc_zoneid = jailid;
311 
312 	cmd = attach ? ZFS_IOC_JAIL : ZFS_IOC_UNJAIL;
313 	if ((ret = zfs_ioctl(hdl, cmd, &zc)) != 0)
314 		zfs_standard_error(hdl, errno, errbuf);
315 
316 	return (ret);
317 }
318 
319 /*
320  * Set loader options for next boot.
321  */
322 int
zpool_nextboot(libzfs_handle_t * hdl,uint64_t pool_guid,uint64_t dev_guid,const char * command)323 zpool_nextboot(libzfs_handle_t *hdl, uint64_t pool_guid, uint64_t dev_guid,
324     const char *command)
325 {
326 	zfs_cmd_t zc = {"\0"};
327 	nvlist_t *args;
328 
329 	args = fnvlist_alloc();
330 	fnvlist_add_uint64(args, ZPOOL_CONFIG_POOL_GUID, pool_guid);
331 	fnvlist_add_uint64(args, ZPOOL_CONFIG_GUID, dev_guid);
332 	fnvlist_add_string(args, "command", command);
333 	zcmd_write_src_nvlist(hdl, &zc, args);
334 	int error = zfs_ioctl(hdl, ZFS_IOC_NEXTBOOT, &zc);
335 	zcmd_free_nvlists(&zc);
336 	nvlist_free(args);
337 	return (error);
338 }
339 
340 /*
341  * Return allocated loaded module version, or NULL on error (with errno set)
342  */
343 char *
zfs_version_kernel(void)344 zfs_version_kernel(void)
345 {
346 	size_t l;
347 	if (sysctlbyname("vfs.zfs.version.module",
348 	    NULL, &l, NULL, 0) == -1)
349 		return (NULL);
350 	char *version = malloc(l);
351 	if (version == NULL)
352 		return (NULL);
353 	if (sysctlbyname("vfs.zfs.version.module",
354 	    version, &l, NULL, 0) == -1) {
355 		free(version);
356 		return (NULL);
357 	}
358 	return (version);
359 }
360