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 2009 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 <unistd.h>
30 #include <strings.h>
31 #include <string.h>
32 #include <dirent.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/param.h>
36 #include <sys/errno.h>
37 #include <limits.h>
38 #include <libnvpair.h>
39 #include <dlfcn.h>
40 #include <libintl.h>
41 #include <sys/systeminfo.h>
42 #include <sys/fs_reparse.h>
43 #include "rp_plugin.h"
44
45 #define MAXISALEN 257 /* based on sysinfo(2) man page */
46
47 static rp_proto_handle_t rp_proto_handle;
48 static rp_proto_plugin_t *rp_proto_list;
49
50 int rp_plugin_init(void);
51 static void proto_plugin_fini(void);
52 static rp_plugin_ops_t *rp_find_protocol(const char *svctype);
53
54 extern int errno;
55 static int rp_plugin_inited = 0;
56
57 /*
58 * reparse_create()
59 *
60 * Create a symlink at the specified 'path' as a reparse point.
61 * This function will fail if path refers to an existing file system
62 * object or an object named string already exists at the given path.
63 *
64 * return 0 if ok else return error code.
65 */
66 int
reparse_create(const char * path,const char * data)67 reparse_create(const char *path, const char *data)
68 {
69 int err;
70 struct stat sbuf;
71
72 if (path == NULL || data == NULL)
73 return (EINVAL);
74
75 if ((err = reparse_validate(data)) != 0)
76 return (err);
77
78 /* check if object exists */
79 if (lstat(path, &sbuf) == 0)
80 return (EEXIST);
81
82 return (symlink(data, path) ? errno : 0);
83 }
84
85 /*
86 * reparse_unparse()
87 *
88 * Convert an nvlist back to a string format suitable to write
89 * to the reparse point symlink body. The string returned is in
90 * allocated memory and must be freed by the caller.
91 *
92 * return 0 if ok else return error code.
93 */
94 int
reparse_unparse(nvlist_t * nvl,char ** stringp)95 reparse_unparse(nvlist_t *nvl, char **stringp)
96 {
97 int err, buflen;
98 char *buf, *stype, *val;
99 nvpair_t *curr;
100
101 if (nvl == NULL || stringp == NULL ||
102 ((curr = nvlist_next_nvpair(nvl, NULL)) == NULL))
103 return (EINVAL);
104
105 buflen = SYMLINK_MAX;
106 if ((buf = malloc(buflen)) == NULL)
107 return (ENOMEM);
108
109 err = 0;
110 (void) snprintf(buf, buflen, "%s", FS_REPARSE_TAG_STR);
111 while (curr != NULL) {
112 if (!(stype = nvpair_name(curr))) {
113 err = EINVAL;
114 break;
115 }
116 if ((strlcat(buf, FS_TOKEN_START_STR, buflen) >= buflen) ||
117 (strlcat(buf, stype, buflen) >= buflen) ||
118 (strlcat(buf, ":", buflen) >= buflen) ||
119 (nvpair_value_string(curr, &val) != 0) ||
120 (strlcat(buf, val, buflen) >= buflen) ||
121 (strlcat(buf, FS_TOKEN_END_STR, buflen) >= buflen)) {
122 err = E2BIG;
123 break;
124 }
125 curr = nvlist_next_nvpair(nvl, curr);
126 }
127 if (err != 0) {
128 free(buf);
129 return (err);
130 }
131 if (strlcat(buf, FS_REPARSE_TAG_END_STR, buflen) >= buflen) {
132 free(buf);
133 return (E2BIG);
134 }
135
136 *stringp = buf;
137 return (0);
138 }
139
140 /*
141 * reparse_deref()
142 *
143 * Accepts the service-specific item from the reparse point and returns
144 * the service-specific data requested. The caller specifies the size
145 * of the buffer provided via *bufsz.
146 *
147 * if ok return 0 and *bufsz is updated to contain the actual length of
148 * the returned results, else return error code. If the error code is
149 * EOVERFLOW; results do not fit in the buffer, *bufsz will be updated
150 * to contain the number of bytes needed to hold the results.
151 */
152 int
reparse_deref(const char * svc_type,const char * svc_data,char * buf,size_t * bufsz)153 reparse_deref(const char *svc_type, const char *svc_data, char *buf,
154 size_t *bufsz)
155 {
156 rp_plugin_ops_t *ops;
157
158 if ((svc_type == NULL) || (svc_data == NULL) || (buf == NULL) ||
159 (bufsz == NULL))
160 return (EINVAL);
161
162 ops = rp_find_protocol(svc_type);
163 if ((ops != NULL) && (ops->rpo_deref != NULL))
164 return (ops->rpo_deref(svc_type, svc_data, buf, bufsz));
165
166 /* no plugin, return error */
167 return (ENOTSUP);
168 }
169
170 /*
171 * reparse_delete()
172 *
173 * Delete a reparse point at a given pathname. It will fail if
174 * a reparse point does not exist at the given path or the pathname
175 * is not a symlink.
176 *
177 * return 0 if ok else return error code.
178 */
179 int
reparse_delete(const char * path)180 reparse_delete(const char *path)
181 {
182 struct stat sbuf;
183
184 if (path == NULL)
185 return (EINVAL);
186
187 /* check if object exists */
188 if (lstat(path, &sbuf) != 0)
189 return (errno);
190
191 if ((sbuf.st_mode & S_IFLNK) != S_IFLNK)
192 return (EINVAL);
193
194 return (unlink(path) ? errno : 0);
195 }
196
197 /*
198 * reparse_add()
199 *
200 * Add a service type entry to a nvlist with a copy of svc_data,
201 * replacing one of the same type if already present.
202 *
203 * return 0 if ok else return error code.
204 */
205 int
reparse_add(nvlist_t * nvl,const char * svc_type,const char * svc_data)206 reparse_add(nvlist_t *nvl, const char *svc_type, const char *svc_data)
207 {
208 int err;
209 char *buf;
210 size_t bufsz;
211 rp_plugin_ops_t *ops;
212
213 if ((nvl == NULL) || (svc_type == NULL) || (svc_data == NULL))
214 return (EINVAL);
215
216 bufsz = SYMLINK_MAX; /* no need to mess around */
217 if ((buf = malloc(bufsz)) == NULL)
218 return (ENOMEM);
219
220 ops = rp_find_protocol(svc_type);
221 if ((ops != NULL) && (ops->rpo_form != NULL))
222 err = ops->rpo_form(svc_type, svc_data, buf, &bufsz);
223 else
224 err = ENOTSUP; /* no plugin */
225
226 if (err != 0) {
227 free(buf);
228 return (err);
229 }
230
231 err = nvlist_add_string(nvl, svc_type, buf);
232 free(buf);
233 return (err);
234 }
235
236 /*
237 * reparse_remove()
238 *
239 * Remove a service type entry from the nvlist, if present.
240 *
241 * return 0 if ok else return error code.
242 */
243 int
reparse_remove(nvlist_t * nvl,const char * svc_type)244 reparse_remove(nvlist_t *nvl, const char *svc_type)
245 {
246 if ((nvl == NULL) || (svc_type == NULL))
247 return (EINVAL);
248
249 return (nvlist_remove_all(nvl, svc_type));
250 }
251
252 /*
253 * Returns true if name is "." or "..", otherwise returns false.
254 */
255 static boolean_t
rp_is_dot_or_dotdot(const char * name)256 rp_is_dot_or_dotdot(const char *name)
257 {
258 if (*name != '.')
259 return (B_FALSE);
260
261 if (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))
262 return (B_TRUE);
263
264 return (B_FALSE);
265 }
266
267 static void
proto_plugin_fini()268 proto_plugin_fini()
269 {
270 rp_proto_plugin_t *p;
271
272 /*
273 * Protocols may call this framework during _fini
274 */
275 for (p = rp_proto_list; p != NULL; p = p->plugin_next) {
276 if (p->plugin_ops->rpo_fini)
277 p->plugin_ops->rpo_fini();
278 }
279 while ((p = rp_proto_list) != NULL) {
280 rp_proto_list = p->plugin_next;
281 if (p->plugin_handle != NULL)
282 (void) dlclose(p->plugin_handle);
283 free(p);
284 }
285
286 if (rp_proto_handle.rp_ops != NULL) {
287 free(rp_proto_handle.rp_ops);
288 rp_proto_handle.rp_ops = NULL;
289 }
290 rp_proto_handle.rp_num_proto = 0;
291 }
292
293 /*
294 * rp_plugin_init()
295 *
296 * Initialize the service type specific plugin modules.
297 * For each reparse service type, there should be a plugin library for it.
298 * This function walks /usr/lib/reparse directory for plugin libraries.
299 * For each plugin library found, initialize it and add it to the internal
300 * list of service type plugin. These are used for service type specific
301 * operations.
302 */
303 int
rp_plugin_init()304 rp_plugin_init()
305 {
306 int err, ret = RP_OK;
307 char isa[MAXISALEN], dirpath[MAXPATHLEN], path[MAXPATHLEN];
308 int num_protos = 0;
309 rp_proto_handle_t *rp_hdl;
310 rp_proto_plugin_t *proto, *tmp;
311 rp_plugin_ops_t *plugin_ops;
312 struct stat st;
313 void *dlhandle;
314 DIR *dir;
315 struct dirent *dent;
316
317 #if defined(_LP64)
318 if (sysinfo(SI_ARCHITECTURE_64, isa, MAXISALEN) == -1)
319 isa[0] = '\0';
320 #else
321 isa[0] = '\0';
322 #endif
323
324 (void) snprintf(dirpath, MAXPATHLEN,
325 "%s/%s", RP_LIB_DIR, isa);
326
327 if ((dir = opendir(dirpath)) == NULL)
328 return (RP_NO_PLUGIN_DIR);
329
330 while ((dent = readdir(dir)) != NULL) {
331 if (rp_is_dot_or_dotdot(dent->d_name))
332 continue;
333
334 (void) snprintf(path, MAXPATHLEN,
335 "%s/%s", dirpath, dent->d_name);
336
337 /*
338 * If file doesn't exist, don't try to map it
339 */
340 if (stat(path, &st) < 0)
341 continue;
342 if ((dlhandle = dlopen(path, RTLD_FIRST|RTLD_LAZY)) == NULL)
343 continue;
344
345 plugin_ops = (rp_plugin_ops_t *)
346 dlsym(dlhandle, "rp_plugin_ops");
347 if (plugin_ops == NULL) {
348 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
349 "Error in plugin ops for service type %s\n%s\n"),
350 dent->d_name, dlerror());
351 (void) dlclose(dlhandle);
352 continue;
353 }
354 proto = (rp_proto_plugin_t *)
355 calloc(1, sizeof (rp_proto_plugin_t));
356 if (proto == NULL) {
357 (void) dlclose(dlhandle);
358 (void) fprintf(stderr,
359 dgettext(TEXT_DOMAIN, "No memory for plugin %s\n"),
360 dent->d_name);
361 ret = RP_NO_MEMORY;
362 break;
363 }
364
365 proto->plugin_ops = plugin_ops;
366 proto->plugin_handle = dlhandle;
367 num_protos++;
368 proto->plugin_next = rp_proto_list;
369 rp_proto_list = proto;
370 }
371
372 (void) closedir(dir);
373
374 if ((num_protos == 0) && (ret == 0))
375 ret = RP_NO_PLUGIN;
376 /*
377 * There was an error, so cleanup prior to return of failure.
378 */
379 if (ret != RP_OK) {
380 proto_plugin_fini();
381 return (ret);
382 }
383
384 rp_proto_handle.rp_ops = (rp_plugin_ops_t **)calloc(num_protos,
385 sizeof (rp_plugin_ops_t *));
386 if (!rp_proto_handle.rp_ops) {
387 proto_plugin_fini();
388 return (RP_NO_MEMORY);
389 }
390
391 rp_hdl = &rp_proto_handle;
392 rp_hdl->rp_num_proto = 0;
393 for (tmp = rp_proto_list; rp_hdl->rp_num_proto < num_protos &&
394 tmp != NULL; tmp = tmp->plugin_next) {
395
396 err = RP_OK;
397 if (tmp->plugin_ops->rpo_init != NULL)
398 err = tmp->plugin_ops->rpo_init();
399 if (err != RP_OK)
400 continue;
401 rp_hdl->rp_ops[rp_hdl->rp_num_proto++] = tmp->plugin_ops;
402 }
403
404 return (rp_hdl->rp_num_proto > 0 ? RP_OK : RP_NO_PLUGIN);
405 }
406
407
408 /*
409 * find_protocol()
410 *
411 * Search the plugin list for the specified protocol and return the
412 * ops vector. return NULL if protocol is not defined.
413 */
414 static rp_plugin_ops_t *
rp_find_protocol(const char * svc_type)415 rp_find_protocol(const char *svc_type)
416 {
417 int i;
418 rp_plugin_ops_t *ops = NULL;
419
420 if (svc_type == NULL)
421 return (NULL);
422
423 if (rp_plugin_inited == 0) {
424 if (rp_plugin_init() == RP_OK)
425 rp_plugin_inited = 1;
426 else
427 return (NULL);
428 }
429
430 for (i = 0; i < rp_proto_handle.rp_num_proto; i++) {
431 ops = rp_proto_handle.rp_ops[i];
432 if (ops->rpo_supports_svc(svc_type))
433 return (ops);
434
435 }
436 return (NULL);
437 }
438