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)
		
	
			
		
			
				
	
	
		
			276 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			276 lines
		
	
	
		
			7.0 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) 2021 Klara, Inc.
 | 
						|
 */
 | 
						|
 | 
						|
#include <alloca.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <libintl.h>
 | 
						|
#include <math.h>
 | 
						|
#include <poll.h>
 | 
						|
#include <stdarg.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <strings.h>
 | 
						|
#include <sys/inotify.h>
 | 
						|
#include <sys/mntent.h>
 | 
						|
#include <sys/mnttab.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <sys/timerfd.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/wait.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
#include <libzfs.h>
 | 
						|
#include <libzfs_core.h>
 | 
						|
 | 
						|
#include "../../libzfs_impl.h"
 | 
						|
#include "zfs_prop.h"
 | 
						|
#include <libzutil.h>
 | 
						|
#include <sys/zfs_sysfs.h>
 | 
						|
 | 
						|
#define	ZDIFF_SHARESDIR		"/.zfs/shares/"
 | 
						|
 | 
						|
const char *
 | 
						|
libzfs_error_init(int error)
 | 
						|
{
 | 
						|
	switch (error) {
 | 
						|
	case ENXIO:
 | 
						|
		return (dgettext(TEXT_DOMAIN, "The ZFS modules are not "
 | 
						|
		    "loaded.\nTry running 'modprobe zfs' as root "
 | 
						|
		    "to load them."));
 | 
						|
	case ENOENT:
 | 
						|
		return (dgettext(TEXT_DOMAIN, "/dev/zfs and /proc/self/mounts "
 | 
						|
		    "are required.\nTry running 'udevadm trigger' and 'mount "
 | 
						|
		    "-t proc proc /proc' as root."));
 | 
						|
	case ENOEXEC:
 | 
						|
		return (dgettext(TEXT_DOMAIN, "The ZFS modules cannot be "
 | 
						|
		    "auto-loaded.\nTry running 'modprobe zfs' as "
 | 
						|
		    "root to manually load them."));
 | 
						|
	case EACCES:
 | 
						|
		return (dgettext(TEXT_DOMAIN, "Permission denied the "
 | 
						|
		    "ZFS utilities must be run as root."));
 | 
						|
	default:
 | 
						|
		return (dgettext(TEXT_DOMAIN, "Failed to initialize the "
 | 
						|
		    "libzfs library."));
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * zfs(4) is loaded by udev if there's a fstype=zfs device present,
 | 
						|
 * but if there isn't, load them automatically;
 | 
						|
 * always wait for ZFS_DEV to appear via udev.
 | 
						|
 *
 | 
						|
 * Environment variables:
 | 
						|
 * - ZFS_MODULE_TIMEOUT="<seconds>" - Seconds to wait for ZFS_DEV,
 | 
						|
 *                                    defaults to 10, max. 10 min.
 | 
						|
 */
 | 
						|
int
 | 
						|
libzfs_load_module(void)
 | 
						|
{
 | 
						|
	if (access(ZFS_DEV, F_OK) == 0)
 | 
						|
		return (0);
 | 
						|
 | 
						|
	if (access(ZFS_SYSFS_DIR, F_OK) != 0) {
 | 
						|
		char *argv[] = {(char *)"modprobe", (char *)"zfs", NULL};
 | 
						|
		if (libzfs_run_process("modprobe", argv, 0))
 | 
						|
			return (ENOEXEC);
 | 
						|
 | 
						|
		if (access(ZFS_SYSFS_DIR, F_OK) != 0)
 | 
						|
			return (ENXIO);
 | 
						|
	}
 | 
						|
 | 
						|
	const char *timeout_str = getenv("ZFS_MODULE_TIMEOUT");
 | 
						|
	int seconds = 10;
 | 
						|
	if (timeout_str)
 | 
						|
		seconds = MIN(strtol(timeout_str, NULL, 0), 600);
 | 
						|
	struct itimerspec timeout = {.it_value.tv_sec = MAX(seconds, 0)};
 | 
						|
 | 
						|
	int ino = inotify_init1(IN_CLOEXEC);
 | 
						|
	if (ino == -1)
 | 
						|
		return (ENOENT);
 | 
						|
	inotify_add_watch(ino, ZFS_DEVDIR, IN_CREATE);
 | 
						|
 | 
						|
	if (access(ZFS_DEV, F_OK) == 0) {
 | 
						|
		close(ino);
 | 
						|
		return (0);
 | 
						|
	} else if (seconds == 0) {
 | 
						|
		close(ino);
 | 
						|
		return (ENOENT);
 | 
						|
	}
 | 
						|
 | 
						|
	size_t evsz = sizeof (struct inotify_event) + NAME_MAX + 1;
 | 
						|
	struct inotify_event *ev = alloca(evsz);
 | 
						|
 | 
						|
	int tout = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
 | 
						|
	if (tout == -1) {
 | 
						|
		close(ino);
 | 
						|
		return (ENOENT);
 | 
						|
	}
 | 
						|
	timerfd_settime(tout, 0, &timeout, NULL);
 | 
						|
 | 
						|
	int ret = ENOENT;
 | 
						|
	struct pollfd pfds[] = {
 | 
						|
		{.fd = ino, .events = POLLIN},
 | 
						|
		{.fd = tout, .events = POLLIN},
 | 
						|
	};
 | 
						|
	while (poll(pfds, ARRAY_SIZE(pfds), -1) != -1) {
 | 
						|
		if (pfds[0].revents & POLLIN) {
 | 
						|
			verify(read(ino, ev, evsz) >
 | 
						|
			    sizeof (struct inotify_event));
 | 
						|
			if (strncmp(ev->name, &ZFS_DEV[sizeof (ZFS_DEVDIR)],
 | 
						|
			    ev->len) == 0) {
 | 
						|
				ret = 0;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if (pfds[1].revents & POLLIN)
 | 
						|
			break;
 | 
						|
	}
 | 
						|
	close(tout);
 | 
						|
	close(ino);
 | 
						|
	return (ret);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
find_shares_object(differ_info_t *di)
 | 
						|
{
 | 
						|
	char fullpath[MAXPATHLEN];
 | 
						|
	struct stat64 sb = { 0 };
 | 
						|
 | 
						|
	(void) strlcpy(fullpath, di->dsmnt, MAXPATHLEN);
 | 
						|
	(void) strlcat(fullpath, ZDIFF_SHARESDIR, MAXPATHLEN);
 | 
						|
 | 
						|
	if (stat64(fullpath, &sb) != 0) {
 | 
						|
		(void) snprintf(di->errbuf, sizeof (di->errbuf),
 | 
						|
		    dgettext(TEXT_DOMAIN, "Cannot stat %s"), fullpath);
 | 
						|
		return (zfs_error(di->zhp->zfs_hdl, EZFS_DIFF, di->errbuf));
 | 
						|
	}
 | 
						|
 | 
						|
	di->shares = (uint64_t)sb.st_ino;
 | 
						|
	return (0);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
zfs_destroy_snaps_nvl_os(libzfs_handle_t *hdl, nvlist_t *snaps)
 | 
						|
{
 | 
						|
	(void) hdl, (void) snaps;
 | 
						|
	return (0);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Return allocated loaded module version, or NULL on error (with errno set)
 | 
						|
 */
 | 
						|
char *
 | 
						|
zfs_version_kernel(void)
 | 
						|
{
 | 
						|
	FILE *f = fopen(ZFS_SYSFS_DIR "/version", "re");
 | 
						|
	if (f == NULL)
 | 
						|
		return (NULL);
 | 
						|
 | 
						|
	char *ret = NULL;
 | 
						|
	size_t l;
 | 
						|
	ssize_t read;
 | 
						|
	if ((read = getline(&ret, &l, f)) == -1) {
 | 
						|
		int err = errno;
 | 
						|
		fclose(f);
 | 
						|
		errno = err;
 | 
						|
		return (NULL);
 | 
						|
	}
 | 
						|
 | 
						|
	fclose(f);
 | 
						|
	if (ret[read - 1] == '\n')
 | 
						|
		ret[read - 1] = '\0';
 | 
						|
	return (ret);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Add or delete the given filesystem to/from the given user namespace.
 | 
						|
 */
 | 
						|
int
 | 
						|
zfs_userns(zfs_handle_t *zhp, const char *nspath, int attach)
 | 
						|
{
 | 
						|
	libzfs_handle_t *hdl = zhp->zfs_hdl;
 | 
						|
	zfs_cmd_t zc = {"\0"};
 | 
						|
	char errbuf[1024];
 | 
						|
	unsigned long cmd;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	if (attach) {
 | 
						|
		(void) snprintf(errbuf, sizeof (errbuf),
 | 
						|
		    dgettext(TEXT_DOMAIN, "cannot add '%s' to namespace"),
 | 
						|
		    zhp->zfs_name);
 | 
						|
	} else {
 | 
						|
		(void) snprintf(errbuf, sizeof (errbuf),
 | 
						|
		    dgettext(TEXT_DOMAIN, "cannot remove '%s' from namespace"),
 | 
						|
		    zhp->zfs_name);
 | 
						|
	}
 | 
						|
 | 
						|
	switch (zhp->zfs_type) {
 | 
						|
	case ZFS_TYPE_VOLUME:
 | 
						|
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
						|
		    "volumes can not be namespaced"));
 | 
						|
		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 | 
						|
	case ZFS_TYPE_SNAPSHOT:
 | 
						|
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
						|
		    "snapshots can not be namespaced"));
 | 
						|
		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 | 
						|
	case ZFS_TYPE_BOOKMARK:
 | 
						|
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
						|
		    "bookmarks can not be namespaced"));
 | 
						|
		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 | 
						|
	case ZFS_TYPE_VDEV:
 | 
						|
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
						|
		    "vdevs can not be namespaced"));
 | 
						|
		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:
 | 
						|
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
						|
		    "pools can not be namespaced"));
 | 
						|
		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 | 
						|
	case ZFS_TYPE_FILESYSTEM:
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	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_cleanup_fd = open(nspath, O_RDONLY);
 | 
						|
	if (zc.zc_cleanup_fd < 0) {
 | 
						|
		return (zfs_error(hdl, EZFS_NOT_USER_NAMESPACE, errbuf));
 | 
						|
	}
 | 
						|
 | 
						|
	cmd = attach ? ZFS_IOC_USERNS_ATTACH : ZFS_IOC_USERNS_DETACH;
 | 
						|
	if ((ret = zfs_ioctl(hdl, cmd, &zc)) != 0)
 | 
						|
		zfs_standard_error(hdl, errno, errbuf);
 | 
						|
 | 
						|
	(void) close(zc.zc_cleanup_fd);
 | 
						|
 | 
						|
	return (ret);
 | 
						|
}
 |