xref: /titanic_51/usr/src/lib/libshare/common/libshare_zfs.c (revision 8dffef91737b06b306a05685fdd319dbf21e38f2)
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  * Note that all ZFS shares are in a subgroup of the top level group
156  * called "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 
176 	    /* make sure this is flagged as a ZFS group */
177 	    if (group != NULL)
178 		ret = sa_set_group_attr(group, "zfs", "true");
179 	}
180 	if (group != NULL) {
181 	    if (proto != NULL) {
182 		optionset = sa_get_optionset(group, proto);
183 		if (optionset == NULL) {
184 		    optionset = sa_create_optionset(group, proto);
185 		} else {
186 		    char **protolist;
187 		    int numprotos, i;
188 		    numprotos = sa_get_protocols(&protolist);
189 		    for (i = 0; i < numprotos; i++) {
190 			optionset = sa_create_optionset(group, protolist[i]);
191 		    }
192 		    if (protolist != NULL)
193 			free(protolist);
194 		}
195 	    }
196 	}
197 	if (err != NULL)
198 	    *err = ret;
199 	return (group);
200 }
201 
202 /*
203  * find_or_create_zfs_subgroup(groupname, optstring, *err)
204  *
205  * ZFS shares will be in a subgroup of the "zfs" master group.  This
206  * function looks to see if the groupname exists and returns it if it
207  * does or else creates a new one with the specified name and returns
208  * that.  The "zfs" group will exist before we get here, but we make
209  * sure just in case.
210  *
211  * err must be a valid pointer.
212  */
213 
214 static sa_group_t
215 find_or_create_zfs_subgroup(char *groupname, char *optstring, int *err)
216 {
217 	sa_group_t group = NULL;
218 	sa_group_t zfs;
219 	char *name;
220 	char *options;
221 
222 	/* start with the top-level "zfs" group */
223 	zfs = sa_get_group("zfs");
224 	*err = SA_OK;
225 	if (zfs != NULL) {
226 	    for (group = sa_get_sub_group(zfs); group != NULL;
227 		group = sa_get_next_group(group)) {
228 		name = sa_get_group_attr(group, "name");
229 		if (name != NULL && strcmp(name, groupname) == 0) {
230 		    /* have the group so break out of here */
231 		    sa_free_attr_string(name);
232 		    break;
233 		}
234 		if (name != NULL)
235 		    sa_free_attr_string(name);
236 	    }
237 
238 	    if (group == NULL) {
239 		/* need to create the sub-group since it doesn't exist */
240 		group = _sa_create_zfs_group(zfs, groupname);
241 		if (group != NULL) {
242 		    set_node_attr(group, "zfs", "true");
243 		}
244 		if (strcmp(optstring, "on") == 0)
245 		    optstring = "rw";
246 		if (group != NULL) {
247 		    options = strdup(optstring);
248 		    if (options != NULL) {
249 			*err = sa_parse_legacy_options(group, options, "nfs");
250 			free(options);
251 		    } else {
252 			*err = SA_NO_MEMORY;
253 		    }
254 		}
255 	    }
256 	}
257 	return (group);
258 }
259 
260 /*
261  * sa_get_zfs_shares(groupname)
262  *
263  * Walk the mnttab for all zfs mounts and determine which are
264  * shared. Find or create the appropriate group/sub-group to contain
265  * the shares.
266  *
267  * All shares are in a sub-group that will hold the properties. This
268  * allows representing the inherited property model.
269  */
270 
271 int
272 sa_get_zfs_shares(char *groupname)
273 {
274 	sa_group_t group;
275 	sa_group_t zfsgroup;
276 	int legacy = 0;
277 	int err;
278 	fs_mntlist_t *list;
279 	fs_mntlist_t *cur;
280 	zfs_handle_t *handle = NULL;
281 	char shareopts[ZFS_MAXPROPLEN];
282 	sa_share_t share;
283 	zfs_source_t source;
284 	char sourcestr[ZFS_MAXPROPLEN];
285 	libzfs_handle_t *libhandle;
286 	char *options;
287 
288 	/*
289 	 * if we can't access libzfs, don't bother doing anything.
290 	 */
291 	libhandle = libzfs_init();
292 	if (libhandle == NULL)
293 	    return (SA_SYSTEM_ERR);
294 
295 	zfsgroup = find_or_create_group(groupname, "nfs", &err);
296 	if (zfsgroup != NULL) {
297 		/*
298 		 * need to walk the mounted ZFS pools and datasets to
299 		 * find shares that are possible.
300 		 */
301 	    list = fs_get_filtered_mount_list(NULL, NULL, "zfs", NULL,
302 					    NULL, 0, &err);
303 	    group = zfsgroup;
304 	    for (cur = list; cur != NULL; cur = cur->next) {
305 		handle = zfs_open(libhandle, cur->resource,
306 				    ZFS_TYPE_FILESYSTEM);
307 		if (handle != NULL) {
308 		    source = ZFS_SRC_ALL;
309 		    if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts,
310 					sizeof (shareopts), &source, sourcestr,
311 					ZFS_MAXPROPLEN,
312 					FALSE) == 0 &&
313 			strcmp(shareopts, "off") != 0) {
314 			/* it is shared so add to list */
315 			share = sa_find_share(cur->mountp);
316 			err = SA_OK;
317 			if (share != NULL) {
318 				/*
319 				 * A zfs file system had been shared
320 				 * through tradiditional methods
321 				 * (share/dfstab or added to a non-zfs
322 				 * group.  Now it has been added to a
323 				 * ZFS group via the zfs
324 				 * command. Remove from previous
325 				 * config and setup with current
326 				 * options.
327 				 */
328 			    err = sa_remove_share(share);
329 			    share = NULL;
330 			}
331 			if (err == SA_OK) {
332 			    if (source & ZFS_SRC_INHERITED) {
333 				int doshopt = 0;
334 				/*
335 				 * Need to find the "real" parent
336 				 * sub-group. It may not be mounted,
337 				 * but it was identified in the
338 				 * "sourcestr" variable. The real
339 				 * parent not mounted can occur if
340 				 * "canmount=off and sharenfs=on".
341 				 */
342 				group = find_or_create_zfs_subgroup(sourcestr,
343 								    shareopts,
344 								    &doshopt);
345 				if (group != NULL) {
346 				    share = _sa_add_share(group, cur->mountp,
347 							SA_SHARE_TRANSIENT,
348 							&err);
349 					/*
350 					 * some options may only be on
351 					 * shares. If the opt string
352 					 * contains one of those, we
353 					 * put it just on the share.
354 					 */
355 				    if (share != NULL &&
356 					doshopt == SA_PROP_SHARE_ONLY) {
357 					options = strdup(shareopts);
358 					if (options != NULL) {
359 					    err = sa_parse_legacy_options(share,
360 								options, "nfs");
361 					    free(options);
362 					}
363 				    }
364 				} else {
365 				    err = SA_NO_MEMORY;
366 				}
367 			    } else {
368 				/*
369 				 * this is a sub-group that actually
370 				 * contains the sharenfs property as
371 				 * opposed to a share that inherited.
372 				 */
373 				group = _sa_create_zfs_group(zfsgroup,
374 								cur->resource);
375 				set_node_attr(group, "zfs", "true");
376 				share = _sa_add_share(group, cur->mountp,
377 							SA_SHARE_TRANSIENT,
378 							&err);
379 				if (err == SA_OK) {
380 				    if (strcmp(shareopts, "on") != 0) {
381 					options = strdup(shareopts);
382 					if (options != NULL) {
383 					    err = sa_parse_legacy_options(group,
384 									options,
385 									"nfs");
386 					    free(options);
387 					}
388 					if (err == SA_PROP_SHARE_ONLY) {
389 					/*
390 					 * Same as above, some
391 					 * properties may only be on
392 					 * shares, but due to the ZFS
393 					 * sub-groups being
394 					 * artificial, we sometimes
395 					 * get this and have to deal
396 					 * with it. We do it by
397 					 * attempting to put it on the
398 					 * share.
399 					 */
400 					    options = strdup(shareopts);
401 					    if (options != NULL)
402 						err = sa_parse_legacy_options(
403 									share,
404 									options,
405 									"nfs");
406 					    free(options);
407 					}
408 					/* unmark the share's changed state */
409 					set_node_attr(share, "changed", NULL);
410 				    }
411 				}
412 			    }
413 			}
414 		    }
415 		}
416 	    }
417 	    if (list != NULL)
418 		fs_free_mount_list(list);
419 	}
420 	if (libhandle != NULL)
421 	    libzfs_fini(libhandle);
422 	return (legacy);
423 }
424 
425 #define	COMMAND		"/usr/sbin/zfs"
426 
427 /*
428  * sa_zfs_set_sharenfs(group, path, on)
429  *
430  * Update the "sharenfs" property on the path. If on is true, then set
431  * to the properties on the group or "on" if no properties are
432  * defined. Set to "off" if on is false.
433  */
434 
435 int
436 sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
437 {
438 	int ret = SA_NOT_IMPLEMENTED;
439 	char *command;
440 
441 	command = malloc(ZFS_MAXPROPLEN * 2);
442 	if (command != NULL) {
443 	    char *opts = NULL;
444 	    char *dataset;
445 	    FILE *pfile;
446 	    /* for now, NFS is always available for "zfs" */
447 	    if (on) {
448 		opts = sa_proto_legacy_format("nfs", group, 1);
449 		if (opts != NULL && strlen(opts) == 0) {
450 		    free(opts);
451 		    opts = strdup("on");
452 		}
453 	    }
454 	    dataset = get_zfs_dataset(path);
455 	    if (dataset != NULL) {
456 		(void) snprintf(command, ZFS_MAXPROPLEN * 2,
457 				"%s set sharenfs=\"%s\" %s", COMMAND,
458 				opts != NULL ? opts : "off",
459 				dataset);
460 		pfile = popen(command, "r");
461 		if (pfile != NULL) {
462 		    ret = pclose(pfile);
463 		    if (ret != 0)
464 			ret = SA_SYSTEM_ERR;
465 		}
466 	    }
467 	    if (opts != NULL)
468 		free(opts);
469 	    if (dataset != NULL)
470 		free(dataset);
471 	    free(command);
472 	}
473 	return (ret);
474 }
475 
476 /*
477  * sa_zfs_update(group)
478  *
479  * call back to ZFS to update the share if necessary.
480  * Don't do it if it isn't a real change.
481  */
482 int
483 sa_zfs_update(sa_group_t group)
484 {
485 	sa_optionset_t protopt;
486 	sa_group_t parent;
487 	char *command;
488 	char *optstring;
489 	int ret = SA_OK;
490 	int doupdate = 0;
491 	FILE *pfile;
492 
493 	if (sa_is_share(group))
494 	    parent = sa_get_parent_group(group);
495 	else
496 	    parent = group;
497 
498 	if (parent != NULL) {
499 	    command = malloc(ZFS_MAXPROPLEN * 2);
500 	    if (command == NULL)
501 		return (SA_NO_MEMORY);
502 
503 	    *command = '\0';
504 	    for (protopt = sa_get_optionset(parent, NULL); protopt != NULL;
505 		protopt = sa_get_next_optionset(protopt)) {
506 
507 		char *proto = sa_get_optionset_attr(protopt, "type");
508 		char *path;
509 		char *dataset = NULL;
510 		char *zfsopts = NULL;
511 
512 		if (sa_is_share(group)) {
513 		    path = sa_get_share_attr((sa_share_t)group, "path");
514 		    if (path != NULL) {
515 			dataset = get_zfs_dataset(path);
516 			sa_free_attr_string(path);
517 		    }
518 		} else {
519 		    dataset = sa_get_group_attr(group, "name");
520 		}
521 		/* update only when there is an optstring found */
522 		doupdate = 0;
523 		if (proto != NULL && dataset != NULL) {
524 		    optstring = sa_proto_legacy_format(proto, group, 1);
525 		    zfsopts = get_zfs_property(dataset, ZFS_PROP_SHARENFS);
526 
527 		    if (optstring != NULL && zfsopts != NULL) {
528 			if (strcmp(optstring, zfsopts) != 0)
529 			    doupdate++;
530 		    }
531 
532 		    if (doupdate) {
533 			if (optstring != NULL && strlen(optstring) > 0) {
534 			    (void) snprintf(command, ZFS_MAXPROPLEN * 2,
535 					    "%s set sharenfs=%s %s", COMMAND,
536 					    optstring, dataset);
537 			} else {
538 			    (void) snprintf(command, ZFS_MAXPROPLEN * 2,
539 					    "%s set sharenfs=on %s", COMMAND,
540 					    dataset);
541 			}
542 			pfile = popen(command, "r");
543 			if (pfile != NULL)
544 			    ret = pclose(pfile);
545 			switch (ret) {
546 			default:
547 			case 1:
548 			    ret = SA_SYSTEM_ERR;
549 			    break;
550 			case 2:
551 			    ret = SA_SYNTAX_ERR;
552 			    break;
553 			case 0:
554 			    break;
555 			}
556 		    }
557 		    if (optstring != NULL) {
558 			free(optstring);
559 		    }
560 		    if (zfsopts != NULL)
561 			free(zfsopts);
562 		}
563 		if (proto != NULL)
564 		    sa_free_attr_string(proto);
565 		if (dataset != NULL)
566 		    free(dataset);
567 	    }
568 	    free(command);
569 	}
570 	return (ret);
571 }
572 
573 /*
574  * sa_group_is_zfs(group)
575  *
576  * Given the group, determine if the zfs attribute is set.
577  */
578 
579 int
580 sa_group_is_zfs(sa_group_t group)
581 {
582 	char *zfs;
583 	int ret = 0;
584 
585 	zfs = sa_get_group_attr(group, "zfs");
586 	if (zfs != NULL) {
587 	    ret = 1;
588 	    sa_free_attr_string(zfs);
589 	}
590 	return (ret);
591 }
592 
593 /*
594  * sa_path_is_zfs(path)
595  *
596  * Check to see if the file system path represents is of type "zfs".
597  */
598 
599 int
600 sa_path_is_zfs(char *path)
601 {
602 	char *fstype;
603 	int ret = 0;
604 
605 	fstype = sa_fstype(path);
606 	if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
607 	    ret = 1;
608 	}
609 	if (fstype != NULL)
610 	    sa_free_fstype(fstype);
611 	return (ret);
612 }
613