diff options
Diffstat (limited to 'sys/contrib/openzfs/lib/libzfs/os/linux/libzfs_util_os.c')
-rw-r--r-- | sys/contrib/openzfs/lib/libzfs/os/linux/libzfs_util_os.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/sys/contrib/openzfs/lib/libzfs/os/linux/libzfs_util_os.c b/sys/contrib/openzfs/lib/libzfs/os/linux/libzfs_util_os.c new file mode 100644 index 000000000000..55dfdf3723bd --- /dev/null +++ b/sys/contrib/openzfs/lib/libzfs/os/linux/libzfs_util_os.c @@ -0,0 +1,275 @@ +// 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); +} |