43 #include <stddef.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <strings.h>
47 #include <unistd.h>
48 #include <fcntl.h>
49 #include <zone.h>
50 #include <grp.h>
51 #include <pwd.h>
52 #include <signal.h>
53 #include <sys/debug.h>
54 #include <sys/list.h>
55 #include <sys/mkdev.h>
56 #include <sys/mntent.h>
57 #include <sys/mnttab.h>
58 #include <sys/mount.h>
59 #include <sys/stat.h>
60 #include <sys/fs/zfs.h>
61 #include <sys/types.h>
62 #include <time.h>
63
64 #include <libzfs.h>
65 #include <libzfs_core.h>
66 #include <zfs_prop.h>
67 #include <zfs_deleg.h>
68 #include <libuutil.h>
69 #include <aclutils.h>
70 #include <directory.h>
71 #include <idmap.h>
72 #include <libshare.h>
73
74 #include "zfs_iter.h"
75 #include "zfs_util.h"
76 #include "zfs_comutil.h"
77
78 libzfs_handle_t *g_zfs;
79
80 static FILE *mnttab_file;
81 static char history_str[HIS_MAX_RECORD_LEN];
82 static boolean_t log_history = B_TRUE;
5732 holds_callback, &cb);
5733 if (ret != 0)
5734 ++errors;
5735 }
5736
5737 /*
5738 * 2. print holds data
5739 */
5740 print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl);
5741
5742 if (nvlist_empty(nvl))
5743 (void) printf(gettext("no datasets available\n"));
5744
5745 nvlist_free(nvl);
5746
5747 return (0 != errors);
5748 }
5749
5750 #define CHECK_SPINNER 30
5751 #define SPINNER_TIME 3 /* seconds */
5752 #define MOUNT_TIME 5 /* seconds */
5753
5754 static int
5755 get_one_dataset(zfs_handle_t *zhp, void *data)
5756 {
5757 static char *spin[] = { "-", "\\", "|", "/" };
5758 static int spinval = 0;
5759 static int spincheck = 0;
5760 static time_t last_spin_time = (time_t)0;
5761 get_all_cb_t *cbp = data;
5762 zfs_type_t type = zfs_get_type(zhp);
5763
5764 if (cbp->cb_verbose) {
5765 if (--spincheck < 0) {
5766 time_t now = time(NULL);
5767 if (last_spin_time + SPINNER_TIME < now) {
5768 update_progress(spin[spinval++ % 4]);
5769 last_spin_time = now;
5770 }
5771 spincheck = CHECK_SPINNER;
5772 }
5773 }
5774
5775 /*
5776 * Interate over any nested datasets.
5777 */
5778 if (zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) {
5779 zfs_close(zhp);
5780 return (1);
5781 }
5782
5783 /*
5784 * Skip any datasets whose type does not match.
5785 */
5786 if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
5787 zfs_close(zhp);
5788 return (0);
5789 }
5790 libzfs_add_handle(cbp, zhp);
5791 assert(cbp->cb_used <= cbp->cb_alloc);
5792
5793 return (0);
5794 }
5795
5796 static void
5797 get_all_datasets(zfs_handle_t ***dslist, size_t *count, boolean_t verbose)
5798 {
5799 get_all_cb_t cb = { 0 };
5800 cb.cb_verbose = verbose;
5801 cb.cb_getone = get_one_dataset;
5802
5803 if (verbose)
5804 set_progress_header(gettext("Reading ZFS config"));
5805 (void) zfs_iter_root(g_zfs, get_one_dataset, &cb);
5806
5807 *dslist = cb.cb_handles;
5808 *count = cb.cb_used;
5809
5810 if (verbose)
5811 finish_progress(gettext("done."));
5812 }
5813
5814 /*
5815 * Generic callback for sharing or mounting filesystems. Because the code is so
5816 * similar, we have a common function with an extra parameter to determine which
5817 * mode we are using.
5818 */
5819 #define OP_SHARE 0x1
5820 #define OP_MOUNT 0x2
5821
5822 /*
5823 * Share or mount a dataset.
5824 */
5825 static int
5826 share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
5827 boolean_t explicit, const char *options)
5828 {
5829 char mountpoint[ZFS_MAXPROPLEN];
5830 char shareopts[ZFS_MAXPROPLEN];
5831 char smbshareopts[ZFS_MAXPROPLEN];
5832 const char *cmdname = op == OP_SHARE ? "share" : "mount";
5833 struct mnttab mnt;
5834 uint64_t zoned, canmount;
5835 boolean_t shared_nfs, shared_smb;
5836
5837 assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM);
5838
5839 /*
5840 * Check to make sure we can mount/share this dataset. If we
5841 * are in the global zone and the filesystem is exported to a
6042 ++current;
6043
6044 /* display header if we're here for the first time */
6045 if (current == 1) {
6046 set_progress_header(gettext("Mounting ZFS filesystems"));
6047 } else if (current != total && last_progress_time + MOUNT_TIME >= now) {
6048 /* too soon to report again */
6049 return;
6050 }
6051
6052 last_progress_time = now;
6053
6054 (void) sprintf(info, "(%d/%d)", current, total);
6055
6056 if (current == total)
6057 finish_progress(info);
6058 else
6059 update_progress(info);
6060 }
6061
6062 static void
6063 append_options(char *mntopts, char *newopts)
6064 {
6065 int len = strlen(mntopts);
6066
6067 /* original length plus new string to append plus 1 for the comma */
6068 if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) {
6069 (void) fprintf(stderr, gettext("the opts argument for "
6070 "'%c' option is too long (more than %d chars)\n"),
6071 "-o", MNT_LINE_MAX);
6072 usage(B_FALSE);
6073 }
6074
6075 if (*mntopts)
6076 mntopts[len++] = ',';
6077
6078 (void) strcpy(&mntopts[len], newopts);
6079 }
6080
6081 static int
6114 case 'O':
6115 flags |= MS_OVERLAY;
6116 break;
6117 case ':':
6118 (void) fprintf(stderr, gettext("missing argument for "
6119 "'%c' option\n"), optopt);
6120 usage(B_FALSE);
6121 break;
6122 case '?':
6123 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
6124 optopt);
6125 usage(B_FALSE);
6126 }
6127 }
6128
6129 argc -= optind;
6130 argv += optind;
6131
6132 /* check number of arguments */
6133 if (do_all) {
6134 zfs_handle_t **dslist = NULL;
6135 size_t i, count = 0;
6136 char *protocol = NULL;
6137
6138 if (op == OP_SHARE && argc > 0) {
6139 if (strcmp(argv[0], "nfs") != 0 &&
6140 strcmp(argv[0], "smb") != 0) {
6141 (void) fprintf(stderr, gettext("share type "
6142 "must be 'nfs' or 'smb'\n"));
6143 usage(B_FALSE);
6144 }
6145 protocol = argv[0];
6146 argc--;
6147 argv++;
6148 }
6149
6150 if (argc != 0) {
6151 (void) fprintf(stderr, gettext("too many arguments\n"));
6152 usage(B_FALSE);
6153 }
6154
6155 start_progress_timer();
6156 get_all_datasets(&dslist, &count, verbose);
6157
6158 if (count == 0)
6159 return (0);
6160
6161 qsort(dslist, count, sizeof (void *), libzfs_dataset_cmp);
6162 sa_init_selective_arg_t sharearg;
6163 sharearg.zhandle_arr = dslist;
6164 sharearg.zhandle_len = count;
6165 if ((ret = zfs_init_libshare_arg(zfs_get_handle(dslist[0]),
6166 SA_INIT_SHARE_API_SELECTIVE, &sharearg)) != SA_OK) {
6167 (void) fprintf(stderr,
6168 gettext("Could not initialize libshare, %d"), ret);
6169 return (ret);
6170 }
6171
6172 for (i = 0; i < count; i++) {
6173 if (verbose)
6174 report_mount_progress(i, count);
6175
6176 if (share_mount_one(dslist[i], op, flags, protocol,
6177 B_FALSE, options) != 0)
6178 ret = 1;
6179 zfs_close(dslist[i]);
6180 }
6181
6182 free(dslist);
6183 } else if (argc == 0) {
6184 struct mnttab entry;
6185
6186 if ((op == OP_SHARE) || (options != NULL)) {
6187 (void) fprintf(stderr, gettext("missing filesystem "
6188 "argument (specify -a for all)\n"));
6189 usage(B_FALSE);
6190 }
6191
6192 /*
6193 * When mount is given no arguments, go through /etc/mnttab and
6194 * display any active ZFS mounts. We hide any snapshots, since
6195 * they are controlled automatically.
6196 */
6197 rewind(mnttab_file);
6198 while (getmntent(mnttab_file, &entry) == 0) {
6199 if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 ||
6200 strchr(entry.mnt_special, '@') != NULL)
6201 continue;
6202
|
43 #include <stddef.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <strings.h>
47 #include <unistd.h>
48 #include <fcntl.h>
49 #include <zone.h>
50 #include <grp.h>
51 #include <pwd.h>
52 #include <signal.h>
53 #include <sys/debug.h>
54 #include <sys/list.h>
55 #include <sys/mkdev.h>
56 #include <sys/mntent.h>
57 #include <sys/mnttab.h>
58 #include <sys/mount.h>
59 #include <sys/stat.h>
60 #include <sys/fs/zfs.h>
61 #include <sys/types.h>
62 #include <time.h>
63 #include <synch.h>
64
65 #include <libzfs.h>
66 #include <libzfs_core.h>
67 #include <zfs_prop.h>
68 #include <zfs_deleg.h>
69 #include <libuutil.h>
70 #include <aclutils.h>
71 #include <directory.h>
72 #include <idmap.h>
73 #include <libshare.h>
74
75 #include "zfs_iter.h"
76 #include "zfs_util.h"
77 #include "zfs_comutil.h"
78
79 libzfs_handle_t *g_zfs;
80
81 static FILE *mnttab_file;
82 static char history_str[HIS_MAX_RECORD_LEN];
83 static boolean_t log_history = B_TRUE;
5733 holds_callback, &cb);
5734 if (ret != 0)
5735 ++errors;
5736 }
5737
5738 /*
5739 * 2. print holds data
5740 */
5741 print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl);
5742
5743 if (nvlist_empty(nvl))
5744 (void) printf(gettext("no datasets available\n"));
5745
5746 nvlist_free(nvl);
5747
5748 return (0 != errors);
5749 }
5750
5751 #define CHECK_SPINNER 30
5752 #define SPINNER_TIME 3 /* seconds */
5753 #define MOUNT_TIME 1 /* seconds */
5754
5755 typedef struct get_all_state {
5756 boolean_t ga_verbose;
5757 get_all_cb_t *ga_cbp;
5758 } get_all_state_t;
5759
5760 static int
5761 get_one_dataset(zfs_handle_t *zhp, void *data)
5762 {
5763 static char *spin[] = { "-", "\\", "|", "/" };
5764 static int spinval = 0;
5765 static int spincheck = 0;
5766 static time_t last_spin_time = (time_t)0;
5767 get_all_state_t *state = data;
5768 zfs_type_t type = zfs_get_type(zhp);
5769
5770 if (state->ga_verbose) {
5771 if (--spincheck < 0) {
5772 time_t now = time(NULL);
5773 if (last_spin_time + SPINNER_TIME < now) {
5774 update_progress(spin[spinval++ % 4]);
5775 last_spin_time = now;
5776 }
5777 spincheck = CHECK_SPINNER;
5778 }
5779 }
5780
5781 /*
5782 * Interate over any nested datasets.
5783 */
5784 if (zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) {
5785 zfs_close(zhp);
5786 return (1);
5787 }
5788
5789 /*
5790 * Skip any datasets whose type does not match.
5791 */
5792 if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
5793 zfs_close(zhp);
5794 return (0);
5795 }
5796 libzfs_add_handle(state->ga_cbp, zhp);
5797 assert(state->ga_cbp->cb_used <= state->ga_cbp->cb_alloc);
5798
5799 return (0);
5800 }
5801
5802 static void
5803 get_all_datasets(get_all_cb_t *cbp, boolean_t verbose)
5804 {
5805 get_all_state_t state = {
5806 .ga_verbose = verbose,
5807 .ga_cbp = cbp
5808 };
5809
5810 if (verbose)
5811 set_progress_header(gettext("Reading ZFS config"));
5812 (void) zfs_iter_root(g_zfs, get_one_dataset, &state);
5813
5814 if (verbose)
5815 finish_progress(gettext("done."));
5816 }
5817
5818 /*
5819 * Generic callback for sharing or mounting filesystems. Because the code is so
5820 * similar, we have a common function with an extra parameter to determine which
5821 * mode we are using.
5822 */
5823 typedef enum { OP_SHARE, OP_MOUNT } share_mount_op_t;
5824
5825 typedef struct share_mount_state {
5826 share_mount_op_t sm_op;
5827 boolean_t sm_verbose;
5828 int sm_flags;
5829 char *sm_options;
5830 char *sm_proto; /* only valid for OP_SHARE */
5831 mutex_t sm_lock; /* protects the remaining fields */
5832 uint_t sm_total; /* number of filesystems to process */
5833 uint_t sm_done; /* number of filesystems processed */
5834 int sm_status; /* -1 if any of the share/mount operations failed */
5835 } share_mount_state_t;
5836
5837 /*
5838 * Share or mount a dataset.
5839 */
5840 static int
5841 share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
5842 boolean_t explicit, const char *options)
5843 {
5844 char mountpoint[ZFS_MAXPROPLEN];
5845 char shareopts[ZFS_MAXPROPLEN];
5846 char smbshareopts[ZFS_MAXPROPLEN];
5847 const char *cmdname = op == OP_SHARE ? "share" : "mount";
5848 struct mnttab mnt;
5849 uint64_t zoned, canmount;
5850 boolean_t shared_nfs, shared_smb;
5851
5852 assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM);
5853
5854 /*
5855 * Check to make sure we can mount/share this dataset. If we
5856 * are in the global zone and the filesystem is exported to a
6057 ++current;
6058
6059 /* display header if we're here for the first time */
6060 if (current == 1) {
6061 set_progress_header(gettext("Mounting ZFS filesystems"));
6062 } else if (current != total && last_progress_time + MOUNT_TIME >= now) {
6063 /* too soon to report again */
6064 return;
6065 }
6066
6067 last_progress_time = now;
6068
6069 (void) sprintf(info, "(%d/%d)", current, total);
6070
6071 if (current == total)
6072 finish_progress(info);
6073 else
6074 update_progress(info);
6075 }
6076
6077 /*
6078 * zfs_foreach_mountpoint() callback that mounts or shares one filesystem and
6079 * updates the progress meter.
6080 */
6081 static int
6082 share_mount_one_cb(zfs_handle_t *zhp, void *arg)
6083 {
6084 share_mount_state_t *sms = arg;
6085 int ret;
6086
6087 ret = share_mount_one(zhp, sms->sm_op, sms->sm_flags, sms->sm_proto,
6088 B_FALSE, sms->sm_options);
6089
6090 (void) mutex_lock(&sms->sm_lock);
6091 if (ret != 0)
6092 sms->sm_status = ret;
6093 sms->sm_done++;
6094 if (sms->sm_verbose)
6095 report_mount_progress(sms->sm_done, sms->sm_total);
6096 (void) mutex_unlock(&sms->sm_lock);
6097 return (ret);
6098 }
6099
6100 static void
6101 append_options(char *mntopts, char *newopts)
6102 {
6103 int len = strlen(mntopts);
6104
6105 /* original length plus new string to append plus 1 for the comma */
6106 if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) {
6107 (void) fprintf(stderr, gettext("the opts argument for "
6108 "'%c' option is too long (more than %d chars)\n"),
6109 "-o", MNT_LINE_MAX);
6110 usage(B_FALSE);
6111 }
6112
6113 if (*mntopts)
6114 mntopts[len++] = ',';
6115
6116 (void) strcpy(&mntopts[len], newopts);
6117 }
6118
6119 static int
6152 case 'O':
6153 flags |= MS_OVERLAY;
6154 break;
6155 case ':':
6156 (void) fprintf(stderr, gettext("missing argument for "
6157 "'%c' option\n"), optopt);
6158 usage(B_FALSE);
6159 break;
6160 case '?':
6161 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
6162 optopt);
6163 usage(B_FALSE);
6164 }
6165 }
6166
6167 argc -= optind;
6168 argv += optind;
6169
6170 /* check number of arguments */
6171 if (do_all) {
6172 char *protocol = NULL;
6173
6174 if (op == OP_SHARE && argc > 0) {
6175 if (strcmp(argv[0], "nfs") != 0 &&
6176 strcmp(argv[0], "smb") != 0) {
6177 (void) fprintf(stderr, gettext("share type "
6178 "must be 'nfs' or 'smb'\n"));
6179 usage(B_FALSE);
6180 }
6181 protocol = argv[0];
6182 argc--;
6183 argv++;
6184 }
6185
6186 if (argc != 0) {
6187 (void) fprintf(stderr, gettext("too many arguments\n"));
6188 usage(B_FALSE);
6189 }
6190
6191 start_progress_timer();
6192 get_all_cb_t cb = { 0 };
6193 get_all_datasets(&cb, verbose);
6194
6195 if (cb.cb_used == 0)
6196 return (0);
6197
6198 if (op == OP_SHARE) {
6199 sa_init_selective_arg_t sharearg;
6200 sharearg.zhandle_arr = cb.cb_handles;
6201 sharearg.zhandle_len = cb.cb_used;
6202 if ((ret = zfs_init_libshare_arg(g_zfs,
6203 SA_INIT_SHARE_API_SELECTIVE, &sharearg)) != SA_OK) {
6204 (void) fprintf(stderr, gettext(
6205 "Could not initialize libshare, %d"), ret);
6206 return (ret);
6207 }
6208 }
6209
6210 share_mount_state_t share_mount_state = { 0 };
6211 share_mount_state.sm_op = op;
6212 share_mount_state.sm_verbose = verbose;
6213 share_mount_state.sm_flags = flags;
6214 share_mount_state.sm_options = options;
6215 share_mount_state.sm_proto = protocol;
6216 share_mount_state.sm_total = cb.cb_used;
6217 (void) mutex_init(&share_mount_state.sm_lock, USYNC_THREAD,
6218 NULL);
6219 /*
6220 * libshare isn't mt-safe, so only do the operation in parallel
6221 * if we're mounting.
6222 */
6223 zfs_foreach_mountpoint(g_zfs, cb.cb_handles, cb.cb_used,
6224 share_mount_one_cb, &share_mount_state, op == OP_MOUNT);
6225 ret = share_mount_state.sm_status;
6226
6227 for (int i = 0; i < cb.cb_used; i++)
6228 zfs_close(cb.cb_handles[i]);
6229 free(cb.cb_handles);
6230 } else if (argc == 0) {
6231 struct mnttab entry;
6232
6233 if ((op == OP_SHARE) || (options != NULL)) {
6234 (void) fprintf(stderr, gettext("missing filesystem "
6235 "argument (specify -a for all)\n"));
6236 usage(B_FALSE);
6237 }
6238
6239 /*
6240 * When mount is given no arguments, go through /etc/mnttab and
6241 * display any active ZFS mounts. We hide any snapshots, since
6242 * they are controlled automatically.
6243 */
6244 rewind(mnttab_file);
6245 while (getmntent(mnttab_file, &entry) == 0) {
6246 if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 ||
6247 strchr(entry.mnt_special, '@') != NULL)
6248 continue;
6249
|