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