mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-23 10:54:35 +03:00
OpenZFS restructuring - libzfs
Factor Linux specific functionality out of libzfs. Reviewed-by: Allan Jude <allanjude@freebsd.org> Reviewed-by: Jorgen Lundman <lundman@lundman.net> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Matthew Macy <mmacy@FreeBSD.org> Closes #9377
This commit is contained in:
committed by
Brian Behlendorf
parent
7c5eff9400
commit
73cdcc6323
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2019 by Delphix. All rights reserved.
|
||||
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
|
||||
* Copyright 2017 RackTop Systems.
|
||||
* Copyright (c) 2018 Datto Inc.
|
||||
* Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <libintl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <zone.h>
|
||||
#include <sys/mntent.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/vfs.h>
|
||||
#include <sys/dsl_crypt.h>
|
||||
#include <libzfs.h>
|
||||
|
||||
#include "libzfs_impl.h"
|
||||
#include <thread_pool.h>
|
||||
|
||||
/*
|
||||
* zfs_init_libshare(zhandle, service)
|
||||
*
|
||||
* Initialize the libshare API if it hasn't already been initialized.
|
||||
* In all cases it returns 0 if it succeeded and an error if not. The
|
||||
* service value is which part(s) of the API to initialize and is a
|
||||
* direct map to the libshare sa_init(service) interface.
|
||||
*/
|
||||
int
|
||||
zfs_init_libshare(libzfs_handle_t *zhandle, int service)
|
||||
{
|
||||
int ret = SA_OK;
|
||||
|
||||
if (ret == SA_OK && zhandle->libzfs_shareflags & ZFSSHARE_MISS) {
|
||||
/*
|
||||
* We had a cache miss. Most likely it is a new ZFS
|
||||
* dataset that was just created. We want to make sure
|
||||
* so check timestamps to see if a different process
|
||||
* has updated any of the configuration. If there was
|
||||
* some non-ZFS change, we need to re-initialize the
|
||||
* internal cache.
|
||||
*/
|
||||
zhandle->libzfs_shareflags &= ~ZFSSHARE_MISS;
|
||||
if (sa_needs_refresh(zhandle->libzfs_sharehdl)) {
|
||||
zfs_uninit_libshare(zhandle);
|
||||
zhandle->libzfs_sharehdl = sa_init(service);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == SA_OK && zhandle && zhandle->libzfs_sharehdl == NULL)
|
||||
zhandle->libzfs_sharehdl = sa_init(service);
|
||||
|
||||
if (ret == SA_OK && zhandle->libzfs_sharehdl == NULL)
|
||||
ret = SA_NO_MEMORY;
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Share the given filesystem according to the options in the specified
|
||||
* protocol specific properties (sharenfs, sharesmb). We rely
|
||||
* on "libshare" to do the dirty work for us.
|
||||
*/
|
||||
int
|
||||
zfs_share_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto)
|
||||
{
|
||||
char mountpoint[ZFS_MAXPROPLEN];
|
||||
char shareopts[ZFS_MAXPROPLEN];
|
||||
char sourcestr[ZFS_MAXPROPLEN];
|
||||
libzfs_handle_t *hdl = zhp->zfs_hdl;
|
||||
sa_share_t share;
|
||||
zfs_share_proto_t *curr_proto;
|
||||
zprop_source_t sourcetype;
|
||||
int err, ret;
|
||||
|
||||
if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL, 0))
|
||||
return (0);
|
||||
|
||||
for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) {
|
||||
/*
|
||||
* Return success if there are no share options.
|
||||
*/
|
||||
if (zfs_prop_get(zhp, proto_table[*curr_proto].p_prop,
|
||||
shareopts, sizeof (shareopts), &sourcetype, sourcestr,
|
||||
ZFS_MAXPROPLEN, B_FALSE) != 0 ||
|
||||
strcmp(shareopts, "off") == 0)
|
||||
continue;
|
||||
|
||||
ret = zfs_init_libshare(hdl, SA_INIT_SHARE_API);
|
||||
if (ret != SA_OK) {
|
||||
(void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
|
||||
dgettext(TEXT_DOMAIN, "cannot share '%s': %s"),
|
||||
zfs_get_name(zhp), sa_errorstr(ret));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the 'zoned' property is set, then zfs_is_mountable()
|
||||
* will have already bailed out if we are in the global zone.
|
||||
* But local zones cannot be NFS servers, so we ignore it for
|
||||
* local zones as well.
|
||||
*/
|
||||
if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED))
|
||||
continue;
|
||||
|
||||
share = sa_find_share(hdl->libzfs_sharehdl, mountpoint);
|
||||
if (share == NULL) {
|
||||
/*
|
||||
* This may be a new file system that was just
|
||||
* created so isn't in the internal cache
|
||||
* (second time through). Rather than
|
||||
* reloading the entire configuration, we can
|
||||
* assume ZFS has done the checking and it is
|
||||
* safe to add this to the internal
|
||||
* configuration.
|
||||
*/
|
||||
if (sa_zfs_process_share(hdl->libzfs_sharehdl,
|
||||
NULL, NULL, mountpoint,
|
||||
proto_table[*curr_proto].p_name, sourcetype,
|
||||
shareopts, sourcestr, zhp->zfs_name) != SA_OK) {
|
||||
(void) zfs_error_fmt(hdl,
|
||||
proto_table[*curr_proto].p_share_err,
|
||||
dgettext(TEXT_DOMAIN, "cannot share '%s'"),
|
||||
zfs_get_name(zhp));
|
||||
return (-1);
|
||||
}
|
||||
hdl->libzfs_shareflags |= ZFSSHARE_MISS;
|
||||
share = sa_find_share(hdl->libzfs_sharehdl,
|
||||
mountpoint);
|
||||
}
|
||||
if (share != NULL) {
|
||||
err = sa_enable_share(share,
|
||||
proto_table[*curr_proto].p_name);
|
||||
if (err != SA_OK) {
|
||||
(void) zfs_error_fmt(hdl,
|
||||
proto_table[*curr_proto].p_share_err,
|
||||
dgettext(TEXT_DOMAIN, "cannot share '%s'"),
|
||||
zfs_get_name(zhp));
|
||||
return (-1);
|
||||
}
|
||||
} else {
|
||||
(void) zfs_error_fmt(hdl,
|
||||
proto_table[*curr_proto].p_share_err,
|
||||
dgettext(TEXT_DOMAIN, "cannot share '%s'"),
|
||||
zfs_get_name(zhp));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unshare a filesystem by mountpoint.
|
||||
*/
|
||||
int
|
||||
unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint,
|
||||
zfs_share_proto_t proto)
|
||||
{
|
||||
sa_share_t share;
|
||||
int err;
|
||||
char *mntpt;
|
||||
/*
|
||||
* Mountpoint could get trashed if libshare calls getmntany
|
||||
* which it does during API initialization, so strdup the
|
||||
* value.
|
||||
*/
|
||||
mntpt = zfs_strdup(hdl, mountpoint);
|
||||
|
||||
/* make sure libshare initialized */
|
||||
if ((err = zfs_init_libshare(hdl, SA_INIT_SHARE_API)) != SA_OK) {
|
||||
free(mntpt); /* don't need the copy anymore */
|
||||
return (zfs_error_fmt(hdl, proto_table[proto].p_unshare_err,
|
||||
dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"),
|
||||
name, sa_errorstr(err)));
|
||||
}
|
||||
|
||||
share = sa_find_share(hdl->libzfs_sharehdl, mntpt);
|
||||
free(mntpt); /* don't need the copy anymore */
|
||||
|
||||
if (share != NULL) {
|
||||
err = sa_disable_share(share, proto_table[proto].p_name);
|
||||
if (err != SA_OK) {
|
||||
return (zfs_error_fmt(hdl,
|
||||
proto_table[proto].p_unshare_err,
|
||||
dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"),
|
||||
name, sa_errorstr(err)));
|
||||
}
|
||||
} else {
|
||||
return (zfs_error_fmt(hdl, proto_table[proto].p_unshare_err,
|
||||
dgettext(TEXT_DOMAIN, "cannot unshare '%s': not found"),
|
||||
name));
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Search the sharetab for the given mountpoint and protocol, returning
|
||||
* a zfs_share_type_t value.
|
||||
*/
|
||||
zfs_share_type_t
|
||||
is_shared_impl(libzfs_handle_t *hdl, const char *mountpoint,
|
||||
zfs_share_proto_t proto)
|
||||
{
|
||||
char buf[MAXPATHLEN], *tab;
|
||||
char *ptr;
|
||||
|
||||
if (hdl->libzfs_sharetab == NULL)
|
||||
return (SHARED_NOT_SHARED);
|
||||
|
||||
/* Reopen ZFS_SHARETAB to prevent reading stale data from open file */
|
||||
if (freopen(ZFS_SHARETAB, "r", hdl->libzfs_sharetab) == NULL)
|
||||
return (SHARED_NOT_SHARED);
|
||||
|
||||
(void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET);
|
||||
|
||||
while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) {
|
||||
|
||||
/* the mountpoint is the first entry on each line */
|
||||
if ((tab = strchr(buf, '\t')) == NULL)
|
||||
continue;
|
||||
|
||||
*tab = '\0';
|
||||
if (strcmp(buf, mountpoint) == 0) {
|
||||
/*
|
||||
* the protocol field is the third field
|
||||
* skip over second field
|
||||
*/
|
||||
ptr = ++tab;
|
||||
if ((tab = strchr(ptr, '\t')) == NULL)
|
||||
continue;
|
||||
ptr = ++tab;
|
||||
if ((tab = strchr(ptr, '\t')) == NULL)
|
||||
continue;
|
||||
*tab = '\0';
|
||||
if (strcmp(ptr,
|
||||
proto_table[proto].p_name) == 0) {
|
||||
switch (proto) {
|
||||
case PROTO_NFS:
|
||||
return (SHARED_NFS);
|
||||
case PROTO_SMB:
|
||||
return (SHARED_SMB);
|
||||
default:
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (SHARED_NOT_SHARED);
|
||||
}
|
||||
|
||||
/*
|
||||
* The filesystem is mounted by invoking the system mount utility rather
|
||||
* than by the system call mount(2). This ensures that the /etc/mtab
|
||||
* file is correctly locked for the update. Performing our own locking
|
||||
* and /etc/mtab update requires making an unsafe assumption about how
|
||||
* the mount utility performs its locking. Unfortunately, this also means
|
||||
* in the case of a mount failure we do not have the exact errno. We must
|
||||
* make due with return value from the mount process.
|
||||
*
|
||||
* In the long term a shared library called libmount is under development
|
||||
* which provides a common API to address the locking and errno issues.
|
||||
* Once the standard mount utility has been updated to use this library
|
||||
* we can add an autoconf check to conditionally use it.
|
||||
*
|
||||
* http://www.kernel.org/pub/linux/utils/util-linux/libmount-docs/index.html
|
||||
*/
|
||||
int
|
||||
do_mount(const char *src, const char *mntpt, char *opts, int flags)
|
||||
{
|
||||
char *argv[9] = {
|
||||
"/bin/mount",
|
||||
"--no-canonicalize",
|
||||
"-t", MNTTYPE_ZFS,
|
||||
"-o", opts,
|
||||
(char *)src,
|
||||
(char *)mntpt,
|
||||
(char *)NULL };
|
||||
int rc;
|
||||
|
||||
/* Return only the most critical mount error */
|
||||
rc = libzfs_run_process(argv[0], argv, STDOUT_VERBOSE|STDERR_VERBOSE);
|
||||
if (rc) {
|
||||
if (rc & MOUNT_FILEIO)
|
||||
return (EIO);
|
||||
if (rc & MOUNT_USER)
|
||||
return (EINTR);
|
||||
if (rc & MOUNT_SOFTWARE)
|
||||
return (EPIPE);
|
||||
if (rc & MOUNT_BUSY)
|
||||
return (EBUSY);
|
||||
if (rc & MOUNT_SYSERR)
|
||||
return (EAGAIN);
|
||||
if (rc & MOUNT_USAGE)
|
||||
return (EINVAL);
|
||||
|
||||
return (ENXIO); /* Generic error */
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
do_unmount(const char *mntpt, int flags)
|
||||
{
|
||||
char force_opt[] = "-f";
|
||||
char lazy_opt[] = "-l";
|
||||
char *argv[7] = {
|
||||
"/bin/umount",
|
||||
"-t", MNTTYPE_ZFS,
|
||||
NULL, NULL, NULL, NULL };
|
||||
int rc, count = 3;
|
||||
|
||||
if (flags & MS_FORCE) {
|
||||
argv[count] = force_opt;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (flags & MS_DETACH) {
|
||||
argv[count] = lazy_opt;
|
||||
count++;
|
||||
}
|
||||
|
||||
argv[count] = (char *)mntpt;
|
||||
rc = libzfs_run_process(argv[0], argv, STDOUT_VERBOSE|STDERR_VERBOSE);
|
||||
|
||||
return (rc ? EINVAL : 0);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_can_user_mount(void)
|
||||
{
|
||||
return (geteuid() == 0);
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2018 by Delphix. All rights reserved.
|
||||
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
|
||||
* Copyright (c) 2018 Datto Inc.
|
||||
* Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
|
||||
* Copyright (c) 2017, Intel Corporation.
|
||||
* Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <libintl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <zone.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/efi_partition.h>
|
||||
#include <sys/systeminfo.h>
|
||||
#include <sys/vtoc.h>
|
||||
#include <sys/zfs_ioctl.h>
|
||||
#include <sys/vdev_disk.h>
|
||||
#include <dlfcn.h>
|
||||
#include <libzutil.h>
|
||||
|
||||
#include "zfs_namecheck.h"
|
||||
#include "zfs_prop.h"
|
||||
#include "libzfs_impl.h"
|
||||
#include "zfs_comutil.h"
|
||||
#include "zfeature_common.h"
|
||||
|
||||
/*
|
||||
* If the device has being dynamically expanded then we need to relabel
|
||||
* the disk to use the new unallocated space.
|
||||
*/
|
||||
int
|
||||
zpool_relabel_disk(libzfs_handle_t *hdl, const char *path, const char *msg)
|
||||
{
|
||||
int fd, error;
|
||||
|
||||
if ((fd = open(path, O_RDWR|O_DIRECT)) < 0) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
|
||||
"relabel '%s': unable to open device: %d"), path, errno);
|
||||
return (zfs_error(hdl, EZFS_OPENFAILED, msg));
|
||||
}
|
||||
|
||||
/*
|
||||
* It's possible that we might encounter an error if the device
|
||||
* does not have any unallocated space left. If so, we simply
|
||||
* ignore that error and continue on.
|
||||
*
|
||||
* Also, we don't call efi_rescan() - that would just return EBUSY.
|
||||
* The module will do it for us in vdev_disk_open().
|
||||
*/
|
||||
error = efi_use_whole_disk(fd);
|
||||
|
||||
/* Flush the buffers to disk and invalidate the page cache. */
|
||||
(void) fsync(fd);
|
||||
(void) ioctl(fd, BLKFLSBUF);
|
||||
|
||||
(void) close(fd);
|
||||
if (error && error != VT_ENOSPC) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
|
||||
"relabel '%s': unable to read disk capacity"), path);
|
||||
return (zfs_error(hdl, EZFS_NOCAP, msg));
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the EFI label from the config, if a label does not exist then
|
||||
* pass back the error to the caller. If the caller has passed a non-NULL
|
||||
* diskaddr argument then we set it to the starting address of the EFI
|
||||
* partition.
|
||||
*/
|
||||
static int
|
||||
read_efi_label(nvlist_t *config, diskaddr_t *sb)
|
||||
{
|
||||
char *path;
|
||||
int fd;
|
||||
char diskname[MAXPATHLEN];
|
||||
int err = -1;
|
||||
|
||||
if (nvlist_lookup_string(config, ZPOOL_CONFIG_PATH, &path) != 0)
|
||||
return (err);
|
||||
|
||||
(void) snprintf(diskname, sizeof (diskname), "%s%s", DISK_ROOT,
|
||||
strrchr(path, '/'));
|
||||
if ((fd = open(diskname, O_RDONLY|O_DIRECT)) >= 0) {
|
||||
struct dk_gpt *vtoc;
|
||||
|
||||
if ((err = efi_alloc_and_read(fd, &vtoc)) >= 0) {
|
||||
if (sb != NULL)
|
||||
*sb = vtoc->efi_parts[0].p_start;
|
||||
efi_free(vtoc);
|
||||
}
|
||||
(void) close(fd);
|
||||
}
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* determine where a partition starts on a disk in the current
|
||||
* configuration
|
||||
*/
|
||||
static diskaddr_t
|
||||
find_start_block(nvlist_t *config)
|
||||
{
|
||||
nvlist_t **child;
|
||||
uint_t c, children;
|
||||
diskaddr_t sb = MAXOFFSET_T;
|
||||
uint64_t wholedisk;
|
||||
|
||||
if (nvlist_lookup_nvlist_array(config,
|
||||
ZPOOL_CONFIG_CHILDREN, &child, &children) != 0) {
|
||||
if (nvlist_lookup_uint64(config,
|
||||
ZPOOL_CONFIG_WHOLE_DISK,
|
||||
&wholedisk) != 0 || !wholedisk) {
|
||||
return (MAXOFFSET_T);
|
||||
}
|
||||
if (read_efi_label(config, &sb) < 0)
|
||||
sb = MAXOFFSET_T;
|
||||
return (sb);
|
||||
}
|
||||
|
||||
for (c = 0; c < children; c++) {
|
||||
sb = find_start_block(child[c]);
|
||||
if (sb != MAXOFFSET_T) {
|
||||
return (sb);
|
||||
}
|
||||
}
|
||||
return (MAXOFFSET_T);
|
||||
}
|
||||
|
||||
static int
|
||||
zpool_label_disk_check(char *path)
|
||||
{
|
||||
struct dk_gpt *vtoc;
|
||||
int fd, err;
|
||||
|
||||
if ((fd = open(path, O_RDONLY|O_DIRECT)) < 0)
|
||||
return (errno);
|
||||
|
||||
if ((err = efi_alloc_and_read(fd, &vtoc)) != 0) {
|
||||
(void) close(fd);
|
||||
return (err);
|
||||
}
|
||||
|
||||
if (vtoc->efi_flags & EFI_GPT_PRIMARY_CORRUPT) {
|
||||
efi_free(vtoc);
|
||||
(void) close(fd);
|
||||
return (EIDRM);
|
||||
}
|
||||
|
||||
efi_free(vtoc);
|
||||
(void) close(fd);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a unique partition name for the ZFS member. Partitions must
|
||||
* have unique names to ensure udev will be able to create symlinks under
|
||||
* /dev/disk/by-partlabel/ for all pool members. The partition names are
|
||||
* of the form <pool>-<unique-id>.
|
||||
*/
|
||||
static void
|
||||
zpool_label_name(char *label_name, int label_size)
|
||||
{
|
||||
uint64_t id = 0;
|
||||
int fd;
|
||||
|
||||
fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
if (read(fd, &id, sizeof (id)) != sizeof (id))
|
||||
id = 0;
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
if (id == 0)
|
||||
id = (((uint64_t)rand()) << 32) | (uint64_t)rand();
|
||||
|
||||
snprintf(label_name, label_size, "zfs-%016llx", (u_longlong_t)id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Label an individual disk. The name provided is the short name,
|
||||
* stripped of any leading /dev path.
|
||||
*/
|
||||
int
|
||||
zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
|
||||
{
|
||||
char path[MAXPATHLEN];
|
||||
struct dk_gpt *vtoc;
|
||||
int rval, fd;
|
||||
size_t resv = EFI_MIN_RESV_SIZE;
|
||||
uint64_t slice_size;
|
||||
diskaddr_t start_block;
|
||||
char errbuf[1024];
|
||||
|
||||
/* prepare an error message just in case */
|
||||
(void) snprintf(errbuf, sizeof (errbuf),
|
||||
dgettext(TEXT_DOMAIN, "cannot label '%s'"), name);
|
||||
|
||||
if (zhp) {
|
||||
nvlist_t *nvroot;
|
||||
|
||||
verify(nvlist_lookup_nvlist(zhp->zpool_config,
|
||||
ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
|
||||
|
||||
if (zhp->zpool_start_block == 0)
|
||||
start_block = find_start_block(nvroot);
|
||||
else
|
||||
start_block = zhp->zpool_start_block;
|
||||
zhp->zpool_start_block = start_block;
|
||||
} else {
|
||||
/* new pool */
|
||||
start_block = NEW_START_BLOCK;
|
||||
}
|
||||
|
||||
(void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name);
|
||||
|
||||
if ((fd = open(path, O_RDWR|O_DIRECT|O_EXCL)) < 0) {
|
||||
/*
|
||||
* This shouldn't happen. We've long since verified that this
|
||||
* is a valid device.
|
||||
*/
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
|
||||
"label '%s': unable to open device: %d"), path, errno);
|
||||
return (zfs_error(hdl, EZFS_OPENFAILED, errbuf));
|
||||
}
|
||||
|
||||
if (efi_alloc_and_init(fd, EFI_NUMPAR, &vtoc) != 0) {
|
||||
/*
|
||||
* The only way this can fail is if we run out of memory, or we
|
||||
* were unable to read the disk's capacity
|
||||
*/
|
||||
if (errno == ENOMEM)
|
||||
(void) no_memory(hdl);
|
||||
|
||||
(void) close(fd);
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
|
||||
"label '%s': unable to read disk capacity"), path);
|
||||
|
||||
return (zfs_error(hdl, EZFS_NOCAP, errbuf));
|
||||
}
|
||||
|
||||
slice_size = vtoc->efi_last_u_lba + 1;
|
||||
slice_size -= EFI_MIN_RESV_SIZE;
|
||||
if (start_block == MAXOFFSET_T)
|
||||
start_block = NEW_START_BLOCK;
|
||||
slice_size -= start_block;
|
||||
slice_size = P2ALIGN(slice_size, PARTITION_END_ALIGNMENT);
|
||||
|
||||
vtoc->efi_parts[0].p_start = start_block;
|
||||
vtoc->efi_parts[0].p_size = slice_size;
|
||||
|
||||
/*
|
||||
* Why we use V_USR: V_BACKUP confuses users, and is considered
|
||||
* disposable by some EFI utilities (since EFI doesn't have a backup
|
||||
* slice). V_UNASSIGNED is supposed to be used only for zero size
|
||||
* partitions, and efi_write() will fail if we use it. V_ROOT, V_BOOT,
|
||||
* etc. were all pretty specific. V_USR is as close to reality as we
|
||||
* can get, in the absence of V_OTHER.
|
||||
*/
|
||||
vtoc->efi_parts[0].p_tag = V_USR;
|
||||
zpool_label_name(vtoc->efi_parts[0].p_name, EFI_PART_NAME_LEN);
|
||||
|
||||
vtoc->efi_parts[8].p_start = slice_size + start_block;
|
||||
vtoc->efi_parts[8].p_size = resv;
|
||||
vtoc->efi_parts[8].p_tag = V_RESERVED;
|
||||
|
||||
rval = efi_write(fd, vtoc);
|
||||
|
||||
/* Flush the buffers to disk and invalidate the page cache. */
|
||||
(void) fsync(fd);
|
||||
(void) ioctl(fd, BLKFLSBUF);
|
||||
|
||||
if (rval == 0)
|
||||
rval = efi_rescan(fd);
|
||||
|
||||
/*
|
||||
* Some block drivers (like pcata) may not support EFI GPT labels.
|
||||
* Print out a helpful error message directing the user to manually
|
||||
* label the disk and give a specific slice.
|
||||
*/
|
||||
if (rval != 0) {
|
||||
(void) close(fd);
|
||||
efi_free(vtoc);
|
||||
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "try using "
|
||||
"parted(8) and then provide a specific slice: %d"), rval);
|
||||
return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
|
||||
}
|
||||
|
||||
(void) close(fd);
|
||||
efi_free(vtoc);
|
||||
|
||||
(void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name);
|
||||
(void) zfs_append_partition(path, MAXPATHLEN);
|
||||
|
||||
/* Wait to udev to signal use the device has settled. */
|
||||
rval = zpool_label_disk_wait(path, DISK_LABEL_WAIT);
|
||||
if (rval) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "failed to "
|
||||
"detect device partitions on '%s': %d"), path, rval);
|
||||
return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
|
||||
}
|
||||
|
||||
/* We can't be to paranoid. Read the label back and verify it. */
|
||||
(void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name);
|
||||
rval = zpool_label_disk_check(path);
|
||||
if (rval) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "freshly written "
|
||||
"EFI label on '%s' is damaged. Ensure\nthis device "
|
||||
"is not in use, and is functioning properly: %d"),
|
||||
path, rval);
|
||||
return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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 <libzfs.h>
|
||||
|
||||
#include "libzfs_impl.h"
|
||||
|
||||
#ifndef F_SETPIPE_SZ
|
||||
#define F_SETPIPE_SZ (F_SETLEASE + 7)
|
||||
#endif /* F_SETPIPE_SZ */
|
||||
|
||||
#ifndef F_GETPIPE_SZ
|
||||
#define F_GETPIPE_SZ (F_GETLEASE + 7)
|
||||
#endif /* F_GETPIPE_SZ */
|
||||
|
||||
void
|
||||
libzfs_set_pipe_max(int infd)
|
||||
{
|
||||
FILE *procf = fopen("/proc/sys/fs/pipe-max-size", "r");
|
||||
|
||||
if (procf != NULL) {
|
||||
unsigned long max_psize;
|
||||
long cur_psize;
|
||||
if (fscanf(procf, "%lu", &max_psize) > 0) {
|
||||
cur_psize = fcntl(infd, F_GETPIPE_SZ);
|
||||
if (cur_psize > 0 &&
|
||||
max_psize > (unsigned long) cur_psize)
|
||||
fcntl(infd, F_SETPIPE_SZ,
|
||||
max_psize);
|
||||
}
|
||||
fclose(procf);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
#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 {
|
||||
fd = open(ZFS_DEV, O_RDWR);
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user