Autoconf selinux support
[zfs.git] / lib / libzfs / libzfs_mount.c
index 7c5c7f3..9950bf9 100644 (file)
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
  *
  *     zfs_is_shared_nfs()
  *     zfs_is_shared_smb()
- *     zfs_is_shared_iscsi()
  *     zfs_share_proto()
  *     zfs_shareall();
- *     zfs_share_iscsi()
  *     zfs_unshare_nfs()
  *     zfs_unshare_smb()
  *     zfs_unshareall_nfs()
  *     zfs_unshareall_smb()
  *     zfs_unshareall()
  *     zfs_unshareall_bypath()
- *     zfs_unshare_iscsi()
  *
  * The following functions are available for pool consumers, and will
  * mount/unmount and share/unshare all datasets within pool:
 #include <unistd.h>
 #include <zone.h>
 #include <sys/mntent.h>
-#include <sys/mnttab.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
+#ifdef HAVE_LIBSELINUX
+#include <selinux/selinux.h>
+#endif /* HAVE_LIBSELINUX */
 
 #include <libzfs.h>
 
 #include <sys/systeminfo.h>
 #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);
 
-static int (*iscsitgt_zfs_share)(const char *);
-static int (*iscsitgt_zfs_unshare)(const char *);
-static int (*iscsitgt_zfs_is_shared)(const char *);
-static int (*iscsitgt_svc_online)();
-
 /*
  * The share protocols table must be in the same order as the zfs_share_prot_t
  * enum in libzfs_impl.h
@@ -126,29 +120,6 @@ zfs_share_proto_t share_all_proto[] = {
        PROTO_END
 };
 
-#pragma init(zfs_iscsi_init)
-static void
-zfs_iscsi_init(void)
-{
-       void *libiscsitgt;
-
-       if ((libiscsitgt = dlopen("/lib/libiscsitgt.so.1",
-           RTLD_LAZY | RTLD_GLOBAL)) == NULL ||
-           (iscsitgt_zfs_share = (int (*)(const char *))dlsym(libiscsitgt,
-           "iscsitgt_zfs_share")) == NULL ||
-           (iscsitgt_zfs_unshare = (int (*)(const char *))dlsym(libiscsitgt,
-           "iscsitgt_zfs_unshare")) == NULL ||
-           (iscsitgt_zfs_is_shared = (int (*)(const char *))dlsym(libiscsitgt,
-           "iscsitgt_zfs_is_shared")) == NULL ||
-           (iscsitgt_svc_online = (int (*)(const char *))dlsym(libiscsitgt,
-           "iscsitgt_svc_online")) == NULL) {
-               iscsitgt_zfs_share = NULL;
-               iscsitgt_zfs_unshare = NULL;
-               iscsitgt_zfs_is_shared = NULL;
-               iscsitgt_svc_online = NULL;
-       }
-}
-
 /*
  * Search the sharetab for the given mountpoint and protocol, returning
  * a zfs_share_type_t value.
@@ -236,18 +207,9 @@ dir_is_empty(const char *dirname)
 boolean_t
 is_mounted(libzfs_handle_t *zfs_hdl, const char *special, char **where)
 {
-       struct mnttab search = { 0 }, entry;
-
-       /*
-        * Search for the entry in /etc/mnttab.  We don't bother getting the
-        * mountpoint, as we can just search for the special device.  This will
-        * also let us find mounts when the mountpoint is 'legacy'.
-        */
-       search.mnt_special = (char *)special;
-       search.mnt_fstype = MNTTYPE_ZFS;
+       struct mnttab entry;
 
-       rewind(zfs_hdl->libzfs_mnttab);
-       if (getmntany(zfs_hdl->libzfs_mnttab, &entry, &search) != 0)
+       if (libzfs_mnttab_find(zfs_hdl, special, &entry) != 0)
                return (B_FALSE);
 
        if (where != NULL)
@@ -312,6 +274,18 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
        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))
+               flags |= MS_RDONLY;
+
+#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);
 
@@ -355,15 +329,29 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
                } else if (errno == EPERM) {
                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                            "Insufficient privileges"));
+               } else if (errno == ENOTSUP) {
+                       char buf[256];
+                       int spa_version;
+
+                       VERIFY(zfs_spa_version(zhp, &spa_version) == 0);
+                       (void) snprintf(buf, sizeof (buf),
+                           dgettext(TEXT_DOMAIN, "Can't mount a version %lld "
+                           "file system on a version %d pool. Pool must be"
+                           " upgraded to mount this file system."),
+                           (u_longlong_t)zfs_prop_get_int(zhp,
+                           ZFS_PROP_VERSION), spa_version);
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, buf));
                } else {
                        zfs_error_aux(hdl, strerror(errno));
                }
-
                return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
                    dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
                    zhp->zfs_name));
        }
 
+       /* add the mounted entry into our cache */
+       libzfs_mnttab_add(hdl, zfs_get_name(zhp), mountpoint,
+           mntopts);
        return (0);
 }
 
@@ -389,26 +377,23 @@ unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags)
 int
 zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
 {
-       struct mnttab search = { 0 }, entry;
+       libzfs_handle_t *hdl = zhp->zfs_hdl;
+       struct mnttab entry;
        char *mntpt = NULL;
 
-       /* check to see if need to unmount the filesystem */
-       search.mnt_special = zhp->zfs_name;
-       search.mnt_fstype = MNTTYPE_ZFS;
-       rewind(zhp->zfs_hdl->libzfs_mnttab);
+       /* check to see if we need to unmount the filesystem */
        if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
-           getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) {
-
+           libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0)) {
                /*
                 * mountpoint may have come from a call to
                 * getmnt/getmntany if it isn't NULL. If it is NULL,
-                * we know it comes from getmntany which can then get
-                * overwritten later. We strdup it to play it safe.
+                * we know it comes from libzfs_mnttab_find which can
+                * then get freed later. We strdup it to play it safe.
                 */
                if (mountpoint == NULL)
-                       mntpt = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp);
+                       mntpt = zfs_strdup(hdl, entry.mnt_mountp);
                else
-                       mntpt = zfs_strdup(zhp->zfs_hdl, mountpoint);
+                       mntpt = zfs_strdup(hdl, mountpoint);
 
                /*
                 * Unshare and unmount the filesystem
@@ -416,11 +401,12 @@ zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
                if (zfs_unshare_proto(zhp, mntpt, share_all_proto) != 0)
                        return (-1);
 
-               if (unmount_one(zhp->zfs_hdl, mntpt, flags) != 0) {
+               if (unmount_one(hdl, mntpt, flags) != 0) {
                        free(mntpt);
                        (void) zfs_shareall(zhp);
                        return (-1);
                }
+               libzfs_mnttab_remove(hdl, zhp->zfs_name);
                free(mntpt);
        }
 
@@ -455,7 +441,7 @@ zfs_is_shared(zfs_handle_t *zhp)
        zfs_share_proto_t *curr_proto;
 
        if (ZFS_IS_VOLUME(zhp))
-               return (zfs_is_shared_iscsi(zhp));
+               return (B_FALSE);
 
        for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
            curr_proto++)
@@ -467,18 +453,14 @@ zfs_is_shared(zfs_handle_t *zhp)
 int
 zfs_share(zfs_handle_t *zhp)
 {
-       if (ZFS_IS_VOLUME(zhp))
-               return (zfs_share_iscsi(zhp));
-
+       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 (zfs_unshare_iscsi(zhp));
-
+       assert(!ZFS_IS_VOLUME(zhp));
        return (zfs_unshareall(zhp));
 }
 
@@ -494,7 +476,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
@@ -546,8 +528,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)
 {
@@ -849,7 +835,7 @@ unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint,
        char *mntpt;
        /*
         * Mountpoint could get trashed if libshare calls getmntany
-        * which id does during API initialization, so strdup the
+        * which it does during API initialization, so strdup the
         * value.
         */
        mntpt = zfs_strdup(hdl, mountpoint);
@@ -887,18 +873,17 @@ int
 zfs_unshare_proto(zfs_handle_t *zhp, const char *mountpoint,
     zfs_share_proto_t *proto)
 {
-       struct mnttab search = { 0 }, entry;
+       libzfs_handle_t *hdl = zhp->zfs_hdl;
+       struct mnttab entry;
        char *mntpt = NULL;
 
        /* check to see if need to unmount the filesystem */
-       search.mnt_special = (char *)zfs_get_name(zhp);
-       search.mnt_fstype = MNTTYPE_ZFS;
        rewind(zhp->zfs_hdl->libzfs_mnttab);
        if (mountpoint != NULL)
-               mntpt = zfs_strdup(zhp->zfs_hdl, mountpoint);
+               mountpoint = mntpt = zfs_strdup(hdl, mountpoint);
 
        if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
-           getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) {
+           libzfs_mnttab_find(hdl, zfs_get_name(zhp), &entry) == 0)) {
                zfs_share_proto_t *curr_proto;
 
                if (mountpoint == NULL)
@@ -907,8 +892,8 @@ zfs_unshare_proto(zfs_handle_t *zhp, const char *mountpoint,
                for (curr_proto = proto; *curr_proto != PROTO_END;
                    curr_proto++) {
 
-                       if (is_shared(zhp->zfs_hdl, mntpt, *curr_proto) &&
-                           unshare_one(zhp->zfs_hdl, zhp->zfs_name,
+                       if (is_shared(hdl, mntpt, *curr_proto) &&
+                           unshare_one(hdl, zhp->zfs_name,
                            mntpt, *curr_proto) != 0) {
                                if (mntpt != NULL)
                                        free(mntpt);
@@ -1010,93 +995,29 @@ remove_mountpoint(zfs_handle_t *zhp)
        }
 }
 
-boolean_t
-zfs_is_shared_iscsi(zfs_handle_t *zhp)
-{
-
-       /*
-        * If iscsi deamon isn't running then we aren't shared
-        */
-       if (iscsitgt_svc_online && iscsitgt_svc_online() == 1)
-               return (B_FALSE);
-       else
-               return (iscsitgt_zfs_is_shared != NULL &&
-                   iscsitgt_zfs_is_shared(zhp->zfs_name) != 0);
-}
-
-int
-zfs_share_iscsi(zfs_handle_t *zhp)
-{
-       char shareopts[ZFS_MAXPROPLEN];
-       const char *dataset = zhp->zfs_name;
-       libzfs_handle_t *hdl = zhp->zfs_hdl;
-
-       /*
-        * Return success if there are no share options.
-        */
-       if (zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, shareopts,
-           sizeof (shareopts), NULL, NULL, 0, B_FALSE) != 0 ||
-           strcmp(shareopts, "off") == 0)
-               return (0);
-
-       if (iscsitgt_zfs_share == NULL || iscsitgt_zfs_share(dataset) != 0) {
-               int error = EZFS_SHAREISCSIFAILED;
-
-               /*
-                * If service isn't availabele and EPERM was
-                * returned then use special error.
-                */
-               if (iscsitgt_svc_online && errno == EPERM &&
-                   (iscsitgt_svc_online() != 0))
-                       error = EZFS_ISCSISVCUNAVAIL;
-
-               return (zfs_error_fmt(hdl, error,
-                   dgettext(TEXT_DOMAIN, "cannot share '%s'"), dataset));
-       }
-
-       return (0);
-}
-
-int
-zfs_unshare_iscsi(zfs_handle_t *zhp)
+void
+libzfs_add_handle(get_all_cb_t *cbp, zfs_handle_t *zhp)
 {
-       const char *dataset = zfs_get_name(zhp);
-       libzfs_handle_t *hdl = zhp->zfs_hdl;
-
-       /*
-        * Return if the volume is not shared
-        */
-       if (zfs_is_shared_iscsi(zhp) != SHARED_ISCSI)
-               return (0);
+       if (cbp->cb_alloc == cbp->cb_used) {
+               size_t newsz;
+               void *ptr;
 
-       /*
-        * If this fails with ENODEV it indicates that zvol wasn't shared so
-        * we should return success in that case.
-        */
-       if (iscsitgt_zfs_unshare == NULL ||
-           (iscsitgt_zfs_unshare(dataset) != 0 && errno != ENODEV)) {
-               if (errno == EPERM)
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "Insufficient privileges to unshare iscsi"));
-               return (zfs_error_fmt(hdl, EZFS_UNSHAREISCSIFAILED,
-                   dgettext(TEXT_DOMAIN, "cannot unshare '%s'"), dataset));
+               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;
        }
-
-       return (0);
+       cbp->cb_handles[cbp->cb_used++] = zhp;
 }
 
-typedef struct mount_cbdata {
-       zfs_handle_t    **cb_datasets;
-       int             cb_used;
-       int             cb_alloc;
-} mount_cbdata_t;
-
 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);
        }
@@ -1106,25 +1027,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;
@@ -1162,7 +1074,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;
@@ -1171,33 +1083,29 @@ 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
-        * succeeded or failed. By using zfs_alloc(), the good pointer
-        * will always be non-NULL.
+        * succeeded or failed.
         */
-       good = zfs_alloc(zhp->zpool_hdl, cb.cb_used * sizeof (int));
+       if ((good = zfs_alloc(zhp->zpool_hdl,
+           cb.cb_used * sizeof (int))) == NULL)
+               goto out;
+
        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;
@@ -1210,7 +1118,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;
        }
 
@@ -1218,34 +1126,12 @@ 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);
 }
 
-
-static int
-zvol_cb(const char *dataset, void *data)
-{
-       libzfs_handle_t *hdl = data;
-       zfs_handle_t *zhp;
-
-       /*
-        * Ignore snapshots and ignore failures from non-existant datasets.
-        */
-       if (strchr(dataset, '@') != NULL ||
-           (zhp = zfs_open(hdl, dataset, ZFS_TYPE_VOLUME)) == NULL)
-               return (0);
-
-       if (zfs_unshare_iscsi(zhp) != 0)
-               return (-1);
-
-       zfs_close(zhp);
-
-       return (0);
-}
-
 static int
 mountpoint_compare(const void *a, const void *b)
 {
@@ -1255,6 +1141,8 @@ mountpoint_compare(const void *a, const void *b)
        return (strcmp(mountb, mounta));
 }
 
+/* alias for 2002/240 */
+#pragma weak zpool_unmount_datasets = zpool_disable_datasets
 /*
  * 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,
@@ -1262,7 +1150,6 @@ mountpoint_compare(const void *a, const void *b)
  * arbitrarily (on I/O error, for example).  Instead, we walk /etc/mnttab and
  * gather all the filesystems that are currently mounted.
  */
-#pragma weak zpool_unmount_datasets = zpool_disable_datasets
 int
 zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force)
 {
@@ -1276,12 +1163,6 @@ zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force)
        int ret = -1;
        int flags = (force ? MS_FORCE : 0);
 
-       /*
-        * First unshare all zvols.
-        */
-       if (zpool_iter_zvol(zhp, zvol_cb, hdl) != 0)
-               return (-1);
-
        namelen = strlen(zhp->zpool_name);
 
        rewind(hdl->libzfs_mnttab);
@@ -1397,3 +1278,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 */