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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <libshare.h>
31 #include "libshare_impl.h"
32 #include <dlfcn.h>
33 #include <link.h>
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/stat.h>
37 #include <dirent.h>
38 #include <libintl.h>
39 #include <sys/systeminfo.h>
40 #include <thread.h>
41 #include <synch.h>
42 
43 #define	MAXISALEN	257	/* based on sysinfo(2) man page */
44 
45 /*
46  * protocol plugin interface
47  *
48  * finds plugins and makes them accessible. This is only "used" by
49  * libshare.so.
50  */
51 
52 struct sa_proto_plugin *sap_proto_list;
53 
54 static struct sa_proto_handle sa_proto_handle;
55 
56 void proto_plugin_fini();
57 
58 /*
59  * Returns true if name is "." or "..", otherwise returns false.
60  */
61 static boolean_t
proto_is_dot_or_dotdot(const char * name)62 proto_is_dot_or_dotdot(const char *name)
63 {
64 	if (*name != '.')
65 		return (B_FALSE);
66 
67 	if ((name[1] == '\0') || (name[1] == '.' && name[2] == '\0'))
68 		return (B_TRUE);
69 
70 	return (B_FALSE);
71 }
72 
73 /*
74  * proto_plugin_init()
75  *
76  * Initialize the protocol specific plugin modules.
77  *
78  * Walk /usr/lib/fs/\* for libshare_*.so modules, for example,
79  * /usr/lib/fs/nfs/libshare_nfs.so. A protocol specific directory
80  * would have modules with names of the form libshare_<proto>.so.
81  * For each protocol found, initialize it and add it to the internal
82  * list of protocols. These are used for protocol specific operations.
83  */
84 
85 int
proto_plugin_init()86 proto_plugin_init()
87 {
88 	struct sa_proto_plugin *proto;
89 	int num_protos = 0;
90 	struct sa_plugin_ops *plugin_ops;
91 	void *dlhandle;
92 	DIR *dir;
93 	struct dirent *dent;
94 	int ret = SA_OK;
95 	struct stat st;
96 	char isa[MAXISALEN];
97 
98 #if defined(_LP64)
99 	if (sysinfo(SI_ARCHITECTURE_64, isa, MAXISALEN) == -1)
100 		isa[0] = '\0';
101 #else
102 	isa[0] = '\0';
103 #endif
104 
105 	if ((dir = opendir(SA_LIB_DIR)) == NULL)
106 		return (SA_OK);
107 
108 	while ((dent = readdir(dir)) != NULL) {
109 		char path[MAXPATHLEN];
110 
111 		if (proto_is_dot_or_dotdot(dent->d_name))
112 			continue;
113 
114 		(void) snprintf(path, MAXPATHLEN,
115 		    "%s/%s/%s/libshare_%s.so.1", SA_LIB_DIR,
116 		    dent->d_name, isa, dent->d_name);
117 
118 		/*
119 		 * If file doesn't exist, don't try to map it
120 		 */
121 		if (stat(path, &st) < 0)
122 			continue;
123 
124 		if ((dlhandle = dlopen(path, RTLD_FIRST|RTLD_LAZY)) == NULL) {
125 			(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
126 			    "Error in plugin for protocol %s: %s\n"),
127 			    dent->d_name, dlerror());
128 			continue;
129 		}
130 
131 		plugin_ops = (struct sa_plugin_ops *)
132 		    dlsym(dlhandle, "sa_plugin_ops");
133 		if (plugin_ops == NULL) {
134 			(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
135 			    "Error in plugin ops for protocol %s: %s\n"),
136 			    dent->d_name, dlerror());
137 			(void) dlclose(dlhandle);
138 			continue;
139 		}
140 
141 		proto = (struct sa_proto_plugin *)
142 		    calloc(1, sizeof (struct sa_proto_plugin));
143 		if (proto == NULL) {
144 			(void) dlclose(dlhandle);
145 			ret = SA_NO_MEMORY;
146 			continue;
147 		}
148 
149 		proto->plugin_ops = plugin_ops;
150 		proto->plugin_handle = dlhandle;
151 		num_protos++;
152 		proto->plugin_next = sap_proto_list;
153 		sap_proto_list = proto;
154 	}
155 
156 	(void) closedir(dir);
157 
158 	if (num_protos != 0) {
159 		sa_proto_handle.sa_proto =
160 		    (char **)calloc(num_protos, sizeof (char *));
161 		sa_proto_handle.sa_ops =
162 		    (struct sa_plugin_ops **)calloc(num_protos,
163 		    sizeof (struct sa_plugin_ops *));
164 		if (sa_proto_handle.sa_proto != NULL &&
165 		    sa_proto_handle.sa_ops != NULL) {
166 			int i;
167 			struct sa_proto_plugin *tmp;
168 
169 			for (i = 0, tmp = sap_proto_list;
170 			    i < num_protos && tmp != NULL;
171 			    tmp = tmp->plugin_next) {
172 				int err;
173 				err = SA_OK;
174 				if (tmp->plugin_ops->sa_init != NULL)
175 					err = tmp->plugin_ops->sa_init();
176 				if (err == SA_OK) {
177 					/*
178 					 * Only include if the init
179 					 * succeeded or was NULL
180 					 */
181 					sa_proto_handle.sa_num_proto++;
182 					sa_proto_handle.sa_ops[i] =
183 					    tmp->plugin_ops;
184 					sa_proto_handle.sa_proto[i] =
185 					    tmp->plugin_ops->sa_protocol;
186 					i++;
187 				}
188 			}
189 		} else {
190 			ret = SA_NO_MEMORY;
191 		}
192 	}
193 
194 	/*
195 	 * There was an error, so cleanup prior to return of failure.
196 	 */
197 	if (ret != SA_OK)
198 		proto_plugin_fini();
199 
200 	return (ret);
201 }
202 
203 /*
204  * proto_plugin_fini()
205  *
206  * Uninitialize all the plugin modules.
207  */
208 
209 void
proto_plugin_fini()210 proto_plugin_fini()
211 {
212 	struct sa_proto_plugin *p;
213 
214 	/*
215 	 * Protocols may call this framework during _fini
216 	 * (the smbfs plugin is known to do this) so do
217 	 * two passes: 1st call _fini; 2nd free, dlclose.
218 	 */
219 	for (p = sap_proto_list; p != NULL; p = p->plugin_next)
220 		p->plugin_ops->sa_fini();
221 
222 	while ((p = sap_proto_list) != NULL) {
223 		sap_proto_list = p->plugin_next;
224 
225 		if (p->plugin_handle != NULL)
226 			(void) dlclose(p->plugin_handle);
227 		free(p);
228 	}
229 	if (sa_proto_handle.sa_ops != NULL) {
230 		free(sa_proto_handle.sa_ops);
231 		sa_proto_handle.sa_ops = NULL;
232 	}
233 	if (sa_proto_handle.sa_proto != NULL) {
234 		free(sa_proto_handle.sa_proto);
235 		sa_proto_handle.sa_proto = NULL;
236 	}
237 	sa_proto_handle.sa_num_proto = 0;
238 }
239 
240 /*
241  * find_protocol(proto)
242  *
243  * Search the plugin list for the specified protocol and return the
244  * ops vector.  NULL if protocol is not defined.
245  */
246 
247 static struct sa_plugin_ops *
find_protocol(char * proto)248 find_protocol(char *proto)
249 {
250 	int i;
251 	struct sa_plugin_ops *ops = NULL;
252 	extern mutex_t sa_global_lock;
253 
254 	(void) mutex_lock(&sa_global_lock);
255 	if (proto != NULL) {
256 		for (i = 0; i < sa_proto_handle.sa_num_proto; i++) {
257 			if (strcmp(proto, sa_proto_handle.sa_proto[i]) == 0) {
258 				ops = sa_proto_handle.sa_ops[i];
259 				break;
260 			}
261 		}
262 	}
263 	(void) mutex_unlock(&sa_global_lock);
264 	return (ops);
265 }
266 
267 /*
268  * sa_proto_share(proto, share)
269  *
270  * Activate a share for the specified protocol.
271  */
272 
273 int
sa_proto_share(char * proto,sa_share_t share)274 sa_proto_share(char *proto, sa_share_t share)
275 {
276 	struct sa_plugin_ops *ops = find_protocol(proto);
277 	int ret = SA_INVALID_PROTOCOL;
278 
279 	if (ops != NULL && ops->sa_share != NULL)
280 		ret = ops->sa_share(share);
281 	return (ret);
282 }
283 
284 /*
285  * sa_proto_unshare(proto, share)
286  *
287  * Deactivate (unshare) the share for this protocol.
288  */
289 
290 int
sa_proto_unshare(sa_share_t share,char * proto,char * path)291 sa_proto_unshare(sa_share_t share, char *proto, char *path)
292 {
293 	struct sa_plugin_ops *ops = find_protocol(proto);
294 	int ret = SA_INVALID_PROTOCOL;
295 
296 	if (ops != NULL && ops->sa_unshare != NULL)
297 		ret = ops->sa_unshare(share, path);
298 	return (ret);
299 }
300 
301 /*
302  * sa_proto_share_resource(char *proto, sa_resource_t resource)
303  *
304  * For protocols that actually enable at the resource level, do the
305  * protocol specific resource enable. If it doesn't, return an error.
306  * Note that the resource functions are optional so can return
307  * SA_NOT_SUPPORTED.
308  */
309 
310 int
sa_proto_share_resource(char * proto,sa_resource_t resource)311 sa_proto_share_resource(char *proto, sa_resource_t resource)
312 {
313 	struct sa_plugin_ops *ops = find_protocol(proto);
314 	int ret = SA_INVALID_PROTOCOL;
315 
316 	if (ops != NULL) {
317 		if (ops->sa_enable_resource != NULL)
318 			ret = ops->sa_enable_resource(resource);
319 		else
320 			ret = SA_NOT_SUPPORTED;
321 	}
322 	return (ret);
323 }
324 
325 /*
326  * sa_proto_unshare_resource(char *proto, sa_resource_t resource)
327  *
328  * For protocols that actually disable at the resource level, do the
329  * protocol specific resource disable. If it doesn't, return an error.
330  */
331 
332 int
sa_proto_unshare_resource(char * proto,sa_resource_t resource)333 sa_proto_unshare_resource(char *proto, sa_resource_t resource)
334 {
335 	struct sa_plugin_ops *ops = find_protocol(proto);
336 	int ret = SA_INVALID_PROTOCOL;
337 
338 	if (ops != NULL) {
339 		if (ops->sa_disable_resource != NULL)
340 			ret = ops->sa_disable_resource(resource);
341 		else
342 			ret = SA_NOT_SUPPORTED;
343 	}
344 	return (ret);
345 }
346 
347 /*
348  * sa_proto_valid_prop(handle, proto, prop, opt)
349  *
350  * Check to see if the specified prop is valid for this protocol.
351  */
352 
353 int
sa_proto_valid_prop(sa_handle_t handle,char * proto,sa_property_t prop,sa_optionset_t opt)354 sa_proto_valid_prop(sa_handle_t handle, char *proto, sa_property_t prop,
355     sa_optionset_t opt)
356 {
357 	struct sa_plugin_ops *ops = find_protocol(proto);
358 	int ret = 0;
359 
360 	if (ops != NULL && ops->sa_valid_prop != NULL)
361 		ret = ops->sa_valid_prop(handle, prop, opt);
362 	return (ret);
363 }
364 
365 /*
366  * sa_proto_valid_space(proto, space)
367  *
368  * Check if space is valid optionspace for proto.
369  * Protocols that don't implement this don't support spaces.
370  */
371 int
sa_proto_valid_space(char * proto,char * token)372 sa_proto_valid_space(char *proto, char *token)
373 {
374 	struct sa_plugin_ops *ops = find_protocol(proto);
375 	int ret = 0;
376 
377 	if (ops != NULL && ops->sa_valid_space != NULL)
378 		ret = ops->sa_valid_space(token);
379 	return (ret);
380 }
381 
382 /*
383  * sa_proto_space_alias(proto, space)
384  *
385  * If the name for space is an alias, return its proper name.  This is
386  * used to translate "default" values into proper form.
387  */
388 char *
sa_proto_space_alias(char * proto,char * space)389 sa_proto_space_alias(char *proto, char *space)
390 {
391 	struct sa_plugin_ops *ops = find_protocol(proto);
392 	char *ret = space;
393 
394 	if (ops != NULL && ops->sa_space_alias != NULL)
395 		ret = ops->sa_space_alias(space);
396 	return (ret);
397 }
398 
399 /*
400  * sa_proto_security_prop(proto, token)
401  *
402  * Check to see if the property name in token is a valid named
403  * optionset property.
404  */
405 
406 int
sa_proto_security_prop(char * proto,char * token)407 sa_proto_security_prop(char *proto, char *token)
408 {
409 	struct sa_plugin_ops *ops = find_protocol(proto);
410 	int ret = 0;
411 
412 	if (ops != NULL && ops->sa_security_prop != NULL)
413 		ret = ops->sa_security_prop(token);
414 	return (ret);
415 }
416 
417 /*
418  * sa_proto_legacy_opts(proto, grouup, options)
419  *
420  * Have the protocol specific parser parse the options string and add
421  * an appropriate optionset to group.
422  */
423 
424 int
sa_proto_legacy_opts(char * proto,sa_group_t group,char * options)425 sa_proto_legacy_opts(char *proto, sa_group_t group, char *options)
426 {
427 	struct sa_plugin_ops *ops = find_protocol(proto);
428 	int ret = SA_INVALID_PROTOCOL;
429 
430 	if (ops != NULL && ops->sa_legacy_opts != NULL)
431 		ret = ops->sa_legacy_opts(group, options);
432 	return (ret);
433 }
434 
435 /*
436  * sa_proto_legacy_format(proto, group, hier)
437  *
438  * Return a legacy format string representing either the group's
439  * properties or the groups hierarchical properties.
440  */
441 
442 char *
sa_proto_legacy_format(char * proto,sa_group_t group,int hier)443 sa_proto_legacy_format(char *proto, sa_group_t group, int hier)
444 {
445 	struct sa_plugin_ops *ops = find_protocol(proto);
446 	char *ret = NULL;
447 
448 	if (ops != NULL && ops->sa_legacy_format != NULL)
449 		ret = ops->sa_legacy_format(group, hier);
450 	return (ret);
451 }
452 
453 void
sa_format_free(char * str)454 sa_format_free(char *str)
455 {
456 	free(str);
457 }
458 
459 /*
460  * sharectl related API functions
461  */
462 
463 /*
464  * sa_proto_get_properties(proto)
465  *
466  * Return the set of properties that are specific to the
467  * protocol. These are usually in /etc/dfs/<proto> and related files,
468  * but only the protocol module knows which ones for sure.
469  */
470 
471 sa_protocol_properties_t
sa_proto_get_properties(char * proto)472 sa_proto_get_properties(char *proto)
473 {
474 	struct sa_plugin_ops *ops = find_protocol(proto);
475 	sa_protocol_properties_t props = NULL;
476 
477 	if (ops != NULL && ops->sa_get_proto_set != NULL)
478 		props = ops->sa_get_proto_set();
479 	return (props);
480 }
481 
482 /*
483  * sa_proto_set_property(proto, prop)
484  *
485  * Update the protocol specific property.
486  */
487 
488 int
sa_proto_set_property(char * proto,sa_property_t prop)489 sa_proto_set_property(char *proto, sa_property_t prop)
490 {
491 	struct sa_plugin_ops *ops = find_protocol(proto);
492 	int ret = SA_OK;
493 
494 	if (ops != NULL && ops->sa_set_proto_prop != NULL)
495 		ret = ops->sa_set_proto_prop(prop);
496 	return (ret);
497 }
498 
499 /*
500  * sa_valid_protocol(proto)
501  *
502  * Check to see if the protocol specified is defined by a
503  * plugin. Returns true (1) or false (0)
504  */
505 
506 int
sa_valid_protocol(char * proto)507 sa_valid_protocol(char *proto)
508 {
509 	struct sa_plugin_ops *ops = find_protocol(proto);
510 	return (ops != NULL);
511 }
512 
513 /*
514  * Return the current operational status of the protocol
515  */
516 
517 char *
sa_get_protocol_status(char * proto)518 sa_get_protocol_status(char *proto)
519 {
520 	struct sa_plugin_ops *ops = find_protocol(proto);
521 	char *ret = NULL;
522 	if (ops != NULL && ops->sa_get_proto_status != NULL)
523 		ret = ops->sa_get_proto_status(proto);
524 	return (ret);
525 }
526 
527 /*
528  * sa_proto_update_legacy(proto, share)
529  *
530  * Update the protocol specific legacy files if necessary for the
531  * specified share.
532  */
533 
534 int
sa_proto_update_legacy(char * proto,sa_share_t share)535 sa_proto_update_legacy(char *proto, sa_share_t share)
536 {
537 	struct sa_plugin_ops *ops = find_protocol(proto);
538 	int ret = SA_NOT_IMPLEMENTED;
539 
540 	if (ops != NULL) {
541 		if (ops->sa_update_legacy != NULL)
542 			ret = ops->sa_update_legacy(share);
543 	}
544 	return (ret);
545 }
546 
547 /*
548  * sa_delete_legacy(proto, share)
549  *
550  * Remove the specified share from the protocol specific legacy files.
551  */
552 
553 int
sa_proto_delete_legacy(char * proto,sa_share_t share)554 sa_proto_delete_legacy(char *proto, sa_share_t share)
555 {
556 	struct sa_plugin_ops *ops = find_protocol(proto);
557 	int ret = SA_NOT_IMPLEMENTED;
558 
559 	if (ops != NULL) {
560 		if (ops->sa_delete_legacy != NULL)
561 			ret = ops->sa_delete_legacy(share);
562 	} else {
563 		if (proto != NULL)
564 			ret = SA_NOT_IMPLEMENTED;
565 		else
566 			ret = SA_INVALID_PROTOCOL;
567 	}
568 	return (ret);
569 }
570 
571 /*
572  * sa_proto_delete_section(proto, section)
573  *
574  * Remove the specified section from the protocol specific legacy files,
575  * if supported.
576  */
577 
578 int
sa_proto_delete_section(char * proto,char * section)579 sa_proto_delete_section(char *proto, char *section)
580 {
581 	struct sa_plugin_ops *ops = find_protocol(proto);
582 	int ret = SA_OK;
583 
584 	if (ops != NULL) {
585 		if (ops->sa_delete_proto_section != NULL)
586 			ret = ops->sa_delete_proto_section(section);
587 	} else {
588 		if (proto != NULL)
589 			ret = SA_NOT_IMPLEMENTED;
590 		else
591 			ret = SA_INVALID_PROTOCOL;
592 	}
593 	return (ret);
594 }
595 
596 /*
597  * sa_proto_change_notify(share, char *protocol)
598  *
599  * Notify the protocol that a change has been made to the share
600  */
601 
602 int
sa_proto_change_notify(sa_share_t share,char * proto)603 sa_proto_change_notify(sa_share_t share, char *proto)
604 {
605 	struct sa_plugin_ops *ops = find_protocol(proto);
606 	int ret = SA_NOT_IMPLEMENTED;
607 
608 	if (ops != NULL) {
609 		if (ops->sa_change_notify != NULL)
610 			ret = ops->sa_change_notify(share);
611 	} else	if (proto == NULL) {
612 
613 			ret = SA_INVALID_PROTOCOL;
614 	}
615 	return (ret);
616 }
617 
618 /*
619  * sa_proto_notify_resource(resource, char *protocol)
620  *
621  * Notify the protocol that a change has been made to the share
622  */
623 
624 int
sa_proto_notify_resource(sa_resource_t resource,char * proto)625 sa_proto_notify_resource(sa_resource_t resource, char *proto)
626 {
627 	struct sa_plugin_ops *ops = find_protocol(proto);
628 	int ret = SA_NOT_IMPLEMENTED;
629 
630 	if (ops != NULL) {
631 		if (ops->sa_notify_resource != NULL)
632 			ret = ops->sa_notify_resource(resource);
633 	} else if (proto == NULL) {
634 			ret = SA_INVALID_PROTOCOL;
635 	}
636 	return (ret);
637 }
638 
639 /*
640  * sa_proto_get_featureset(protocol)
641  *
642  * Get bitmask of defined features of the protocol. These are
643  * primarily things like SA_FEATURE_RESOURCE (shares are by resource
644  * name rather than path) and other operational features that affect
645  * behavior.
646  */
647 
648 uint64_t
sa_proto_get_featureset(char * proto)649 sa_proto_get_featureset(char *proto)
650 {
651 	struct sa_plugin_ops *ops = find_protocol(proto);
652 	uint64_t ret = 0;
653 
654 	if (ops != NULL) {
655 		if (ops->sa_features != NULL)
656 			ret = ops->sa_features();
657 	}
658 	/* if not implemented, zero is valid */
659 	return (ret);
660 }
661 
662 /*
663  * sa_proto_get_transients(sa_handle_t)
664  *
665  * Called to get any protocol specific transient shares.  NFS doesn't
666  * use this since the info is in sharetab which is processed as a
667  * common transient store.
668  *
669  * The protocol plugin should verify that the share isn't in the
670  * repository and then add it as a transient.
671  *
672  * Not having an entry is not a problem. It returns 0 in that case.
673  */
674 
675 int
sa_proto_get_transients(sa_handle_t handle,char * proto)676 sa_proto_get_transients(sa_handle_t handle, char *proto)
677 {
678 	struct sa_plugin_ops *ops = find_protocol(proto);
679 	int ret = 0;
680 
681 	if (ops != NULL) {
682 		if (ops->sa_get_transient_shares != NULL)
683 			ret = ops->sa_get_transient_shares(handle);
684 	}
685 	return (ret);
686 }
687 
688 /*
689  * sa_proto_rename_resource(sa_handle_t, proto, sa_resource_t, newname)
690  *
691  * Protocols may need to know when a resource has changed names in
692  * order to notify clients. This must be done "before" the name in the
693  * resource has been changed. Not being implemented is not a problem.
694  */
695 
696 int
sa_proto_rename_resource(sa_handle_t handle,char * proto,sa_resource_t resource,char * newname)697 sa_proto_rename_resource(sa_handle_t handle, char *proto,
698     sa_resource_t resource, char *newname)
699 {
700 	struct sa_plugin_ops *ops = find_protocol(proto);
701 	int ret = SA_OK;
702 
703 	if (ops != NULL) {
704 		if (ops->sa_rename_resource != NULL)
705 			ret = ops->sa_rename_resource(handle, resource,
706 			    newname);
707 	}
708 	return (ret);
709 }
710