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 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 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 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 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 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 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 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 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 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 * 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 }