1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include "libfsmgt.h"
30 #include <libzfs.h>
31 #include <string.h>
32 #include <libshare.h>
33 #include "libshare_impl.h"
34 
35 extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *);
36 extern sa_group_t _sa_create_zfs_group(sa_group_t, char *);
37 extern char *sa_fstype(char *);
38 extern void set_node_attr(void *, char *, char *);
39 extern int sa_is_share(void *);
40 /*
41  * File system specific code for ZFS
42  */
43 
44 /*
45  * get_zfs_dataset(path)
46  *
47  * get the name of the ZFS dataset the path is equivalent to.  The
48  * dataset name is used for get/set of ZFS properties since libzfs
49  * requires a dataset to do a zfs_open().
50  */
51 
52 static char *
53 get_zfs_dataset(char *path)
54 {
55 	fs_mntlist_t *list;
56 	fs_mntlist_t *cur;
57 	int err;
58 	char *dataset = NULL;
59 
60 	list = fs_get_filtered_mount_list(NULL, NULL, "zfs", NULL,
61 					    NULL, 0, &err);
62 	for (cur = list; cur != NULL; cur = cur->next) {
63 	    if (strcmp(path, cur->mountp) == 0 ||
64 		strncmp(path, cur->mountp, strlen(cur->mountp)) == 0) {
65 		/*
66 		 * we want the longest resource so keep trying. This
67 		 * check avoids dropping out on a partial match. ZFS
68 		 * resources are ordered when mounted in order to
69 		 * ensure inheritence of properties.
70 		 */
71 		dataset = cur->resource;
72 	    }
73 	}
74 	if (dataset != NULL) {
75 	    dataset = strdup(dataset);
76 	}
77 	fs_free_mount_list(list);
78 	return (dataset);
79 }
80 
81 /*
82  * get_zfs_property(dataset, property)
83  *
84  * Get the file system property specified from the ZFS dataset.
85  */
86 
87 static char *
88 get_zfs_property(char *dataset, zfs_prop_t property)
89 {
90 	zfs_handle_t *handle = NULL;
91 	char shareopts[ZFS_MAXPROPLEN];
92 	libzfs_handle_t *libhandle;
93 
94 	libhandle = libzfs_init();
95 	if (libhandle != NULL) {
96 	    handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
97 	    if (handle != NULL) {
98 		if (zfs_prop_get(handle, property, shareopts,
99 				sizeof (shareopts), NULL, NULL, 0,
100 				FALSE) == 0) {
101 		    zfs_close(handle);
102 		    libzfs_fini(libhandle);
103 		    return (strdup(shareopts));
104 		}
105 		zfs_close(handle);
106 	    }
107 	    libzfs_fini(libhandle);
108 	}
109 	return (NULL);
110 }
111 
112 /*
113  * sa_zfs_is_shared(path)
114  *
115  * Check to see if the ZFS path provided has the sharenfs option set
116  * or not.
117  */
118 
119 int
120 sa_zfs_is_shared(char *path)
121 {
122 	int ret = 0;
123 	char *dataset;
124 	zfs_handle_t *handle = NULL;
125 	char shareopts[ZFS_MAXPROPLEN];
126 	libzfs_handle_t *libhandle;
127 
128 	dataset = get_zfs_dataset(path);
129 	if (dataset != NULL) {
130 	    libhandle = libzfs_init();
131 	    if (libhandle != NULL) {
132 		handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
133 		if (handle != NULL) {
134 		    if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts,
135 					sizeof (shareopts), NULL, NULL, 0,
136 					FALSE) == 0 &&
137 			strcmp(shareopts, "off") != 0)
138 			ret = 1; /* it is shared */
139 		    zfs_close(handle);
140 		}
141 		libzfs_fini(libhandle);
142 	    }
143 	    free(dataset);
144 	}
145 	return (ret);
146 }
147 
148 /*
149  * find_or_create_group(groupname, proto, *err)
150  *
151  * While walking the ZFS tree, we need to add shares to a defined
152  * group. If the group doesn't exist, create it first, making sure it
153  * is marked as a ZFS group.
154  *
155  * Not that all ZFS shares are in a subgroup of the top level group
156  * "zfs".
157  */
158 
159 static sa_group_t
160 find_or_create_group(char *groupname, char *proto, int *err)
161 {
162 	sa_group_t group;
163 	sa_optionset_t optionset;
164 	int ret = SA_OK;
165 
166 	/*
167 	 * we check to see if the "zfs" group exists. Since this
168 	 * should be the top level group, we don't want the
169 	 * parent. This is to make sure the zfs group has been created
170 	 * and to created if it hasn't been.
171 	 */
172 	group = sa_get_group(groupname);
173 	if (group == NULL) {
174 	    group = sa_create_group(groupname, &ret);
175 	    if (group != NULL)
176 		ret = sa_set_group_attr(group, "zfs", "true");
177 	}
178 	if (group != NULL) {
179 	    if (proto != NULL) {
180 		optionset = sa_get_optionset(group, proto);
181 		if (optionset == NULL) {
182 		    optionset = sa_create_optionset(group, proto);
183 		} else {
184 		    char **protolist;
185 		    int numprotos, i;
186 		    numprotos = sa_get_protocols(&protolist);
187 		    for (i = 0; i < numprotos; i++) {
188 			optionset = sa_create_optionset(group, protolist[i]);
189 		    }
190 		    if (protolist != NULL)
191 			free(protolist);
192 		}
193 	    }
194 	}
195 	if (err != NULL)
196 	    *err = ret;
197 	return (group);
198 }
199 
200 /*
201  * sa_get_zfs_shares(groupname)
202  *
203  * Walk the mnttab for all zfs mounts and determine which are
204  * shared. Find or create the appropriate group/sub-group to contain
205  * the shares.
206  *
207  * All shares are in a sub-group that will hold the properties. This
208  * allows representing the inherited property model.
209  */
210 
211 int
212 sa_get_zfs_shares(char *groupname)
213 {
214 	sa_group_t group;
215 	sa_group_t zfsgroup;
216 	int legacy = 0;
217 	int err;
218 	fs_mntlist_t *list;
219 	fs_mntlist_t *cur;
220 	zfs_handle_t *handle = NULL;
221 	char shareopts[ZFS_MAXPROPLEN];
222 	sa_share_t share;
223 	zfs_source_t source;
224 	char sourcestr[ZFS_MAXPROPLEN];
225 	libzfs_handle_t *libhandle;
226 
227 	/*
228 	 * if we can't access libzfs, don't bother doing anything.
229 	 */
230 	libhandle = libzfs_init();
231 	if (libhandle == NULL)
232 	    return (SA_SYSTEM_ERR);
233 
234 	zfsgroup = find_or_create_group(groupname, "nfs", &err);
235 	if (zfsgroup != NULL) {
236 		/*
237 		 * need to walk the mounted ZFS pools and datasets to
238 		 * find shares that are possible.
239 		 */
240 	    list = fs_get_filtered_mount_list(NULL, NULL, "zfs", NULL,
241 					    NULL, 0, &err);
242 	    group = zfsgroup;
243 	    for (cur = list; cur != NULL; cur = cur->next) {
244 		handle = zfs_open(libhandle, cur->resource,
245 				    ZFS_TYPE_FILESYSTEM);
246 		if (handle != NULL) {
247 		    source = ZFS_SRC_ALL;
248 		    if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts,
249 					sizeof (shareopts), &source, sourcestr,
250 					ZFS_MAXPROPLEN,
251 					FALSE) == 0 &&
252 			strcmp(shareopts, "off") != 0) {
253 			/* it is shared so add to list */
254 			share = sa_find_share(cur->mountp);
255 			err = SA_OK;
256 			if (share != NULL) {
257 				/*
258 				 * A zfs file system had been shared
259 				 * through tradiditional methods
260 				 * (share/dfstab or added to a non-zfs
261 				 * group.  Now it has been added to a
262 				 * ZFS group via the zfs
263 				 * command. Remove from previous
264 				 * config and setup with current
265 				 * options.
266 				 */
267 			    err = sa_remove_share(share);
268 			    share = NULL;
269 			}
270 			if (err == SA_OK) {
271 			    if (source & ZFS_SRC_INHERITED) {
272 				share = _sa_add_share(group, cur->mountp,
273 							SA_SHARE_TRANSIENT,
274 							&err);
275 			    } else {
276 				group = _sa_create_zfs_group(zfsgroup,
277 								cur->resource);
278 				set_node_attr(group, "zfs", "true");
279 				share = _sa_add_share(group, cur->mountp,
280 							SA_SHARE_TRANSIENT,
281 							&err);
282 				if (err == SA_OK) {
283 				    char *options;
284 				    if (strcmp(shareopts, "on") != 0) {
285 					options = strdup(shareopts);
286 					if (options != NULL) {
287 					    err = sa_parse_legacy_options(group,
288 									options,
289 									"nfs");
290 					    free(options);
291 					}
292 					/* unmark the share's changed state */
293 					set_node_attr(share, "changed", NULL);
294 				    }
295 				}
296 			    }
297 			}
298 		    }
299 		}
300 	    }
301 	    if (list != NULL)
302 		fs_free_mount_list(list);
303 	}
304 	if (libhandle != NULL)
305 	    libzfs_fini(libhandle);
306 	return (legacy);
307 }
308 
309 #define	COMMAND		"/usr/sbin/zfs"
310 
311 /*
312  * sa_zfs_set_sharenfs(group, path, on)
313  *
314  * Update the "sharenfs" property on the path. If on is true, then set
315  * to the properties on the group or "on" if no properties are
316  * defined. Set to "off" if on is false.
317  */
318 
319 int
320 sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
321 {
322 	int ret = SA_NOT_IMPLEMENTED;
323 	char *command;
324 
325 	command = malloc(ZFS_MAXPROPLEN * 2);
326 	if (command != NULL) {
327 	    char *opts = NULL;
328 	    char *dataset;
329 	    FILE *pfile;
330 	    /* for now, NFS is always available for "zfs" */
331 	    if (on) {
332 		opts = sa_proto_legacy_format("nfs", group, 1);
333 		if (opts != NULL && strlen(opts) == 0) {
334 		    free(opts);
335 		    opts = strdup("on");
336 		}
337 	    }
338 	    dataset = get_zfs_dataset(path);
339 	    if (dataset != NULL) {
340 		(void) snprintf(command, ZFS_MAXPROPLEN * 2,
341 				"%s set sharenfs=\"%s\" %s", COMMAND,
342 				opts != NULL ? opts : "off",
343 				dataset);
344 		pfile = popen(command, "r");
345 		if (pfile != NULL) {
346 		    ret = pclose(pfile);
347 		    if (ret != 0)
348 			ret = SA_SYSTEM_ERR;
349 		}
350 	    }
351 	    if (opts != NULL)
352 		free(opts);
353 	    if (dataset != NULL)
354 		free(dataset);
355 	    free(command);
356 	}
357 	return (ret);
358 }
359 
360 /*
361  * sa_zfs_update(group)
362  *
363  * call back to ZFS to update the share if necessary.
364  * Don't do it if it isn't a real change.
365  */
366 int
367 sa_zfs_update(sa_group_t group)
368 {
369 	sa_optionset_t protopt;
370 	sa_group_t parent;
371 	char *command;
372 	char *optstring;
373 	int ret = SA_OK;
374 	int doupdate = 0;
375 	FILE *pfile;
376 
377 	if (sa_is_share(group))
378 	    parent = sa_get_parent_group(group);
379 	else
380 	    parent = group;
381 
382 	if (parent != NULL) {
383 	    command = malloc(ZFS_MAXPROPLEN * 2);
384 	    if (command == NULL)
385 		return (SA_NO_MEMORY);
386 
387 	    *command = '\0';
388 	    for (protopt = sa_get_optionset(parent, NULL); protopt != NULL;
389 		protopt = sa_get_next_optionset(protopt)) {
390 
391 		char *proto = sa_get_optionset_attr(protopt, "type");
392 		char *path;
393 		char *dataset = NULL;
394 		char *zfsopts = NULL;
395 
396 		if (sa_is_share(group)) {
397 		    path = sa_get_share_attr((sa_share_t)group, "path");
398 		    if (path != NULL) {
399 			dataset = get_zfs_dataset(path);
400 			sa_free_attr_string(path);
401 		    }
402 		} else {
403 		    dataset = sa_get_group_attr(group, "name");
404 		}
405 		/* update only when there is an optstring found */
406 		doupdate = 0;
407 		if (proto != NULL && dataset != NULL) {
408 		    optstring = sa_proto_legacy_format(proto, group, 1);
409 		    zfsopts = get_zfs_property(dataset, ZFS_PROP_SHARENFS);
410 
411 		    if (optstring != NULL && zfsopts != NULL) {
412 			if (strcmp(optstring, zfsopts) != 0)
413 			    doupdate++;
414 		    }
415 
416 		    if (doupdate) {
417 			if (optstring != NULL && strlen(optstring) > 0) {
418 			    (void) snprintf(command, ZFS_MAXPROPLEN * 2,
419 					    "%s set sharenfs=%s %s", COMMAND,
420 					    optstring, dataset);
421 			} else {
422 			    (void) snprintf(command, ZFS_MAXPROPLEN * 2,
423 					    "%s set sharenfs=on %s", COMMAND,
424 					    dataset);
425 			}
426 			pfile = popen(command, "r");
427 			if (pfile != NULL)
428 			    ret = pclose(pfile);
429 			switch (ret) {
430 			default:
431 			case 1:
432 			    ret = SA_SYSTEM_ERR;
433 			    break;
434 			case 2:
435 			    ret = SA_SYNTAX_ERR;
436 			    break;
437 			case 0:
438 			    break;
439 			}
440 		    }
441 		    if (optstring != NULL) {
442 			free(optstring);
443 		    }
444 		    if (zfsopts != NULL)
445 			free(zfsopts);
446 		}
447 		if (proto != NULL)
448 		    sa_free_attr_string(proto);
449 		if (dataset != NULL)
450 		    free(dataset);
451 	    }
452 	    free(command);
453 	}
454 	return (ret);
455 }
456 
457 /*
458  * sa_group_is_zfs(group)
459  *
460  * Given the group, determine if the zfs attribute is set.
461  */
462 
463 int
464 sa_group_is_zfs(sa_group_t group)
465 {
466 	char *zfs;
467 	int ret = 0;
468 
469 	zfs = sa_get_group_attr(group, "zfs");
470 	if (zfs != NULL) {
471 	    ret = 1;
472 	    sa_free_attr_string(zfs);
473 	}
474 	return (ret);
475 }
476 
477 /*
478  * sa_path_is_zfs(path)
479  *
480  * Check to see if the file system path represents is of type "zfs".
481  */
482 
483 int
484 sa_path_is_zfs(char *path)
485 {
486 	char *fstype;
487 	int ret = 0;
488 
489 	fstype = sa_fstype(path);
490 	if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
491 	    ret = 1;
492 	}
493 	if (fstype != NULL)
494 	    sa_free_fstype(fstype);
495 	return (ret);
496 }
497