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:
parent
7c5eff9400
commit
73cdcc6323
|
@ -205,6 +205,52 @@ extern int zfs_parse_options(char *, zfs_share_proto_t);
|
|||
extern int zfs_unshare_proto(zfs_handle_t *,
|
||||
const char *, zfs_share_proto_t *);
|
||||
|
||||
typedef struct {
|
||||
zfs_prop_t p_prop;
|
||||
char *p_name;
|
||||
int p_share_err;
|
||||
int p_unshare_err;
|
||||
} proto_table_t;
|
||||
|
||||
typedef struct differ_info {
|
||||
zfs_handle_t *zhp;
|
||||
char *fromsnap;
|
||||
char *frommnt;
|
||||
char *tosnap;
|
||||
char *tomnt;
|
||||
char *ds;
|
||||
char *dsmnt;
|
||||
char *tmpsnap;
|
||||
char errbuf[1024];
|
||||
boolean_t isclone;
|
||||
boolean_t scripted;
|
||||
boolean_t classify;
|
||||
boolean_t timestamped;
|
||||
uint64_t shares;
|
||||
int zerr;
|
||||
int cleanupfd;
|
||||
int outputfd;
|
||||
int datafd;
|
||||
} differ_info_t;
|
||||
|
||||
extern proto_table_t proto_table[PROTO_END];
|
||||
|
||||
extern int do_mount(const char *src, const char *mntpt, char *opts, int flags);
|
||||
extern int do_unmount(const char *mntpt, int flags);
|
||||
extern int zfs_can_user_mount(void);
|
||||
extern int zfs_share_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto);
|
||||
extern int unshare_one(libzfs_handle_t *hdl, const char *name,
|
||||
const char *mountpoint, zfs_share_proto_t proto);
|
||||
extern boolean_t zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
|
||||
zprop_source_t *source, int flags);
|
||||
extern zfs_share_type_t is_shared_impl(libzfs_handle_t *hdl,
|
||||
const char *mountpoint, zfs_share_proto_t proto);
|
||||
extern int libzfs_load_module(void);
|
||||
extern int zpool_relabel_disk(libzfs_handle_t *hdl, const char *path,
|
||||
const char *msg);
|
||||
extern int find_shares_object(differ_info_t *di);
|
||||
extern void libzfs_set_pipe_max(int infd);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -27,6 +27,15 @@ USER_C = \
|
|||
libzfs_status.c \
|
||||
libzfs_util.c
|
||||
|
||||
if BUILD_LINUX
|
||||
USER_C += \
|
||||
os/linux/libzfs_mount_os.c \
|
||||
os/linux/libzfs_pool_os.c \
|
||||
os/linux/libzfs_sendrecv_os.c \
|
||||
os/linux/libzfs_util_os.c
|
||||
endif
|
||||
|
||||
|
||||
KERNEL_C = \
|
||||
algs/sha2/sha2.c \
|
||||
zfeature_common.c \
|
||||
|
@ -51,14 +60,19 @@ nodist_libzfs_la_SOURCES = \
|
|||
|
||||
libzfs_la_LIBADD = \
|
||||
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
||||
$(top_builddir)/lib/libshare/libshare.la \
|
||||
$(top_builddir)/lib/libuutil/libuutil.la \
|
||||
$(top_builddir)/lib/libzfs_core/libzfs_core.la \
|
||||
$(top_builddir)/lib/libzutil/libzutil.la
|
||||
|
||||
libzfs_la_LIBADD += -lm $(LIBSSL)
|
||||
if BUILD_LINUX
|
||||
libzfs_la_LIBADD += \
|
||||
$(top_builddir)/lib/libshare/libshare.la
|
||||
endif
|
||||
|
||||
libzfs_la_LDFLAGS = -version-info 2:0:0
|
||||
|
||||
libzfs_la_LIBADD += -lm $(LIBSSL)
|
||||
|
||||
EXTRA_DIST = $(libzfs_pc_DATA) $(USER_C)
|
||||
|
||||
# Licensing data
|
||||
|
|
|
@ -48,7 +48,6 @@
|
|||
#include "libzfs_impl.h"
|
||||
|
||||
#define ZDIFF_SNAPDIR "/.zfs/snapshot/"
|
||||
#define ZDIFF_SHARESDIR "/.zfs/shares/"
|
||||
#define ZDIFF_PREFIX "zfs-diff-%d"
|
||||
|
||||
#define ZDIFF_ADDED '+'
|
||||
|
@ -56,26 +55,6 @@
|
|||
#define ZDIFF_REMOVED '-'
|
||||
#define ZDIFF_RENAMED 'R'
|
||||
|
||||
typedef struct differ_info {
|
||||
zfs_handle_t *zhp;
|
||||
char *fromsnap;
|
||||
char *frommnt;
|
||||
char *tosnap;
|
||||
char *tomnt;
|
||||
char *ds;
|
||||
char *dsmnt;
|
||||
char *tmpsnap;
|
||||
char errbuf[1024];
|
||||
boolean_t isclone;
|
||||
boolean_t scripted;
|
||||
boolean_t classify;
|
||||
boolean_t timestamped;
|
||||
uint64_t shares;
|
||||
int zerr;
|
||||
int cleanupfd;
|
||||
int outputfd;
|
||||
int datafd;
|
||||
} differ_info_t;
|
||||
|
||||
/*
|
||||
* Given a {dsname, object id}, get the object path
|
||||
|
@ -487,25 +466,6 @@ differ(void *arg)
|
|||
return ((void *)0);
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static int
|
||||
make_temp_snapshot(differ_info_t *di)
|
||||
{
|
||||
|
@ -737,7 +697,7 @@ setup_differ_info(zfs_handle_t *zhp, const char *fromsnap,
|
|||
{
|
||||
di->zhp = zhp;
|
||||
|
||||
di->cleanupfd = open(ZFS_DEV, O_RDWR);
|
||||
di->cleanupfd = open(ZFS_DEV, O_RDWR|O_EXCL);
|
||||
VERIFY(di->cleanupfd >= 0);
|
||||
|
||||
if (get_snapshot_names(di, fromsnap, tosnap) != 0)
|
||||
|
|
|
@ -94,7 +94,6 @@
|
|||
static int mount_tp_nthr = 512; /* tpool threads for multi-threaded mounting */
|
||||
|
||||
static void zfs_mount_task(void *);
|
||||
static int zfs_share_proto(zfs_handle_t *, zfs_share_proto_t *);
|
||||
zfs_share_type_t zfs_is_shared_proto(zfs_handle_t *, char **,
|
||||
zfs_share_proto_t);
|
||||
|
||||
|
@ -102,13 +101,6 @@ zfs_share_type_t zfs_is_shared_proto(zfs_handle_t *, char **,
|
|||
* The share protocols table must be in the same order as the zfs_share_proto_t
|
||||
* enum in libzfs_impl.h
|
||||
*/
|
||||
typedef struct {
|
||||
zfs_prop_t p_prop;
|
||||
char *p_name;
|
||||
int p_share_err;
|
||||
int p_unshare_err;
|
||||
} proto_table_t;
|
||||
|
||||
proto_table_t proto_table[PROTO_END] = {
|
||||
{ZFS_PROP_SHARENFS, "nfs", EZFS_SHARENFSFAILED, EZFS_UNSHARENFSFAILED},
|
||||
{ZFS_PROP_SHARESMB, "smb", EZFS_SHARESMBFAILED, EZFS_UNSHARESMBFAILED},
|
||||
|
@ -129,60 +121,7 @@ zfs_share_proto_t share_all_proto[] = {
|
|||
PROTO_END
|
||||
};
|
||||
|
||||
/*
|
||||
* Search the sharetab for the given mountpoint and protocol, returning
|
||||
* a zfs_share_type_t value.
|
||||
*/
|
||||
static zfs_share_type_t
|
||||
is_shared(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);
|
||||
}
|
||||
|
||||
static boolean_t
|
||||
dir_is_empty_stat(const char *dirname)
|
||||
|
@ -304,7 +243,7 @@ zfs_is_mounted(zfs_handle_t *zhp, char **where)
|
|||
* Returns true if the given dataset is mountable, false otherwise. Returns the
|
||||
* mountpoint in 'buf'.
|
||||
*/
|
||||
static boolean_t
|
||||
boolean_t
|
||||
zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
|
||||
zprop_source_t *source, int flags)
|
||||
{
|
||||
|
@ -329,10 +268,6 @@ zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
|
|||
getzoneid() == GLOBAL_ZONEID)
|
||||
return (B_FALSE);
|
||||
|
||||
if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) &&
|
||||
getzoneid() == GLOBAL_ZONEID)
|
||||
return (B_FALSE);
|
||||
|
||||
if (zfs_prop_get_int(zhp, ZFS_PROP_REDACTED) && !(flags & MS_FORCE))
|
||||
return (B_FALSE);
|
||||
|
||||
|
@ -359,68 +294,6 @@ zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
|
|||
* http://www.kernel.org/pub/linux/utils/util-linux/libmount-docs/index.html
|
||||
*/
|
||||
|
||||
static int
|
||||
do_mount(const char *src, const char *mntpt, char *opts)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_add_option(zfs_handle_t *zhp, char *options, int len,
|
||||
zfs_prop_t prop, char *on, char *off)
|
||||
|
@ -503,9 +376,8 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
|
|||
(void) strlcat(mntopts, "," MNTOPT_RO, sizeof (mntopts));
|
||||
|
||||
if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL,
|
||||
flags)) {
|
||||
flags))
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Append default mount options which apply to the mount point.
|
||||
|
@ -599,7 +471,7 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
|
|||
}
|
||||
|
||||
/* perform the mount */
|
||||
rc = do_mount(zfs_get_name(zhp), mountpoint, mntopts);
|
||||
rc = do_mount(zfs_get_name(zhp), mountpoint, mntopts, flags);
|
||||
if (rc) {
|
||||
/*
|
||||
* Generic errors are nasty, but there are just way too many
|
||||
|
@ -793,7 +665,7 @@ zfs_is_shared_proto(zfs_handle_t *zhp, char **where, zfs_share_proto_t proto)
|
|||
if (!zfs_is_mounted(zhp, &mountpoint))
|
||||
return (SHARED_NOT_SHARED);
|
||||
|
||||
if ((rc = is_shared(zhp->zfs_hdl, mountpoint, proto))
|
||||
if ((rc = is_shared_impl(zhp->zfs_hdl, mountpoint, proto))
|
||||
!= SHARED_NOT_SHARED) {
|
||||
if (where != NULL)
|
||||
*where = mountpoint;
|
||||
|
@ -820,44 +692,6 @@ zfs_is_shared_smb(zfs_handle_t *zhp, char **where)
|
|||
PROTO_SMB) != SHARED_NOT_SHARED);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* zfs_uninit_libshare(zhandle)
|
||||
*
|
||||
|
@ -886,102 +720,6 @@ zfs_parse_options(char *options, zfs_share_proto_t proto)
|
|||
proto_table[proto].p_name));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static 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 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) {
|
||||
int err;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
zfs_share_nfs(zfs_handle_t *zhp)
|
||||
{
|
||||
|
@ -1000,50 +738,6 @@ zfs_shareall(zfs_handle_t *zhp)
|
|||
return (zfs_share_proto(zhp, share_all_proto));
|
||||
}
|
||||
|
||||
/*
|
||||
* Unshare a filesystem by mountpoint.
|
||||
*/
|
||||
static 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unshare the given filesystem.
|
||||
*/
|
||||
|
@ -1069,7 +763,7 @@ zfs_unshare_proto(zfs_handle_t *zhp, const char *mountpoint,
|
|||
for (curr_proto = proto; *curr_proto != PROTO_END;
|
||||
curr_proto++) {
|
||||
|
||||
if (is_shared(hdl, mntpt, *curr_proto) &&
|
||||
if (is_shared_impl(hdl, mntpt, *curr_proto) &&
|
||||
unshare_one(hdl, zhp->zfs_name,
|
||||
mntpt, *curr_proto) != 0) {
|
||||
if (mntpt != NULL)
|
||||
|
@ -1170,7 +864,8 @@ remove_mountpoint(zfs_handle_t *zhp)
|
|||
char mountpoint[ZFS_MAXPROPLEN];
|
||||
zprop_source_t source;
|
||||
|
||||
if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), &source, 0))
|
||||
if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint),
|
||||
&source, 0))
|
||||
return;
|
||||
|
||||
if (source == ZPROP_SRC_DEFAULT ||
|
||||
|
@ -1738,7 +1433,7 @@ zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force)
|
|||
zfs_share_proto_t *curr_proto;
|
||||
for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
|
||||
curr_proto++) {
|
||||
if (is_shared(hdl, mountpoints[i], *curr_proto) &&
|
||||
if (is_shared_impl(hdl, mountpoints[i], *curr_proto) &&
|
||||
unshare_one(hdl, mountpoints[i],
|
||||
mountpoints[i], *curr_proto) != 0)
|
||||
goto out;
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
#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>
|
||||
|
@ -53,7 +52,6 @@
|
|||
#include "zfs_comutil.h"
|
||||
#include "zfeature_common.h"
|
||||
|
||||
static int read_efi_label(nvlist_t *config, diskaddr_t *sb);
|
||||
static boolean_t zpool_vdev_is_interior(const char *name);
|
||||
|
||||
typedef struct prop_flags {
|
||||
|
@ -2814,45 +2812,6 @@ zpool_get_physpath(zpool_handle_t *zhp, char *physpath, size_t phypath_size)
|
|||
phypath_size));
|
||||
}
|
||||
|
||||
/*
|
||||
* If the device has being dynamically expanded then we need to relabel
|
||||
* the disk to use the new unallocated space.
|
||||
*/
|
||||
static 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a vdev path to a GUID. Returns GUID or 0 on error.
|
||||
*
|
||||
|
@ -4432,260 +4391,6 @@ zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj,
|
|||
free(mntpnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait while the specified activity is in progress in the pool.
|
||||
*/
|
||||
|
|
|
@ -2479,7 +2479,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
|
|||
++holdseq;
|
||||
(void) snprintf(sdd.holdtag, sizeof (sdd.holdtag),
|
||||
".send-%d-%llu", getpid(), (u_longlong_t)holdseq);
|
||||
sdd.cleanup_fd = open(ZFS_DEV, O_RDWR);
|
||||
sdd.cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
|
||||
if (sdd.cleanup_fd < 0) {
|
||||
err = errno;
|
||||
goto stderr_out;
|
||||
|
@ -5390,37 +5390,12 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props,
|
|||
return (-2);
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
#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 */
|
||||
|
||||
/*
|
||||
* It is not uncommon for gigabytes to be processed in zfs receive.
|
||||
* Speculatively increase the buffer size via Linux-specific fcntl()
|
||||
* call.
|
||||
* Speculatively increase the buffer size if supported by the platform.
|
||||
*/
|
||||
if (S_ISFIFO(sb.st_mode)) {
|
||||
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)
|
||||
(void) fcntl(infd, F_SETPIPE_SZ,
|
||||
max_psize);
|
||||
}
|
||||
fclose(procf);
|
||||
}
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
if (S_ISFIFO(sb.st_mode))
|
||||
libzfs_set_pipe_max(infd);
|
||||
|
||||
if (props) {
|
||||
err = nvlist_lookup_string(props, "origin", &originsnap);
|
||||
|
@ -5428,7 +5403,7 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props,
|
|||
return (err);
|
||||
}
|
||||
|
||||
cleanup_fd = open(ZFS_DEV, O_RDWR);
|
||||
cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
|
||||
VERIFY(cleanup_fd >= 0);
|
||||
|
||||
err = zfs_receive_impl(hdl, tosnap, originsnap, flags, infd, NULL, NULL,
|
||||
|
|
|
@ -56,37 +56,13 @@
|
|||
#include <libzutil.h>
|
||||
#include <sys/zfs_sysfs.h>
|
||||
|
||||
|
||||
int
|
||||
libzfs_errno(libzfs_handle_t *hdl)
|
||||
{
|
||||
return (hdl->libzfs_error);
|
||||
}
|
||||
|
||||
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."));
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
libzfs_error_action(libzfs_handle_t *hdl)
|
||||
{
|
||||
|
@ -712,19 +688,6 @@ libzfs_print_on_error(libzfs_handle_t *hdl, boolean_t printerr)
|
|||
hdl->libzfs_printerr = printerr;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read lines from an open file descriptor and store them in an array of
|
||||
* strings until EOF. lines[] will be allocated and populated with all the
|
||||
|
@ -903,84 +866,13 @@ libzfs_envvar_is_set(char *envvar)
|
|||
return (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(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);
|
||||
}
|
||||
|
||||
libzfs_handle_t *
|
||||
libzfs_init(void)
|
||||
{
|
||||
libzfs_handle_t *hdl;
|
||||
int error;
|
||||
|
||||
error = libzfs_load_module(ZFS_DRIVER);
|
||||
error = libzfs_load_module();
|
||||
if (error) {
|
||||
errno = error;
|
||||
return (NULL);
|
||||
|
@ -1215,12 +1107,6 @@ zcmd_read_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t **nvlp)
|
|||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
|
||||
{
|
||||
return (ioctl(hdl->libzfs_fd, request, zc));
|
||||
}
|
||||
|
||||
/*
|
||||
* ================================================================
|
||||
* API shared by zfs and zpool property management
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue