From 73cdcc632304ed72aafabef1fb03c45a27dcc58f Mon Sep 17 00:00:00 2001 From: Matthew Macy Date: Thu, 3 Oct 2019 10:33:16 -0700 Subject: [PATCH] OpenZFS restructuring - libzfs Factor Linux specific functionality out of libzfs. Reviewed-by: Allan Jude Reviewed-by: Jorgen Lundman Reviewed-by: Brian Behlendorf Signed-off-by: Matthew Macy Closes #9377 --- include/libzfs_impl.h | 46 +++ lib/libzfs/Makefile.am | 18 +- lib/libzfs/libzfs_diff.c | 42 +-- lib/libzfs/libzfs_mount.c | 321 +------------------- lib/libzfs/libzfs_pool.c | 295 ------------------ lib/libzfs/libzfs_sendrecv.c | 35 +-- lib/libzfs/libzfs_util.c | 118 +------- lib/libzfs/os/linux/libzfs_mount_os.c | 367 +++++++++++++++++++++++ lib/libzfs/os/linux/libzfs_pool_os.c | 345 +++++++++++++++++++++ lib/libzfs/os/linux/libzfs_sendrecv_os.c | 52 ++++ lib/libzfs/os/linux/libzfs_util_os.c | 184 ++++++++++++ 11 files changed, 1026 insertions(+), 797 deletions(-) create mode 100644 lib/libzfs/os/linux/libzfs_mount_os.c create mode 100644 lib/libzfs/os/linux/libzfs_pool_os.c create mode 100644 lib/libzfs/os/linux/libzfs_sendrecv_os.c create mode 100644 lib/libzfs/os/linux/libzfs_util_os.c diff --git a/include/libzfs_impl.h b/include/libzfs_impl.h index 9a46b9f12960..e9fd4cf41fe7 100644 --- a/include/libzfs_impl.h +++ b/include/libzfs_impl.h @@ -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 diff --git a/lib/libzfs/Makefile.am b/lib/libzfs/Makefile.am index 3d14a77c1a8b..da42307bb6fb 100644 --- a/lib/libzfs/Makefile.am +++ b/lib/libzfs/Makefile.am @@ -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 diff --git a/lib/libzfs/libzfs_diff.c b/lib/libzfs/libzfs_diff.c index 1b5c44b047e2..169180751d55 100644 --- a/lib/libzfs/libzfs_diff.c +++ b/lib/libzfs/libzfs_diff.c @@ -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) diff --git a/lib/libzfs/libzfs_mount.c b/lib/libzfs/libzfs_mount.c index 5bd3c67bec5e..c2955ccfaba5 100644 --- a/lib/libzfs/libzfs_mount.c +++ b/lib/libzfs/libzfs_mount.c @@ -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; diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index 2641b11528a2..155f2c3adf88 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include #include @@ -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 -. - */ -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. */ diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c index 7026438828f4..0b6b3156829b 100644 --- a/lib/libzfs/libzfs_sendrecv.c +++ b/lib/libzfs/libzfs_sendrecv.c @@ -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, diff --git a/lib/libzfs/libzfs_util.c b/lib/libzfs/libzfs_util.c index 04100071d051..ae66db39a4b6 100644 --- a/lib/libzfs/libzfs_util.c +++ b/lib/libzfs/libzfs_util.c @@ -56,37 +56,13 @@ #include #include + 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 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 diff --git a/lib/libzfs/os/linux/libzfs_mount_os.c b/lib/libzfs/os/linux/libzfs_mount_os.c new file mode 100644 index 000000000000..af1cafd28a9e --- /dev/null +++ b/lib/libzfs/os/linux/libzfs_mount_os.c @@ -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 + * Copyright 2017 RackTop Systems. + * Copyright (c) 2018 Datto Inc. + * Copyright 2018 OmniOS Community Edition (OmniOSce) Association. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libzfs_impl.h" +#include + +/* + * 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); +} diff --git a/lib/libzfs/os/linux/libzfs_pool_os.c b/lib/libzfs/os/linux/libzfs_pool_os.c new file mode 100644 index 000000000000..e1250f12173c --- /dev/null +++ b/lib/libzfs/os/linux/libzfs_pool_os.c @@ -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 + * Copyright (c) 2018 Datto Inc. + * Copyright (c) 2017 Open-E, Inc. All Rights Reserved. + * Copyright (c) 2017, Intel Corporation. + * Copyright (c) 2018, loli10K + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 -. + */ +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); +} diff --git a/lib/libzfs/os/linux/libzfs_sendrecv_os.c b/lib/libzfs/os/linux/libzfs_sendrecv_os.c new file mode 100644 index 000000000000..eeb1f07f2dea --- /dev/null +++ b/lib/libzfs/os/linux/libzfs_sendrecv_os.c @@ -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 + +#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); + } +} diff --git a/lib/libzfs/os/linux/libzfs_util_os.c b/lib/libzfs/os/linux/libzfs_util_os.c new file mode 100644 index 000000000000..c27dc91bc6ea --- /dev/null +++ b/lib/libzfs/os/linux/libzfs_util_os.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libzfs_impl.h" +#include "zfs_prop.h" +#include + +#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 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); +}