xref: /freebsd-src/sys/contrib/openzfs/lib/libzfs/os/freebsd/libzfs_compat.c (revision fd45b686f9d92f583366c75b22c04c7ee49709c0)
1eda14cbcSMatt Macy /*
2eda14cbcSMatt Macy  * CDDL HEADER START
3eda14cbcSMatt Macy  *
4eda14cbcSMatt Macy  * The contents of this file are subject to the terms of the
5eda14cbcSMatt Macy  * Common Development and Distribution License (the "License").
6eda14cbcSMatt Macy  * You may not use this file except in compliance with the License.
7eda14cbcSMatt Macy  *
8eda14cbcSMatt Macy  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9271171e0SMartin Matuska  * or https://opensource.org/licenses/CDDL-1.0.
10eda14cbcSMatt Macy  * See the License for the specific language governing permissions
11eda14cbcSMatt Macy  * and limitations under the License.
12eda14cbcSMatt Macy  *
13eda14cbcSMatt Macy  * When distributing Covered Code, include this CDDL HEADER in each
14eda14cbcSMatt Macy  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15eda14cbcSMatt Macy  * If applicable, add the following below this CDDL HEADER, with the
16eda14cbcSMatt Macy  * fields enclosed by brackets "[]" replaced with your own identifying
17eda14cbcSMatt Macy  * information: Portions Copyright [yyyy] [name of copyright owner]
18eda14cbcSMatt Macy  *
19eda14cbcSMatt Macy  * CDDL HEADER END
20eda14cbcSMatt Macy  */
21eda14cbcSMatt Macy 
22eda14cbcSMatt Macy /*
23eda14cbcSMatt Macy  * Copyright (c) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
24eda14cbcSMatt Macy  */
2516038816SMartin Matuska #include "../../libzfs_impl.h"
26eda14cbcSMatt Macy #include <libzfs.h>
27eda14cbcSMatt Macy #include <libzutil.h>
28eda14cbcSMatt Macy #include <sys/sysctl.h>
29eda14cbcSMatt Macy #include <libintl.h>
30eda14cbcSMatt Macy #include <sys/linker.h>
31eda14cbcSMatt Macy #include <sys/module.h>
32eda14cbcSMatt Macy #include <sys/stat.h>
33eda14cbcSMatt Macy #include <sys/param.h>
34eda14cbcSMatt Macy 
35eda14cbcSMatt Macy #ifdef IN_BASE
36eda14cbcSMatt Macy #define	ZFS_KMOD	"zfs"
37eda14cbcSMatt Macy #else
38eda14cbcSMatt Macy #define	ZFS_KMOD	"openzfs"
39eda14cbcSMatt Macy #endif
40eda14cbcSMatt Macy 
413494f7c0SMartin Matuska #ifndef HAVE_EXECVPE
423494f7c0SMartin Matuska /* FreeBSD prior to 15 lacks execvpe */
43eda14cbcSMatt Macy static int
execvPe(const char * name,const char * path,char * const * argv,char * const * envp)44eda14cbcSMatt Macy execvPe(const char *name, const char *path, char * const *argv,
45eda14cbcSMatt Macy     char * const *envp)
46eda14cbcSMatt Macy {
47eda14cbcSMatt Macy 	const char **memp;
48eda14cbcSMatt Macy 	size_t cnt, lp, ln;
49eda14cbcSMatt Macy 	int eacces, save_errno;
5016038816SMartin Matuska 	char buf[MAXPATHLEN];
5116038816SMartin Matuska 	const char *bp, *np, *op, *p;
52eda14cbcSMatt Macy 	struct stat sb;
53eda14cbcSMatt Macy 
54eda14cbcSMatt Macy 	eacces = 0;
55eda14cbcSMatt Macy 
56eda14cbcSMatt Macy 	/* If it's an absolute or relative path name, it's easy. */
57eda14cbcSMatt Macy 	if (strchr(name, '/')) {
58eda14cbcSMatt Macy 		bp = name;
5916038816SMartin Matuska 		op = NULL;
60eda14cbcSMatt Macy 		goto retry;
61eda14cbcSMatt Macy 	}
62eda14cbcSMatt Macy 	bp = buf;
63eda14cbcSMatt Macy 
64eda14cbcSMatt Macy 	/* If it's an empty path name, fail in the usual POSIX way. */
65eda14cbcSMatt Macy 	if (*name == '\0') {
66eda14cbcSMatt Macy 		errno = ENOENT;
67eda14cbcSMatt Macy 		return (-1);
68eda14cbcSMatt Macy 	}
69eda14cbcSMatt Macy 
7016038816SMartin Matuska 	op = path;
7116038816SMartin Matuska 	ln = strlen(name);
7216038816SMartin Matuska 	while (op != NULL) {
7316038816SMartin Matuska 		np = strchrnul(op, ':');
7416038816SMartin Matuska 
75eda14cbcSMatt Macy 		/*
76eda14cbcSMatt Macy 		 * It's a SHELL path -- double, leading and trailing colons
77eda14cbcSMatt Macy 		 * mean the current directory.
78eda14cbcSMatt Macy 		 */
7916038816SMartin Matuska 		if (np == op) {
8016038816SMartin Matuska 			/* Empty component. */
81eda14cbcSMatt Macy 			p = ".";
82eda14cbcSMatt Macy 			lp = 1;
8316038816SMartin Matuska 		} else {
8416038816SMartin Matuska 			/* Non-empty component. */
8516038816SMartin Matuska 			p = op;
8616038816SMartin Matuska 			lp = np - op;
8716038816SMartin Matuska 		}
8816038816SMartin Matuska 
8916038816SMartin Matuska 		/* Advance to the next component or terminate after this. */
9016038816SMartin Matuska 		if (*np == '\0')
9116038816SMartin Matuska 			op = NULL;
9216038816SMartin Matuska 		else
9316038816SMartin Matuska 			op = np + 1;
94eda14cbcSMatt Macy 
95eda14cbcSMatt Macy 		/*
96eda14cbcSMatt Macy 		 * If the path is too long complain.  This is a possible
97eda14cbcSMatt Macy 		 * security issue; given a way to make the path too long
98eda14cbcSMatt Macy 		 * the user may execute the wrong program.
99eda14cbcSMatt Macy 		 */
100eda14cbcSMatt Macy 		if (lp + ln + 2 > sizeof (buf)) {
101eda14cbcSMatt Macy 			(void) write(STDERR_FILENO, "execvP: ", 8);
102eda14cbcSMatt Macy 			(void) write(STDERR_FILENO, p, lp);
103eda14cbcSMatt Macy 			(void) write(STDERR_FILENO, ": path too long\n",
104eda14cbcSMatt Macy 			    16);
105eda14cbcSMatt Macy 			continue;
106eda14cbcSMatt Macy 		}
107da5137abSMartin Matuska 		memcpy(buf, p, lp);
108eda14cbcSMatt Macy 		buf[lp] = '/';
109da5137abSMartin Matuska 		memcpy(buf + lp + 1, name, ln);
110eda14cbcSMatt Macy 		buf[lp + ln + 1] = '\0';
111eda14cbcSMatt Macy 
112eda14cbcSMatt Macy retry:		(void) execve(bp, argv, envp);
113eda14cbcSMatt Macy 		switch (errno) {
114eda14cbcSMatt Macy 		case E2BIG:
115eda14cbcSMatt Macy 			goto done;
116eda14cbcSMatt Macy 		case ELOOP:
117eda14cbcSMatt Macy 		case ENAMETOOLONG:
118eda14cbcSMatt Macy 		case ENOENT:
119eda14cbcSMatt Macy 			break;
120eda14cbcSMatt Macy 		case ENOEXEC:
121eda14cbcSMatt Macy 			for (cnt = 0; argv[cnt]; ++cnt)
122eda14cbcSMatt Macy 				;
12316038816SMartin Matuska 
12416038816SMartin Matuska 			/*
12516038816SMartin Matuska 			 * cnt may be 0 above; always allocate at least
12616038816SMartin Matuska 			 * 3 entries so that we can at least fit "sh", bp, and
12716038816SMartin Matuska 			 * the NULL terminator.  We can rely on cnt to take into
12816038816SMartin Matuska 			 * account the NULL terminator in all other scenarios,
12916038816SMartin Matuska 			 * as we drop argv[0].
13016038816SMartin Matuska 			 */
13116038816SMartin Matuska 			memp = alloca(MAX(3, cnt + 2) * sizeof (char *));
132eda14cbcSMatt Macy 			if (memp == NULL) {
133eda14cbcSMatt Macy 				/* errno = ENOMEM; XXX override ENOEXEC? */
134eda14cbcSMatt Macy 				goto done;
135eda14cbcSMatt Macy 			}
13616038816SMartin Matuska 			if (cnt > 0) {
13716038816SMartin Matuska 				memp[0] = argv[0];
13816038816SMartin Matuska 				memp[1] = bp;
139da5137abSMartin Matuska 				memcpy(memp + 2, argv + 1,
14016038816SMartin Matuska 				    cnt * sizeof (char *));
14116038816SMartin Matuska 			} else {
142eda14cbcSMatt Macy 				memp[0] = "sh";
143eda14cbcSMatt Macy 				memp[1] = bp;
14416038816SMartin Matuska 				memp[2] = NULL;
14516038816SMartin Matuska 			}
14616038816SMartin Matuska 			(void) execve(_PATH_BSHELL,
14716038816SMartin Matuska 			    __DECONST(char **, memp), envp);
148eda14cbcSMatt Macy 			goto done;
149eda14cbcSMatt Macy 		case ENOMEM:
150eda14cbcSMatt Macy 			goto done;
151eda14cbcSMatt Macy 		case ENOTDIR:
152eda14cbcSMatt Macy 			break;
153eda14cbcSMatt Macy 		case ETXTBSY:
154eda14cbcSMatt Macy 			/*
155eda14cbcSMatt Macy 			 * We used to retry here, but sh(1) doesn't.
156eda14cbcSMatt Macy 			 */
157eda14cbcSMatt Macy 			goto done;
158eda14cbcSMatt Macy 		default:
159eda14cbcSMatt Macy 			/*
160eda14cbcSMatt Macy 			 * EACCES may be for an inaccessible directory or
161eda14cbcSMatt Macy 			 * a non-executable file.  Call stat() to decide
162eda14cbcSMatt Macy 			 * which.  This also handles ambiguities for EFAULT
163eda14cbcSMatt Macy 			 * and EIO, and undocumented errors like ESTALE.
164eda14cbcSMatt Macy 			 * We hope that the race for a stat() is unimportant.
165eda14cbcSMatt Macy 			 */
166eda14cbcSMatt Macy 			save_errno = errno;
167eda14cbcSMatt Macy 			if (stat(bp, &sb) != 0)
168eda14cbcSMatt Macy 				break;
169eda14cbcSMatt Macy 			if (save_errno == EACCES) {
170eda14cbcSMatt Macy 				eacces = 1;
171eda14cbcSMatt Macy 				continue;
172eda14cbcSMatt Macy 			}
173eda14cbcSMatt Macy 			errno = save_errno;
174eda14cbcSMatt Macy 			goto done;
175eda14cbcSMatt Macy 		}
176eda14cbcSMatt Macy 	}
177eda14cbcSMatt Macy 	if (eacces)
178eda14cbcSMatt Macy 		errno = EACCES;
179eda14cbcSMatt Macy 	else
180eda14cbcSMatt Macy 		errno = ENOENT;
181eda14cbcSMatt Macy done:
182eda14cbcSMatt Macy 	return (-1);
183eda14cbcSMatt Macy }
184eda14cbcSMatt Macy 
185eda14cbcSMatt Macy int
execvpe(const char * name,char * const argv[],char * const envp[])186eda14cbcSMatt Macy execvpe(const char *name, char * const argv[], char * const envp[])
187eda14cbcSMatt Macy {
188eda14cbcSMatt Macy 	const char *path;
189eda14cbcSMatt Macy 
190eda14cbcSMatt Macy 	/* Get the path we're searching. */
191eda14cbcSMatt Macy 	if ((path = getenv("PATH")) == NULL)
192eda14cbcSMatt Macy 		path = _PATH_DEFPATH;
193eda14cbcSMatt Macy 
194eda14cbcSMatt Macy 	return (execvPe(name, path, argv, envp));
195eda14cbcSMatt Macy }
1963494f7c0SMartin Matuska #endif /* !HAVE_EXECVPE */
197eda14cbcSMatt Macy 
19848184e76SRyan Libby static __thread char errbuf[ERRBUFLEN];
199180f8225SMatt Macy 
200eda14cbcSMatt Macy const char *
libzfs_error_init(int error)201eda14cbcSMatt Macy libzfs_error_init(int error)
202eda14cbcSMatt Macy {
203180f8225SMatt Macy 	char *msg = errbuf;
204c03c5b1cSMartin Matuska 	size_t msglen = sizeof (errbuf);
205eda14cbcSMatt Macy 
206180f8225SMatt Macy 	if (modfind("zfs") < 0) {
207c03c5b1cSMartin Matuska 		size_t len = snprintf(msg, msglen, dgettext(TEXT_DOMAIN,
208180f8225SMatt Macy 		    "Failed to load %s module: "), ZFS_KMOD);
209dbd5678dSMartin Matuska 		if (len >= msglen)
210dbd5678dSMartin Matuska 			len = msglen - 1;
211180f8225SMatt Macy 		msg += len;
212180f8225SMatt Macy 		msglen -= len;
213180f8225SMatt Macy 	}
214180f8225SMatt Macy 
215*fd45b686SMartin Matuska 	(void) snprintf(msg, msglen, "%s", zfs_strerror(error));
216180f8225SMatt Macy 
217180f8225SMatt Macy 	return (errbuf);
218eda14cbcSMatt Macy }
219eda14cbcSMatt Macy 
220eda14cbcSMatt Macy int
zfs_ioctl(libzfs_handle_t * hdl,int request,zfs_cmd_t * zc)221eda14cbcSMatt Macy zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
222eda14cbcSMatt Macy {
2236ba2210eSMartin Matuska 	return (lzc_ioctl_fd(hdl->libzfs_fd, request, zc));
224eda14cbcSMatt Macy }
225eda14cbcSMatt Macy 
226eda14cbcSMatt Macy /*
227eda14cbcSMatt Macy  * Verify the required ZFS_DEV device is available and optionally attempt
228eda14cbcSMatt Macy  * to load the ZFS modules.  Under normal circumstances the modules
229eda14cbcSMatt Macy  * should already have been loaded by some external mechanism.
230eda14cbcSMatt Macy  */
231eda14cbcSMatt Macy int
libzfs_load_module(void)232eda14cbcSMatt Macy libzfs_load_module(void)
233eda14cbcSMatt Macy {
234eda14cbcSMatt Macy 	/*
235eda14cbcSMatt Macy 	 * XXX: kldfind(ZFS_KMOD) would be nice here, but we retain
236eda14cbcSMatt Macy 	 * modfind("zfs") so out-of-base openzfs userland works with the
237eda14cbcSMatt Macy 	 * in-base module.
238eda14cbcSMatt Macy 	 */
239eda14cbcSMatt Macy 	if (modfind("zfs") < 0) {
240eda14cbcSMatt Macy 		/* Not present in kernel, try loading it. */
241eda14cbcSMatt Macy 		if (kldload(ZFS_KMOD) < 0 && errno != EEXIST) {
242eda14cbcSMatt Macy 			return (errno);
243eda14cbcSMatt Macy 		}
244eda14cbcSMatt Macy 	}
245eda14cbcSMatt Macy 	return (0);
246eda14cbcSMatt Macy }
247eda14cbcSMatt Macy 
248eda14cbcSMatt Macy int
zpool_relabel_disk(libzfs_handle_t * hdl,const char * path,const char * msg)249eda14cbcSMatt Macy zpool_relabel_disk(libzfs_handle_t *hdl, const char *path, const char *msg)
250eda14cbcSMatt Macy {
251c03c5b1cSMartin Matuska 	(void) hdl, (void) path, (void) msg;
252eda14cbcSMatt Macy 	return (0);
253eda14cbcSMatt Macy }
254eda14cbcSMatt Macy 
255eda14cbcSMatt Macy int
zpool_label_disk(libzfs_handle_t * hdl,zpool_handle_t * zhp,const char * name)256eda14cbcSMatt Macy zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, const char *name)
257eda14cbcSMatt Macy {
258c03c5b1cSMartin Matuska 	(void) hdl, (void) zhp, (void) name;
259eda14cbcSMatt Macy 	return (0);
260eda14cbcSMatt Macy }
261eda14cbcSMatt Macy 
262eda14cbcSMatt Macy int
find_shares_object(differ_info_t * di)263eda14cbcSMatt Macy find_shares_object(differ_info_t *di)
264eda14cbcSMatt Macy {
265c03c5b1cSMartin Matuska 	(void) di;
266eda14cbcSMatt Macy 	return (0);
267eda14cbcSMatt Macy }
268eda14cbcSMatt Macy 
2696ba2210eSMartin Matuska int
zfs_destroy_snaps_nvl_os(libzfs_handle_t * hdl,nvlist_t * snaps)2706ba2210eSMartin Matuska zfs_destroy_snaps_nvl_os(libzfs_handle_t *hdl, nvlist_t *snaps)
2716ba2210eSMartin Matuska {
272c03c5b1cSMartin Matuska 	(void) hdl, (void) snaps;
2736ba2210eSMartin Matuska 	return (0);
2746ba2210eSMartin Matuska }
2756ba2210eSMartin Matuska 
276eda14cbcSMatt Macy /*
277eda14cbcSMatt Macy  * Attach/detach the given filesystem to/from the given jail.
278eda14cbcSMatt Macy  */
279eda14cbcSMatt Macy int
zfs_jail(zfs_handle_t * zhp,int jailid,int attach)280eda14cbcSMatt Macy zfs_jail(zfs_handle_t *zhp, int jailid, int attach)
281eda14cbcSMatt Macy {
282eda14cbcSMatt Macy 	libzfs_handle_t *hdl = zhp->zfs_hdl;
283eda14cbcSMatt Macy 	zfs_cmd_t zc = {"\0"};
284eda14cbcSMatt Macy 	unsigned long cmd;
285eda14cbcSMatt Macy 	int ret;
286eda14cbcSMatt Macy 
287eda14cbcSMatt Macy 	if (attach) {
288eda14cbcSMatt Macy 		(void) snprintf(errbuf, sizeof (errbuf),
289eda14cbcSMatt Macy 		    dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name);
290eda14cbcSMatt Macy 	} else {
291eda14cbcSMatt Macy 		(void) snprintf(errbuf, sizeof (errbuf),
292eda14cbcSMatt Macy 		    dgettext(TEXT_DOMAIN, "cannot unjail '%s'"), zhp->zfs_name);
293eda14cbcSMatt Macy 	}
294eda14cbcSMatt Macy 
295eda14cbcSMatt Macy 	switch (zhp->zfs_type) {
296eda14cbcSMatt Macy 	case ZFS_TYPE_VOLUME:
297eda14cbcSMatt Macy 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
298eda14cbcSMatt Macy 		    "volumes can not be jailed"));
299eda14cbcSMatt Macy 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
300eda14cbcSMatt Macy 	case ZFS_TYPE_SNAPSHOT:
301eda14cbcSMatt Macy 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
302eda14cbcSMatt Macy 		    "snapshots can not be jailed"));
303eda14cbcSMatt Macy 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
304eda14cbcSMatt Macy 	case ZFS_TYPE_BOOKMARK:
305eda14cbcSMatt Macy 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
306eda14cbcSMatt Macy 		    "bookmarks can not be jailed"));
307eda14cbcSMatt Macy 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
308681ce946SMartin Matuska 	case ZFS_TYPE_VDEV:
309681ce946SMartin Matuska 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
310681ce946SMartin Matuska 		    "vdevs can not be jailed"));
311681ce946SMartin Matuska 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
312c03c5b1cSMartin Matuska 	case ZFS_TYPE_INVALID:
313c03c5b1cSMartin Matuska 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
314c03c5b1cSMartin Matuska 		    "invalid zfs_type_t: ZFS_TYPE_INVALID"));
315c03c5b1cSMartin Matuska 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
316eda14cbcSMatt Macy 	case ZFS_TYPE_POOL:
317eda14cbcSMatt Macy 	case ZFS_TYPE_FILESYSTEM:
318eda14cbcSMatt Macy 		/* OK */
319eda14cbcSMatt Macy 		;
320eda14cbcSMatt Macy 	}
321eda14cbcSMatt Macy 	assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
322eda14cbcSMatt Macy 
323eda14cbcSMatt Macy 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
324eda14cbcSMatt Macy 	zc.zc_objset_type = DMU_OST_ZFS;
325eda14cbcSMatt Macy 	zc.zc_zoneid = jailid;
326eda14cbcSMatt Macy 
327eda14cbcSMatt Macy 	cmd = attach ? ZFS_IOC_JAIL : ZFS_IOC_UNJAIL;
328eda14cbcSMatt Macy 	if ((ret = zfs_ioctl(hdl, cmd, &zc)) != 0)
329eda14cbcSMatt Macy 		zfs_standard_error(hdl, errno, errbuf);
330eda14cbcSMatt Macy 
331eda14cbcSMatt Macy 	return (ret);
332eda14cbcSMatt Macy }
333eda14cbcSMatt Macy 
334eda14cbcSMatt Macy /*
335eda14cbcSMatt Macy  * Set loader options for next boot.
336eda14cbcSMatt Macy  */
337eda14cbcSMatt Macy int
zpool_nextboot(libzfs_handle_t * hdl,uint64_t pool_guid,uint64_t dev_guid,const char * command)338eda14cbcSMatt Macy zpool_nextboot(libzfs_handle_t *hdl, uint64_t pool_guid, uint64_t dev_guid,
339eda14cbcSMatt Macy     const char *command)
340eda14cbcSMatt Macy {
341eda14cbcSMatt Macy 	zfs_cmd_t zc = {"\0"};
342eda14cbcSMatt Macy 	nvlist_t *args;
343eda14cbcSMatt Macy 
344eda14cbcSMatt Macy 	args = fnvlist_alloc();
345eda14cbcSMatt Macy 	fnvlist_add_uint64(args, ZPOOL_CONFIG_POOL_GUID, pool_guid);
346eda14cbcSMatt Macy 	fnvlist_add_uint64(args, ZPOOL_CONFIG_GUID, dev_guid);
347eda14cbcSMatt Macy 	fnvlist_add_string(args, "command", command);
348716fd348SMartin Matuska 	zcmd_write_src_nvlist(hdl, &zc, args);
349716fd348SMartin Matuska 	int error = zfs_ioctl(hdl, ZFS_IOC_NEXTBOOT, &zc);
350eda14cbcSMatt Macy 	zcmd_free_nvlists(&zc);
351eda14cbcSMatt Macy 	nvlist_free(args);
352eda14cbcSMatt Macy 	return (error);
353eda14cbcSMatt Macy }
354eda14cbcSMatt Macy 
355eda14cbcSMatt Macy /*
356e3aa18adSMartin Matuska  * Return allocated loaded module version, or NULL on error (with errno set)
357eda14cbcSMatt Macy  */
358e3aa18adSMartin Matuska char *
zfs_version_kernel(void)359e3aa18adSMartin Matuska zfs_version_kernel(void)
360eda14cbcSMatt Macy {
361e3aa18adSMartin Matuska 	size_t l;
362e3aa18adSMartin Matuska 	if (sysctlbyname("vfs.zfs.version.module",
363e3aa18adSMartin Matuska 	    NULL, &l, NULL, 0) == -1)
364e3aa18adSMartin Matuska 		return (NULL);
365e3aa18adSMartin Matuska 	char *version = malloc(l);
366e3aa18adSMartin Matuska 	if (version == NULL)
367e3aa18adSMartin Matuska 		return (NULL);
368e3aa18adSMartin Matuska 	if (sysctlbyname("vfs.zfs.version.module",
369e3aa18adSMartin Matuska 	    version, &l, NULL, 0) == -1) {
370e3aa18adSMartin Matuska 		free(version);
371e3aa18adSMartin Matuska 		return (NULL);
372e3aa18adSMartin Matuska 	}
373e3aa18adSMartin Matuska 	return (version);
374eda14cbcSMatt Macy }
375