X-Git-Url: https://git.camperquake.de/gitweb.cgi?a=blobdiff_plain;f=lib%2Flibzfs%2Flibzfs_mount.c;h=977b2ee31b3fa5461476ade9d04c49635767ec28;hb=9a616b5d17185c7fa5cd0d39ff8bc101cad8466d;hp=0675ec229790de630c9d21eae59d9361809ab16f;hpb=428870ff734fdaccc342b33fc53cf94724409a46;p=zfs.git diff --git a/lib/libzfs/libzfs_mount.c b/lib/libzfs/libzfs_mount.c index 0675ec2..977b2ee 100644 --- a/lib/libzfs/libzfs_mount.c +++ b/lib/libzfs/libzfs_mount.c @@ -72,6 +72,9 @@ #include #include #include +#ifdef HAVE_LIBSELINUX +#include +#endif /* HAVE_LIBSELINUX */ #include @@ -81,6 +84,7 @@ #include #define MAXISALEN 257 /* based on sysinfo(2) man page */ +#ifdef HAVE_ZPL 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); @@ -117,20 +121,21 @@ zfs_share_proto_t share_all_proto[] = { }; /* - * Search the sharetab for the given mountpoint and protocol, returning + * Search for NFS and SMB exports 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); (void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET); + /* Search /etc/exports for NFS exports */ + /* FIXME: Assumes the file is tab delimited. */ while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) { /* the mountpoint is the first entry on each line */ @@ -139,31 +144,15 @@ is_shared(libzfs_handle_t *hdl, const char *mountpoint, zfs_share_proto_t proto) *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); - } - } + if (proto == PROTO_NFS) + return (SHARED_NFS); + else + return (SHARED_NOT_SHARED); } } + /* XXX: Search /etc/samba/smb.conf for SMB exports, return SHARED_SMB */ + return (SHARED_NOT_SHARED); } @@ -255,6 +244,82 @@ zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen, } /* + * 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 + */ + +static int +do_mount(const char *src, const char *mntpt, char *opts) +{ + char *argv[8] = { + "/bin/mount", + "-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); + if (rc) { + if (rc & MOUNT_FILEIO) + return EIO; + if (rc & MOUNT_USER) + return EINTR; + if (rc & MOUNT_SOFTWARE) + return EPIPE; + 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); + + return (rc ? EINVAL : 0); +} + +/* * Mount the given filesystem. */ int @@ -264,12 +329,30 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags) char mountpoint[ZFS_MAXPROPLEN]; char mntopts[MNT_LINE_MAX]; libzfs_handle_t *hdl = zhp->zfs_hdl; + int rc; if (options == NULL) - mntopts[0] = '\0'; + (void) strlcpy(mntopts, MNTOPT_DEFAULTS, sizeof (mntopts)); else (void) strlcpy(mntopts, options, sizeof (mntopts)); + /* + * If the pool is imported read-only then all mounts must be read-only + */ + if (zpool_get_prop_int(zhp->zpool_hdl, ZPOOL_PROP_READONLY, NULL)) + (void) strlcat(mntopts, "," MNTOPT_RO, sizeof (mntopts)); + + /* + * Append zfsutil option so the mount helper allow the mount + */ + strlcat(mntopts, "," MNTOPT_ZFSUTIL, sizeof (mntopts)); + +#ifdef HAVE_LIBSELINUX + if (is_selinux_enabled()) + (void) strlcat(mntopts, ",context=\"system_u:" + "object_r:file_t:s0\"", sizeof (mntopts)); +#endif /* HAVE_LIBSELINUX */ + if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) return (0); @@ -286,12 +369,9 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags) /* * Determine if the mountpoint is empty. If so, refuse to perform the - * mount. We don't perform this check if MS_OVERLAY is specified, which - * would defeat the point. We also avoid this check if 'remount' is - * specified. + * mount. We don't perform this check if 'remount' is specified. */ - if ((flags & MS_OVERLAY) == 0 && - strstr(mntopts, MNTOPT_REMOUNT) == NULL && + if (strstr(mntopts, MNTOPT_REMOUNT) == NULL && !dir_is_empty(mountpoint)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "directory is not empty")); @@ -300,20 +380,20 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags) } /* perform the mount */ - if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, - MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { + rc = do_mount(zfs_get_name(zhp), mountpoint, mntopts); + if (rc) { /* * Generic errors are nasty, but there are just way too many * from mount(), and they're well-understood. We pick a few * common ones to improve upon. */ - if (errno == EBUSY) { + if (rc == EBUSY) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "mountpoint or dataset is busy")); - } else if (errno == EPERM) { + } else if (rc == EPERM) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Insufficient privileges")); - } else if (errno == ENOTSUP) { + } else if (rc == ENOTSUP) { char buf[256]; int spa_version; @@ -326,7 +406,7 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags) ZFS_PROP_VERSION), spa_version); zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, buf)); } else { - zfs_error_aux(hdl, strerror(errno)); + zfs_error_aux(hdl, strerror(rc)); } return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), @@ -334,8 +414,7 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags) } /* add the mounted entry into our cache */ - libzfs_mnttab_add(hdl, zfs_get_name(zhp), mountpoint, - mntopts); + libzfs_mnttab_add(hdl, zfs_get_name(zhp), mountpoint, mntopts); return (0); } @@ -345,7 +424,7 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags) static int unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags) { - if (umount2(mountpoint, flags) != 0) { + if (do_unmount(mountpoint, flags) != 0) { zfs_error_aux(hdl, strerror(errno)); return (zfs_error_fmt(hdl, EZFS_UMOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), @@ -437,18 +516,14 @@ zfs_is_shared(zfs_handle_t *zhp) int zfs_share(zfs_handle_t *zhp) { - if (ZFS_IS_VOLUME(zhp)) - return (0); - + assert(!ZFS_IS_VOLUME(zhp)); return (zfs_share_proto(zhp, share_all_proto)); } int zfs_unshare(zfs_handle_t *zhp) { - if (ZFS_IS_VOLUME(zhp)) - return (0); - + assert(!ZFS_IS_VOLUME(zhp)); return (zfs_unshareall(zhp)); } @@ -464,7 +539,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(zhp->zfs_hdl, mountpoint, proto))) { if (where != NULL) *where = mountpoint; else @@ -516,8 +591,12 @@ static void (*_sa_update_sharetab_ts)(sa_handle_t); * values to be used later. This is triggered by the runtime loader. * Make sure the correct ISA version is loaded. */ - +#ifdef __GNUC__ +static void +_zfs_init_libshare(void) __attribute__((constructor)); +#else #pragma init(_zfs_init_libshare) +#endif static void _zfs_init_libshare(void) { @@ -714,10 +793,12 @@ zfs_share_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto) return (0); if ((ret = zfs_init_libshare(hdl, SA_INIT_SHARE_API)) != SA_OK) { +#ifdef HAVE_SHARE (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED, dgettext(TEXT_DOMAIN, "cannot share '%s': %s"), zfs_get_name(zhp), _sa_errorstr != NULL ? _sa_errorstr(ret) : ""); +#endif /* HAVE_SHARE */ return (-1); } @@ -979,18 +1060,29 @@ remove_mountpoint(zfs_handle_t *zhp) } } -typedef struct mount_cbdata { - zfs_handle_t **cb_datasets; - int cb_used; - int cb_alloc; -} mount_cbdata_t; +void +libzfs_add_handle(get_all_cb_t *cbp, zfs_handle_t *zhp) +{ + if (cbp->cb_alloc == cbp->cb_used) { + size_t newsz; + void *ptr; + + newsz = cbp->cb_alloc ? cbp->cb_alloc * 2 : 64; + ptr = zfs_realloc(zhp->zfs_hdl, + cbp->cb_handles, cbp->cb_alloc * sizeof (void *), + newsz * sizeof (void *)); + cbp->cb_handles = ptr; + cbp->cb_alloc = newsz; + } + cbp->cb_handles[cbp->cb_used++] = zhp; +} static int mount_cb(zfs_handle_t *zhp, void *data) { - mount_cbdata_t *cbp = data; + get_all_cb_t *cbp = data; - if (!(zfs_get_type(zhp) & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME))) { + if (!(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM)) { zfs_close(zhp); return (0); } @@ -1000,25 +1092,16 @@ mount_cb(zfs_handle_t *zhp, void *data) return (0); } - if (cbp->cb_alloc == cbp->cb_used) { - void *ptr; - - if ((ptr = zfs_realloc(zhp->zfs_hdl, - cbp->cb_datasets, cbp->cb_alloc * sizeof (void *), - cbp->cb_alloc * 2 * sizeof (void *))) == NULL) - return (-1); - cbp->cb_datasets = ptr; - - cbp->cb_alloc *= 2; + libzfs_add_handle(cbp, zhp); + if (zfs_iter_filesystems(zhp, mount_cb, cbp) != 0) { + zfs_close(zhp); + return (-1); } - - cbp->cb_datasets[cbp->cb_used++] = zhp; - - return (zfs_iter_filesystems(zhp, mount_cb, cbp)); + return (0); } -static int -dataset_cmp(const void *a, const void *b) +int +libzfs_dataset_cmp(const void *a, const void *b) { zfs_handle_t **za = (zfs_handle_t **)a; zfs_handle_t **zb = (zfs_handle_t **)b; @@ -1056,7 +1139,7 @@ dataset_cmp(const void *a, const void *b) int zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) { - mount_cbdata_t cb = { 0 }; + get_all_cb_t cb = { 0 }; libzfs_handle_t *hdl = zhp->zpool_hdl; zfs_handle_t *zfsp; int i, ret = -1; @@ -1065,23 +1148,17 @@ zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) /* * Gather all non-snap datasets within the pool. */ - if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL) - return (-1); - cb.cb_alloc = 4; - if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_DATASET)) == NULL) goto out; - cb.cb_datasets[0] = zfsp; - cb.cb_used = 1; - + libzfs_add_handle(&cb, zfsp); if (zfs_iter_filesystems(zfsp, mount_cb, &cb) != 0) goto out; - /* * Sort the datasets by mountpoint. */ - qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_cmp); + qsort(cb.cb_handles, cb.cb_used, sizeof (void *), + libzfs_dataset_cmp); /* * And mount all the datasets, keeping track of which ones @@ -1093,7 +1170,7 @@ zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) ret = 0; for (i = 0; i < cb.cb_used; i++) { - if (zfs_mount(cb.cb_datasets[i], mntopts, flags) != 0) + if (zfs_mount(cb.cb_handles[i], mntopts, flags) != 0) ret = -1; else good[i] = 1; @@ -1106,7 +1183,7 @@ zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) * zfs_alloc is supposed to exit if memory isn't available. */ for (i = 0; i < cb.cb_used; i++) { - if (good[i] && zfs_share(cb.cb_datasets[i]) != 0) + if (good[i] && zfs_share(cb.cb_handles[i]) != 0) ret = -1; } @@ -1114,8 +1191,8 @@ zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) out: for (i = 0; i < cb.cb_used; i++) - zfs_close(cb.cb_datasets[i]); - free(cb.cb_datasets); + zfs_close(cb.cb_handles[i]); + free(cb.cb_handles); return (ret); } @@ -1135,7 +1212,7 @@ mountpoint_compare(const void *a, const void *b) * Unshare and unmount all datasets within the given pool. We don't want to * rely on traversing the DSL to discover the filesystems within the pool, * because this may be expensive (if not all of them are mounted), and can fail - * arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and + * arbitrarily (on I/O error, for example). Instead, we walk /etc/mtab and * gather all the filesystems that are currently mounted. */ int @@ -1266,3 +1343,53 @@ out: return (ret); } + +#else /* HAVE_ZPL */ + +int +zfs_unshare_iscsi(zfs_handle_t *zhp) +{ + return 0; +} + +int +zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) +{ + return 0; +} + +void +remove_mountpoint(zfs_handle_t *zhp) { + return; +} + +boolean_t +is_mounted(libzfs_handle_t *zfs_hdl, const char *special, char **where) +{ + return B_FALSE; +} + +boolean_t +zfs_is_mounted(zfs_handle_t *zhp, char **where) +{ + return is_mounted(zhp->zfs_hdl, zfs_get_name(zhp), where); +} + +boolean_t +zfs_is_shared(zfs_handle_t *zhp) +{ + return B_FALSE; +} + +int +zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) +{ + return B_FALSE; +} + +int +zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force) +{ + return B_FALSE; +} +#endif /* HAVE_ZPL */