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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
33 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/errno.h>
38 #include <libnvpair.h>
41 #include <sys/systeminfo.h>
42 #include <sys/fs_reparse.h>
43 #include "rp_plugin.h"
45 #define MAXISALEN 257 /* based on sysinfo(2) man page */
47 static rp_proto_handle_t rp_proto_handle
;
48 static rp_proto_plugin_t
*rp_proto_list
;
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
);
55 static int rp_plugin_inited
= 0;
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.
64 * return 0 if ok else return error code.
67 reparse_create(const char *path
, const char *data
)
72 if (path
== NULL
|| data
== NULL
)
75 if ((err
= reparse_validate(data
)) != 0)
78 /* check if object exists */
79 if (lstat(path
, &sbuf
) == 0)
82 return (symlink(data
, path
) ? errno
: 0);
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.
92 * return 0 if ok else return error code.
95 reparse_unparse(nvlist_t
*nvl
, char **stringp
)
98 char *buf
, *stype
, *val
;
101 if (nvl
== NULL
|| stringp
== NULL
||
102 ((curr
= nvlist_next_nvpair(nvl
, NULL
)) == NULL
))
105 buflen
= SYMLINK_MAX
;
106 if ((buf
= malloc(buflen
)) == NULL
)
110 (void) snprintf(buf
, buflen
, "%s", FS_REPARSE_TAG_STR
);
111 while (curr
!= NULL
) {
112 if (!(stype
= nvpair_name(curr
))) {
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
)) {
125 curr
= nvlist_next_nvpair(nvl
, curr
);
131 if (strlcat(buf
, FS_REPARSE_TAG_END_STR
, buflen
) >= buflen
) {
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.
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.
153 reparse_deref(const char *svc_type
, const char *svc_data
, char *buf
,
156 rp_plugin_ops_t
*ops
;
158 if ((svc_type
== NULL
) || (svc_data
== NULL
) || (buf
== NULL
) ||
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
));
166 /* no plugin, return error */
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
177 * return 0 if ok else return error code.
180 reparse_delete(const char *path
)
187 /* check if object exists */
188 if (lstat(path
, &sbuf
) != 0)
191 if ((sbuf
.st_mode
& S_IFLNK
) != S_IFLNK
)
194 return (unlink(path
) ? errno
: 0);
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.
203 * return 0 if ok else return error code.
206 reparse_add(nvlist_t
*nvl
, const char *svc_type
, const char *svc_data
)
211 rp_plugin_ops_t
*ops
;
213 if ((nvl
== NULL
) || (svc_type
== NULL
) || (svc_data
== NULL
))
216 bufsz
= SYMLINK_MAX
; /* no need to mess around */
217 if ((buf
= malloc(bufsz
)) == NULL
)
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
);
224 err
= ENOTSUP
; /* no plugin */
231 err
= nvlist_add_string(nvl
, svc_type
, buf
);
239 * Remove a service type entry from the nvlist, if present.
241 * return 0 if ok else return error code.
244 reparse_remove(nvlist_t
*nvl
, const char *svc_type
)
246 if ((nvl
== NULL
) || (svc_type
== NULL
))
249 return (nvlist_remove_all(nvl
, svc_type
));
253 * Returns true if name is "." or "..", otherwise returns false.
256 rp_is_dot_or_dotdot(const char *name
)
261 if (name
[1] == '\0' || (name
[1] == '.' && name
[2] == '\0'))
270 rp_proto_plugin_t
*p
;
273 * Protocols may call this framework during _fini
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();
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
);
286 if (rp_proto_handle
.rp_ops
!= NULL
) {
287 free(rp_proto_handle
.rp_ops
);
288 rp_proto_handle
.rp_ops
= NULL
;
290 rp_proto_handle
.rp_num_proto
= 0;
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
306 int err
, ret
= RP_OK
;
307 char isa
[MAXISALEN
], dirpath
[MAXPATHLEN
], path
[MAXPATHLEN
];
309 rp_proto_handle_t
*rp_hdl
;
310 rp_proto_plugin_t
*proto
, *tmp
;
311 rp_plugin_ops_t
*plugin_ops
;
318 if (sysinfo(SI_ARCHITECTURE_64
, isa
, MAXISALEN
) == -1)
324 (void) snprintf(dirpath
, MAXPATHLEN
,
325 "%s/%s", RP_LIB_DIR
, isa
);
327 if ((dir
= opendir(dirpath
)) == NULL
)
328 return (RP_NO_PLUGIN_DIR
);
330 while ((dent
= readdir(dir
)) != NULL
) {
331 if (rp_is_dot_or_dotdot(dent
->d_name
))
334 (void) snprintf(path
, MAXPATHLEN
,
335 "%s/%s", dirpath
, dent
->d_name
);
338 * If file doesn't exist, don't try to map it
340 if (stat(path
, &st
) < 0)
342 if ((dlhandle
= dlopen(path
, RTLD_FIRST
|RTLD_LAZY
)) == NULL
)
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
);
354 proto
= (rp_proto_plugin_t
*)
355 calloc(1, sizeof (rp_proto_plugin_t
));
357 (void) dlclose(dlhandle
);
358 (void) fprintf(stderr
,
359 dgettext(TEXT_DOMAIN
, "No memory for plugin %s\n"),
365 proto
->plugin_ops
= plugin_ops
;
366 proto
->plugin_handle
= dlhandle
;
368 proto
->plugin_next
= rp_proto_list
;
369 rp_proto_list
= proto
;
372 (void) closedir(dir
);
374 if ((num_protos
== 0) && (ret
== 0))
377 * There was an error, so cleanup prior to return of failure.
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
) {
388 return (RP_NO_MEMORY
);
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
) {
397 if (tmp
->plugin_ops
->rpo_init
!= NULL
)
398 err
= tmp
->plugin_ops
->rpo_init();
401 rp_hdl
->rp_ops
[rp_hdl
->rp_num_proto
++] = tmp
->plugin_ops
;
404 return (rp_hdl
->rp_num_proto
> 0 ? RP_OK
: RP_NO_PLUGIN
);
411 * Search the plugin list for the specified protocol and return the
412 * ops vector. return NULL if protocol is not defined.
414 static rp_plugin_ops_t
*
415 rp_find_protocol(const char *svc_type
)
418 rp_plugin_ops_t
*ops
= NULL
;
420 if (svc_type
== NULL
)
423 if (rp_plugin_inited
== 0) {
424 if (rp_plugin_init() == RP_OK
)
425 rp_plugin_inited
= 1;
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
))