2019-10-03 20:33:16 +03:00
|
|
|
/*
|
|
|
|
* 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 http://www.opensolaris.org/os/licensing.
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <libintl.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <strings.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/mnttab.h>
|
|
|
|
#include <sys/mntent.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
|
|
|
#include <libzfs.h>
|
|
|
|
#include <libzfs_core.h>
|
|
|
|
|
|
|
|
#include "libzfs_impl.h"
|
|
|
|
#include "zfs_prop.h"
|
|
|
|
#include <libzutil.h>
|
2020-02-13 00:00:19 +03:00
|
|
|
#include <sys/zfs_sysfs.h>
|
2019-10-03 20:33:16 +03:00
|
|
|
|
|
|
|
#define ZDIFF_SHARESDIR "/.zfs/shares/"
|
|
|
|
|
|
|
|
int
|
|
|
|
zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
|
|
|
|
{
|
|
|
|
return (ioctl(hdl->libzfs_fd, request, zc));
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
libzfs_error_init(int error)
|
|
|
|
{
|
|
|
|
switch (error) {
|
|
|
|
case ENXIO:
|
|
|
|
return (dgettext(TEXT_DOMAIN, "The ZFS modules are not "
|
|
|
|
"loaded.\nTry running '/sbin/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 '/sbin/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."));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
libzfs_module_loaded(const char *module)
|
|
|
|
{
|
|
|
|
const char path_prefix[] = "/sys/module/";
|
|
|
|
char path[256];
|
|
|
|
|
|
|
|
memcpy(path, path_prefix, sizeof (path_prefix) - 1);
|
|
|
|
strcpy(path + sizeof (path_prefix) - 1, module);
|
|
|
|
|
|
|
|
return (access(path, F_OK) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* Environment variables:
|
|
|
|
* - ZFS_MODULE_LOADING="YES|yes|ON|on" - Attempt to load modules.
|
|
|
|
* - ZFS_MODULE_TIMEOUT="<seconds>" - Seconds to wait for ZFS_DEV
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
libzfs_load_module_impl(const char *module)
|
|
|
|
{
|
|
|
|
char *argv[4] = {"/sbin/modprobe", "-q", (char *)module, (char *)0};
|
|
|
|
char *load_str, *timeout_str;
|
|
|
|
long timeout = 10; /* seconds */
|
|
|
|
long busy_timeout = 10; /* milliseconds */
|
|
|
|
int load = 0, fd;
|
|
|
|
hrtime_t start;
|
|
|
|
|
|
|
|
/* Optionally request module loading */
|
|
|
|
if (!libzfs_module_loaded(module)) {
|
|
|
|
load_str = getenv("ZFS_MODULE_LOADING");
|
|
|
|
if (load_str) {
|
|
|
|
if (!strncasecmp(load_str, "YES", strlen("YES")) ||
|
|
|
|
!strncasecmp(load_str, "ON", strlen("ON")))
|
|
|
|
load = 1;
|
|
|
|
else
|
|
|
|
load = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (load) {
|
|
|
|
if (libzfs_run_process("/sbin/modprobe", argv, 0))
|
|
|
|
return (ENOEXEC);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!libzfs_module_loaded(module))
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Device creation by udev is asynchronous and waiting may be
|
|
|
|
* required. Busy wait for 10ms and then fall back to polling every
|
|
|
|
* 10ms for the allowed timeout (default 10s, max 10m). This is
|
|
|
|
* done to optimize for the common case where the device is
|
|
|
|
* immediately available and to avoid penalizing the possible
|
|
|
|
* case where udev is slow or unable to create the device.
|
|
|
|
*/
|
|
|
|
timeout_str = getenv("ZFS_MODULE_TIMEOUT");
|
|
|
|
if (timeout_str) {
|
|
|
|
timeout = strtol(timeout_str, NULL, 0);
|
|
|
|
timeout = MAX(MIN(timeout, (10 * 60)), 0); /* 0 <= N <= 600 */
|
|
|
|
}
|
|
|
|
|
|
|
|
start = gethrtime();
|
|
|
|
do {
|
2021-04-08 23:17:38 +03:00
|
|
|
fd = open(ZFS_DEV, O_RDWR | O_CLOEXEC);
|
2019-10-03 20:33:16 +03:00
|
|
|
if (fd >= 0) {
|
|
|
|
(void) close(fd);
|
|
|
|
return (0);
|
|
|
|
} else if (errno != ENOENT) {
|
|
|
|
return (errno);
|
|
|
|
} else if (NSEC2MSEC(gethrtime() - start) < busy_timeout) {
|
|
|
|
sched_yield();
|
|
|
|
} else {
|
|
|
|
usleep(10 * MILLISEC);
|
|
|
|
}
|
|
|
|
} while (NSEC2MSEC(gethrtime() - start) < (timeout * MILLISEC));
|
|
|
|
|
|
|
|
return (ENOENT);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
libzfs_load_module(void)
|
|
|
|
{
|
|
|
|
return (libzfs_load_module_impl(ZFS_DRIVER));
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2020-02-13 00:00:19 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Fill given version buffer with zfs kernel version read from ZFS_SYSFS_DIR
|
|
|
|
* Returns 0 on success, and -1 on error (with errno set)
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
zfs_version_kernel(char *version, int len)
|
|
|
|
{
|
|
|
|
int _errno;
|
|
|
|
int fd;
|
|
|
|
int rlen;
|
|
|
|
|
2021-04-08 23:17:38 +03:00
|
|
|
if ((fd = open(ZFS_SYSFS_DIR "/version", O_RDONLY | O_CLOEXEC)) == -1)
|
2020-02-13 00:00:19 +03:00
|
|
|
return (-1);
|
|
|
|
|
|
|
|
if ((rlen = read(fd, version, len)) == -1) {
|
|
|
|
version[0] = '\0';
|
|
|
|
_errno = errno;
|
|
|
|
(void) close(fd);
|
|
|
|
errno = _errno;
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
version[rlen-1] = '\0'; /* discard '\n' */
|
|
|
|
|
|
|
|
if (close(fd) == -1)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|