mirror of
				https://git.proxmox.com/git/mirror_zfs.git
				synced 2025-10-26 18:05:04 +03:00 
			
		
		
		
	Sponsored-by: https://despairlabs.com/sponsor/
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Rob Norris <robn@despairlabs.com>
Closes #17344
(cherry picked from commit f454cc1723)
		
	
			
		
			
				
	
	
		
			370 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			370 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: CDDL-1.0
 | 
						|
/*
 | 
						|
 * CDDL HEADER START
 | 
						|
 *
 | 
						|
 * The contents of this file are subject to the terms of the
 | 
						|
 * Common Development and Distribution License (the "License").
 | 
						|
 * You may not use this file except in compliance with the License.
 | 
						|
 *
 | 
						|
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 | 
						|
 * or https://opensource.org/licenses/CDDL-1.0.
 | 
						|
 * See the License for the specific language governing permissions
 | 
						|
 * and limitations under the License.
 | 
						|
 *
 | 
						|
 * When distributing Covered Code, include this CDDL HEADER in each
 | 
						|
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 | 
						|
 * If applicable, add the following below this CDDL HEADER, with the
 | 
						|
 * fields enclosed by brackets "[]" replaced with your own identifying
 | 
						|
 * information: Portions Copyright [yyyy] [name of copyright owner]
 | 
						|
 *
 | 
						|
 * CDDL HEADER END
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * Copyright (c) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
 | 
						|
 */
 | 
						|
#include "../../libzfs_impl.h"
 | 
						|
#include <libzfs.h>
 | 
						|
#include <libzutil.h>
 | 
						|
#include <sys/sysctl.h>
 | 
						|
#include <libintl.h>
 | 
						|
#include <sys/linker.h>
 | 
						|
#include <sys/module.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <sys/param.h>
 | 
						|
 | 
						|
#ifdef IN_BASE
 | 
						|
#define	ZFS_KMOD	"zfs"
 | 
						|
#else
 | 
						|
#define	ZFS_KMOD	"openzfs"
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef HAVE_EXECVPE
 | 
						|
/* FreeBSD prior to 15 lacks execvpe */
 | 
						|
static int
 | 
						|
execvPe(const char *name, const char *path, char * const *argv,
 | 
						|
    char * const *envp)
 | 
						|
{
 | 
						|
	const char **memp;
 | 
						|
	size_t cnt, lp, ln;
 | 
						|
	int eacces, save_errno;
 | 
						|
	char buf[MAXPATHLEN];
 | 
						|
	const char *bp, *np, *op, *p;
 | 
						|
	struct stat sb;
 | 
						|
 | 
						|
	eacces = 0;
 | 
						|
 | 
						|
	/* If it's an absolute or relative path name, it's easy. */
 | 
						|
	if (strchr(name, '/')) {
 | 
						|
		bp = name;
 | 
						|
		op = NULL;
 | 
						|
		goto retry;
 | 
						|
	}
 | 
						|
	bp = buf;
 | 
						|
 | 
						|
	/* If it's an empty path name, fail in the usual POSIX way. */
 | 
						|
	if (*name == '\0') {
 | 
						|
		errno = ENOENT;
 | 
						|
		return (-1);
 | 
						|
	}
 | 
						|
 | 
						|
	op = path;
 | 
						|
	ln = strlen(name);
 | 
						|
	while (op != NULL) {
 | 
						|
		np = strchrnul(op, ':');
 | 
						|
 | 
						|
		/*
 | 
						|
		 * It's a SHELL path -- double, leading and trailing colons
 | 
						|
		 * mean the current directory.
 | 
						|
		 */
 | 
						|
		if (np == op) {
 | 
						|
			/* Empty component. */
 | 
						|
			p = ".";
 | 
						|
			lp = 1;
 | 
						|
		} else {
 | 
						|
			/* Non-empty component. */
 | 
						|
			p = op;
 | 
						|
			lp = np - op;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Advance to the next component or terminate after this. */
 | 
						|
		if (*np == '\0')
 | 
						|
			op = NULL;
 | 
						|
		else
 | 
						|
			op = np + 1;
 | 
						|
 | 
						|
		/*
 | 
						|
		 * If the path is too long complain.  This is a possible
 | 
						|
		 * security issue; given a way to make the path too long
 | 
						|
		 * the user may execute the wrong program.
 | 
						|
		 */
 | 
						|
		if (lp + ln + 2 > sizeof (buf)) {
 | 
						|
			(void) write(STDERR_FILENO, "execvP: ", 8);
 | 
						|
			(void) write(STDERR_FILENO, p, lp);
 | 
						|
			(void) write(STDERR_FILENO, ": path too long\n",
 | 
						|
			    16);
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		memcpy(buf, p, lp);
 | 
						|
		buf[lp] = '/';
 | 
						|
		memcpy(buf + lp + 1, name, ln);
 | 
						|
		buf[lp + ln + 1] = '\0';
 | 
						|
 | 
						|
retry:		(void) execve(bp, argv, envp);
 | 
						|
		switch (errno) {
 | 
						|
		case E2BIG:
 | 
						|
			goto done;
 | 
						|
		case ELOOP:
 | 
						|
		case ENAMETOOLONG:
 | 
						|
		case ENOENT:
 | 
						|
			break;
 | 
						|
		case ENOEXEC:
 | 
						|
			for (cnt = 0; argv[cnt]; ++cnt)
 | 
						|
				;
 | 
						|
 | 
						|
			/*
 | 
						|
			 * cnt may be 0 above; always allocate at least
 | 
						|
			 * 3 entries so that we can at least fit "sh", bp, and
 | 
						|
			 * the NULL terminator.  We can rely on cnt to take into
 | 
						|
			 * account the NULL terminator in all other scenarios,
 | 
						|
			 * as we drop argv[0].
 | 
						|
			 */
 | 
						|
			memp = alloca(MAX(3, cnt + 2) * sizeof (char *));
 | 
						|
			if (memp == NULL) {
 | 
						|
				/* errno = ENOMEM; XXX override ENOEXEC? */
 | 
						|
				goto done;
 | 
						|
			}
 | 
						|
			if (cnt > 0) {
 | 
						|
				memp[0] = argv[0];
 | 
						|
				memp[1] = bp;
 | 
						|
				memcpy(memp + 2, argv + 1,
 | 
						|
				    cnt * sizeof (char *));
 | 
						|
			} else {
 | 
						|
				memp[0] = "sh";
 | 
						|
				memp[1] = bp;
 | 
						|
				memp[2] = NULL;
 | 
						|
			}
 | 
						|
			(void) execve(_PATH_BSHELL,
 | 
						|
			    __DECONST(char **, memp), envp);
 | 
						|
			goto done;
 | 
						|
		case ENOMEM:
 | 
						|
			goto done;
 | 
						|
		case ENOTDIR:
 | 
						|
			break;
 | 
						|
		case ETXTBSY:
 | 
						|
			/*
 | 
						|
			 * We used to retry here, but sh(1) doesn't.
 | 
						|
			 */
 | 
						|
			goto done;
 | 
						|
		default:
 | 
						|
			/*
 | 
						|
			 * EACCES may be for an inaccessible directory or
 | 
						|
			 * a non-executable file.  Call stat() to decide
 | 
						|
			 * which.  This also handles ambiguities for EFAULT
 | 
						|
			 * and EIO, and undocumented errors like ESTALE.
 | 
						|
			 * We hope that the race for a stat() is unimportant.
 | 
						|
			 */
 | 
						|
			save_errno = errno;
 | 
						|
			if (stat(bp, &sb) != 0)
 | 
						|
				break;
 | 
						|
			if (save_errno == EACCES) {
 | 
						|
				eacces = 1;
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			errno = save_errno;
 | 
						|
			goto done;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (eacces)
 | 
						|
		errno = EACCES;
 | 
						|
	else
 | 
						|
		errno = ENOENT;
 | 
						|
done:
 | 
						|
	return (-1);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
execvpe(const char *name, char * const argv[], char * const envp[])
 | 
						|
{
 | 
						|
	const char *path;
 | 
						|
 | 
						|
	/* Get the path we're searching. */
 | 
						|
	if ((path = getenv("PATH")) == NULL)
 | 
						|
		path = _PATH_DEFPATH;
 | 
						|
 | 
						|
	return (execvPe(name, path, argv, envp));
 | 
						|
}
 | 
						|
#endif /* !HAVE_EXECVPE */
 | 
						|
 | 
						|
static __thread char errbuf[ERRBUFLEN];
 | 
						|
 | 
						|
const char *
 | 
						|
libzfs_error_init(int error)
 | 
						|
{
 | 
						|
	char *msg = errbuf;
 | 
						|
	size_t msglen = sizeof (errbuf);
 | 
						|
 | 
						|
	if (modfind("zfs") < 0) {
 | 
						|
		size_t len = snprintf(msg, msglen, dgettext(TEXT_DOMAIN,
 | 
						|
		    "Failed to load %s module: "), ZFS_KMOD);
 | 
						|
		if (len >= msglen)
 | 
						|
			len = msglen - 1;
 | 
						|
		msg += len;
 | 
						|
		msglen -= len;
 | 
						|
	}
 | 
						|
 | 
						|
	(void) snprintf(msg, msglen, "%s", zfs_strerror(error));
 | 
						|
 | 
						|
	return (errbuf);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Verify the required ZFS_DEV device is available and optionally attempt
 | 
						|
 * to load the ZFS modules.  Under normal circumstances the modules
 | 
						|
 * should already have been loaded by some external mechanism.
 | 
						|
 */
 | 
						|
int
 | 
						|
libzfs_load_module(void)
 | 
						|
{
 | 
						|
	/*
 | 
						|
	 * XXX: kldfind(ZFS_KMOD) would be nice here, but we retain
 | 
						|
	 * modfind("zfs") so out-of-base openzfs userland works with the
 | 
						|
	 * in-base module.
 | 
						|
	 */
 | 
						|
	if (modfind("zfs") < 0) {
 | 
						|
		/* Not present in kernel, try loading it. */
 | 
						|
		if (kldload(ZFS_KMOD) < 0 && errno != EEXIST) {
 | 
						|
			return (errno);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return (0);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
zpool_relabel_disk(libzfs_handle_t *hdl, const char *path, const char *msg)
 | 
						|
{
 | 
						|
	(void) hdl, (void) path, (void) msg;
 | 
						|
	return (0);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, const char *name)
 | 
						|
{
 | 
						|
	(void) hdl, (void) zhp, (void) name;
 | 
						|
	return (0);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
find_shares_object(differ_info_t *di)
 | 
						|
{
 | 
						|
	(void) di;
 | 
						|
	return (0);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
zfs_destroy_snaps_nvl_os(libzfs_handle_t *hdl, nvlist_t *snaps)
 | 
						|
{
 | 
						|
	(void) hdl, (void) snaps;
 | 
						|
	return (0);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Attach/detach the given filesystem to/from the given jail.
 | 
						|
 */
 | 
						|
int
 | 
						|
zfs_jail(zfs_handle_t *zhp, int jailid, int attach)
 | 
						|
{
 | 
						|
	libzfs_handle_t *hdl = zhp->zfs_hdl;
 | 
						|
	zfs_cmd_t zc = {"\0"};
 | 
						|
	unsigned long cmd;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	if (attach) {
 | 
						|
		(void) snprintf(errbuf, sizeof (errbuf),
 | 
						|
		    dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name);
 | 
						|
	} else {
 | 
						|
		(void) snprintf(errbuf, sizeof (errbuf),
 | 
						|
		    dgettext(TEXT_DOMAIN, "cannot unjail '%s'"), zhp->zfs_name);
 | 
						|
	}
 | 
						|
 | 
						|
	switch (zhp->zfs_type) {
 | 
						|
	case ZFS_TYPE_VOLUME:
 | 
						|
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
						|
		    "volumes can not be jailed"));
 | 
						|
		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 | 
						|
	case ZFS_TYPE_SNAPSHOT:
 | 
						|
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
						|
		    "snapshots can not be jailed"));
 | 
						|
		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 | 
						|
	case ZFS_TYPE_BOOKMARK:
 | 
						|
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
						|
		    "bookmarks can not be jailed"));
 | 
						|
		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 | 
						|
	case ZFS_TYPE_VDEV:
 | 
						|
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
						|
		    "vdevs can not be jailed"));
 | 
						|
		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 | 
						|
	case ZFS_TYPE_INVALID:
 | 
						|
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
						|
		    "invalid zfs_type_t: ZFS_TYPE_INVALID"));
 | 
						|
		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 | 
						|
	case ZFS_TYPE_POOL:
 | 
						|
	case ZFS_TYPE_FILESYSTEM:
 | 
						|
		/* OK */
 | 
						|
		;
 | 
						|
	}
 | 
						|
	assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
 | 
						|
 | 
						|
	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 | 
						|
	zc.zc_objset_type = DMU_OST_ZFS;
 | 
						|
	zc.zc_zoneid = jailid;
 | 
						|
 | 
						|
	cmd = attach ? ZFS_IOC_JAIL : ZFS_IOC_UNJAIL;
 | 
						|
	if ((ret = zfs_ioctl(hdl, cmd, &zc)) != 0)
 | 
						|
		zfs_standard_error(hdl, errno, errbuf);
 | 
						|
 | 
						|
	return (ret);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Set loader options for next boot.
 | 
						|
 */
 | 
						|
int
 | 
						|
zpool_nextboot(libzfs_handle_t *hdl, uint64_t pool_guid, uint64_t dev_guid,
 | 
						|
    const char *command)
 | 
						|
{
 | 
						|
	zfs_cmd_t zc = {"\0"};
 | 
						|
	nvlist_t *args;
 | 
						|
 | 
						|
	args = fnvlist_alloc();
 | 
						|
	fnvlist_add_uint64(args, ZPOOL_CONFIG_POOL_GUID, pool_guid);
 | 
						|
	fnvlist_add_uint64(args, ZPOOL_CONFIG_GUID, dev_guid);
 | 
						|
	fnvlist_add_string(args, "command", command);
 | 
						|
	zcmd_write_src_nvlist(hdl, &zc, args);
 | 
						|
	int error = zfs_ioctl(hdl, ZFS_IOC_NEXTBOOT, &zc);
 | 
						|
	zcmd_free_nvlists(&zc);
 | 
						|
	nvlist_free(args);
 | 
						|
	return (error);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Return allocated loaded module version, or NULL on error (with errno set)
 | 
						|
 */
 | 
						|
char *
 | 
						|
zfs_version_kernel(void)
 | 
						|
{
 | 
						|
	size_t l;
 | 
						|
	if (sysctlbyname("vfs.zfs.version.module",
 | 
						|
	    NULL, &l, NULL, 0) == -1)
 | 
						|
		return (NULL);
 | 
						|
	char *version = malloc(l);
 | 
						|
	if (version == NULL)
 | 
						|
		return (NULL);
 | 
						|
	if (sysctlbyname("vfs.zfs.version.module",
 | 
						|
	    version, &l, NULL, 0) == -1) {
 | 
						|
		free(version);
 | 
						|
		return (NULL);
 | 
						|
	}
 | 
						|
	return (version);
 | 
						|
}
 |