mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-29 12:29:35 +03:00
324 lines
7.1 KiB
C
324 lines
7.1 KiB
C
|
/*
|
||
|
* CDDL HEADER SART
|
||
|
*
|
||
|
* 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
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Copyright (c) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
|
||
|
*/
|
||
|
#include <os/freebsd/zfs/sys/zfs_ioctl_compat.h>
|
||
|
#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>
|
||
|
|
||
|
int zfs_ioctl_version = ZFS_IOCVER_UNDEF;
|
||
|
// static int zfs_spa_version = -1;
|
||
|
|
||
|
void
|
||
|
libzfs_set_pipe_max(int infd)
|
||
|
{
|
||
|
/* FreeBSD automatically resizes */
|
||
|
}
|
||
|
|
||
|
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 *cur, buf[MAXPATHLEN];
|
||
|
const char *p, *bp;
|
||
|
struct stat sb;
|
||
|
|
||
|
eacces = 0;
|
||
|
|
||
|
/* If it's an absolute or relative path name, it's easy. */
|
||
|
if (strchr(name, '/')) {
|
||
|
bp = name;
|
||
|
cur = 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);
|
||
|
}
|
||
|
|
||
|
cur = alloca(strlen(path) + 1);
|
||
|
if (cur == NULL) {
|
||
|
errno = ENOMEM;
|
||
|
return (-1);
|
||
|
}
|
||
|
strcpy(cur, path);
|
||
|
while ((p = strsep(&cur, ":")) != NULL) {
|
||
|
/*
|
||
|
* It's a SHELL path -- double, leading and trailing colons
|
||
|
* mean the current directory.
|
||
|
*/
|
||
|
if (*p == '\0') {
|
||
|
p = ".";
|
||
|
lp = 1;
|
||
|
} else
|
||
|
lp = strlen(p);
|
||
|
ln = strlen(name);
|
||
|
|
||
|
/*
|
||
|
* 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;
|
||
|
}
|
||
|
bcopy(p, buf, lp);
|
||
|
buf[lp] = '/';
|
||
|
bcopy(name, buf + lp + 1, 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)
|
||
|
;
|
||
|
memp = alloca((cnt + 2) * sizeof (char *));
|
||
|
if (memp == NULL) {
|
||
|
/* errno = ENOMEM; XXX override ENOEXEC? */
|
||
|
goto done;
|
||
|
}
|
||
|
memp[0] = "sh";
|
||
|
memp[1] = bp;
|
||
|
bcopy(argv + 1, memp + 2, cnt * sizeof (char *));
|
||
|
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));
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
/*
|
||
|
* Get the SPA version
|
||
|
*/
|
||
|
static int
|
||
|
get_zfs_spa_version(void)
|
||
|
{
|
||
|
size_t ver_size;
|
||
|
int ver = 0;
|
||
|
|
||
|
ver_size = sizeof (ver);
|
||
|
sysctlbyname("vfs.zfs.version.spa", &ver, &ver_size, NULL, 0);
|
||
|
|
||
|
return (ver);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Get zfs_ioctl_version
|
||
|
*/
|
||
|
int
|
||
|
get_zfs_ioctl_version(void)
|
||
|
{
|
||
|
size_t ver_size;
|
||
|
int ver = ZFS_IOCVER_NONE;
|
||
|
|
||
|
ver_size = sizeof (ver);
|
||
|
sysctlbyname("vfs.zfs.version.ioctl", &ver, &ver_size, NULL, 0);
|
||
|
|
||
|
return (ver);
|
||
|
}
|
||
|
|
||
|
const char *
|
||
|
libzfs_error_init(int error)
|
||
|
{
|
||
|
|
||
|
return (strerror(error));
|
||
|
}
|
||
|
|
||
|
int
|
||
|
zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
|
||
|
{
|
||
|
return (zfs_ioctl_fd(hdl->libzfs_fd, request, zc));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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
|
||
|
*/
|
||
|
int
|
||
|
libzfs_load_module(void)
|
||
|
{
|
||
|
/* XXX: modname is "zfs" but file is named "openzfs". */
|
||
|
if (modfind("zfs") < 0) {
|
||
|
/* Not present in kernel, try loading it. */
|
||
|
if (kldload("openzfs") < 0 && errno != EEXIST) {
|
||
|
return (errno);
|
||
|
}
|
||
|
}
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
zpool_relabel_disk(libzfs_handle_t *hdl, const char *path, const char *msg)
|
||
|
{
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
|
||
|
{
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
find_shares_object(differ_info_t *di)
|
||
|
{
|
||
|
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 } };
|
||
|
char errbuf[1024];
|
||
|
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_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 = ioctl(hdl->libzfs_fd, cmd, &zc)) != 0)
|
||
|
zfs_standard_error(hdl, errno, errbuf);
|
||
|
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Fill given version buffer with zfs kernel version.
|
||
|
* Returns 0 on success, and -1 on error (with errno set)
|
||
|
*/
|
||
|
int
|
||
|
zfs_version_kernel(char *version, int len)
|
||
|
{
|
||
|
size_t l = len;
|
||
|
|
||
|
return (sysctlbyname("vfs.zfs.version.module",
|
||
|
version, &l, NULL, 0));
|
||
|
}
|