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 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <libintl.h> 31 #include <errno.h> 32 #include <strings.h> 33 #include <unistd.h> 34 #include <sys/types.h> 35 #include <sys/stat.h> 36 #include <sys/param.h> 37 #include <sys/fstyp.h> 38 #include <fcntl.h> 39 #include <errno.h> 40 #include <dirent.h> 41 #include <dlfcn.h> 42 #include <link.h> 43 #include <libnvpair.h> 44 #include <libfstyp.h> 45 #include <libfstyp_module.h> 46 47 /* default module directory */ 48 const char *default_libfs_dir = "/usr/lib/fs"; 49 50 #define FSTYP_VERSION 1 51 52 #ifndef TEXT_DOMAIN 53 #define TEXT_DOMAIN "SYS_TEST" 54 #endif 55 56 typedef struct fstyp_ops { 57 int (*fstyp_init)(int fd, off64_t offset, 58 fstyp_mod_handle_t *handle); 59 void (*fstyp_fini)(fstyp_mod_handle_t handle); 60 int (*fstyp_ident)(fstyp_mod_handle_t handle); 61 int (*fstyp_get_attr)(fstyp_mod_handle_t handle, 62 nvlist_t **attr); 63 int (*fstyp_dump)(fstyp_mod_handle_t handle, 64 FILE *fout, FILE *ferr); 65 } fstyp_ops_t; 66 67 typedef struct fstyp_module { 68 struct fstyp_module *next; 69 char fsname[FSTYPSZ + 1]; 70 char *pathname; /* absolute module pathname */ 71 void *dl_handle; /* can be NULL if not loaded */ 72 fstyp_ops_t ops; 73 fstyp_mod_handle_t mod_handle; 74 } fstyp_module_t; 75 76 struct fstyp_handle { 77 char *libfs_dir; /* directory to look for modules */ 78 char *module_dir; /* specific module directory */ 79 fstyp_module_t *modules; /* list of modules */ 80 fstyp_module_t *modules_tail; /* last module in the list */ 81 fstyp_module_t *ident; /* identified module */ 82 int fd; 83 off64_t offset; 84 long name_max; 85 }; 86 87 #define NELEM(a) sizeof (a) / sizeof (*(a)) 88 89 /* local functions */ 90 static int fstyp_ident_all(struct fstyp_handle *h, const char **ident); 91 static int fstyp_ident_one(struct fstyp_handle *h, const char *fsname, 92 const char **ident); 93 static fstyp_module_t *fstyp_find_module_by_name(struct fstyp_handle *h, 94 const char *fsname); 95 static int fstyp_init_module(struct fstyp_handle *h, 96 char *mdir, char *fsname, fstyp_module_t **mpp); 97 static void fstyp_fini_module(struct fstyp_handle *h, 98 fstyp_module_t *mp); 99 static int fstyp_init_all_modules(struct fstyp_handle *h); 100 static void fstyp_fini_all_modules(struct fstyp_handle *h); 101 static int fstyp_load_module(struct fstyp_handle *h, 102 fstyp_module_t *mp); 103 static void fstyp_unload_module(struct fstyp_handle *h, 104 fstyp_module_t *); 105 106 /* 107 * Locate and initialize all modules. 108 * If 'module_dir' is specified, only initialize module from this dir. 109 */ 110 int 111 fstyp_init(int fd, off64_t offset, char *module_dir, fstyp_handle_t *handle) 112 { 113 struct fstyp_handle *h; 114 int error; 115 116 if ((h = calloc(1, sizeof (struct fstyp_handle))) == NULL) { 117 return (FSTYP_ERR_NOMEM); 118 } 119 if ((module_dir != NULL) && 120 ((h->module_dir = strdup(module_dir)) == NULL)) { 121 free(h); 122 return (FSTYP_ERR_NOMEM); 123 } 124 125 h->fd = fd; 126 h->offset = offset; 127 h->libfs_dir = (char *)default_libfs_dir; 128 129 if ((h->name_max = pathconf(h->libfs_dir, _PC_NAME_MAX)) < 0) { 130 h->name_max = MAXNAMELEN; 131 } 132 h->name_max++; 133 134 if (h->module_dir == NULL) { 135 error = fstyp_init_all_modules(h); 136 } else { 137 error = fstyp_init_module(h, h->module_dir, "", NULL); 138 } 139 if (error != 0) { 140 fstyp_fini(h); 141 return (error); 142 } 143 144 *handle = h; 145 return (0); 146 } 147 148 void 149 fstyp_fini(struct fstyp_handle *h) 150 { 151 if (h != NULL) { 152 fstyp_fini_all_modules(h); 153 if (h->module_dir != NULL) { 154 free(h->module_dir); 155 } 156 free(h); 157 } 158 } 159 160 /* 161 * Identify the filesystem, return result in 'ident'. 162 * If 'fsname' is specified, only attempt that filesystem. 163 */ 164 int 165 fstyp_ident(struct fstyp_handle *h, const char *fsname, const char **ident) 166 { 167 if (fsname == NULL) { 168 return (fstyp_ident_all(h, ident)); 169 } else { 170 return (fstyp_ident_one(h, fsname, ident)); 171 } 172 } 173 174 /* 175 * use all modules for identification 176 */ 177 static int 178 fstyp_ident_all(struct fstyp_handle *h, const char **ident) 179 { 180 fstyp_module_t *mp; 181 182 if (h->ident != NULL) { 183 *ident = &h->ident->fsname[0]; 184 return (0); 185 } 186 187 for (mp = h->modules; mp != NULL; mp = mp->next) { 188 if ((fstyp_load_module(h, mp) == 0) && 189 (mp->ops.fstyp_ident(mp->mod_handle) == 0)) { 190 if (h->ident != NULL) { 191 h->ident = NULL; 192 *ident = NULL; 193 return (FSTYP_ERR_MULT_MATCH); 194 } else { 195 h->ident = mp; 196 *ident = &mp->fsname[0]; 197 return (0); 198 } 199 } 200 } 201 return (FSTYP_ERR_NO_MATCH); 202 } 203 204 /* 205 * use only the specified module for identification 206 */ 207 static int 208 fstyp_ident_one(struct fstyp_handle *h, const char *fsname, const char **ident) 209 { 210 fstyp_module_t *mp; 211 int error = FSTYP_ERR_NO_MATCH; 212 213 if (h->ident != NULL) { 214 if (strcmp(h->ident->fsname, fsname) == 0) { 215 *ident = (char *)fsname; 216 return (0); 217 } else { 218 return (FSTYP_ERR_NO_MATCH); 219 } 220 } 221 222 if (strlen(fsname) > FSTYPSZ) { 223 return (FSTYP_ERR_NAME_TOO_LONG); 224 } 225 if (h->module_dir == NULL) { 226 mp = fstyp_find_module_by_name(h, fsname); 227 } else { 228 mp = h->modules; 229 } 230 if (mp == NULL) { 231 return (FSTYP_ERR_MOD_NOT_FOUND); 232 } 233 234 if (((error = fstyp_load_module(h, mp)) == 0) && 235 ((error = mp->ops.fstyp_ident(mp->mod_handle)) == 0)) { 236 h->ident = mp; 237 *ident = (char *)fsname; 238 return (0); 239 } 240 return (error); 241 } 242 243 /* 244 * Get the list of fs attributes. 245 */ 246 int 247 fstyp_get_attr(struct fstyp_handle *h, nvlist_t **attr) 248 { 249 fstyp_module_t *mp = h->ident; 250 251 if (mp == NULL) { 252 return (FSTYP_ERR_NO_MATCH); 253 } 254 255 return (mp->ops.fstyp_get_attr(mp->mod_handle, attr)); 256 } 257 258 /* 259 * Dump free-form filesystem information. 260 */ 261 int 262 fstyp_dump(struct fstyp_handle *h, FILE *fout, FILE *ferr) 263 { 264 fstyp_module_t *mp = h->ident; 265 266 if (mp == NULL) { 267 return (FSTYP_ERR_NO_MATCH); 268 } 269 270 if (mp->ops.fstyp_dump == NULL) { 271 return (FSTYP_ERR_NOP); 272 } 273 274 return (mp->ops.fstyp_dump(mp->mod_handle, fout, ferr)); 275 } 276 277 /* ARGSUSED */ 278 const char * 279 fstyp_strerror(struct fstyp_handle *h, int error) 280 { 281 char *str; 282 283 switch (error) { 284 case FSTYP_ERR_OK: 285 str = dgettext(TEXT_DOMAIN, "success"); 286 break; 287 case FSTYP_ERR_NO_MATCH: 288 str = dgettext(TEXT_DOMAIN, "no matches"); 289 break; 290 case FSTYP_ERR_MULT_MATCH: 291 str = dgettext(TEXT_DOMAIN, "multiple matches"); 292 break; 293 case FSTYP_ERR_HANDLE: 294 str = dgettext(TEXT_DOMAIN, "invalid handle"); 295 break; 296 case FSTYP_ERR_OFFSET: 297 str = dgettext(TEXT_DOMAIN, "invalid or unsupported offset"); 298 break; 299 case FSTYP_ERR_NO_PARTITION: 300 str = dgettext(TEXT_DOMAIN, "partition not found"); 301 break; 302 case FSTYP_ERR_NOP: 303 str = dgettext(TEXT_DOMAIN, "no such operation"); 304 break; 305 case FSTYP_ERR_DEV_OPEN: 306 str = dgettext(TEXT_DOMAIN, "cannot open device"); 307 break; 308 case FSTYP_ERR_IO: 309 str = dgettext(TEXT_DOMAIN, "i/o error"); 310 break; 311 case FSTYP_ERR_NOMEM: 312 str = dgettext(TEXT_DOMAIN, "out of memory"); 313 break; 314 case FSTYP_ERR_MOD_NOT_FOUND: 315 str = dgettext(TEXT_DOMAIN, "module not found"); 316 break; 317 case FSTYP_ERR_MOD_DIR_OPEN: 318 str = dgettext(TEXT_DOMAIN, "cannot open module directory"); 319 break; 320 case FSTYP_ERR_MOD_OPEN: 321 str = dgettext(TEXT_DOMAIN, "cannot open module"); 322 break; 323 case FSTYP_ERR_MOD_VERSION: 324 str = dgettext(TEXT_DOMAIN, "invalid module version"); 325 break; 326 case FSTYP_ERR_MOD_INVALID: 327 str = dgettext(TEXT_DOMAIN, "invalid module"); 328 break; 329 case FSTYP_ERR_NAME_TOO_LONG: 330 str = dgettext(TEXT_DOMAIN, "filesystem name too long"); 331 break; 332 default: 333 str = dgettext(TEXT_DOMAIN, "undefined error"); 334 break; 335 } 336 337 return (str); 338 } 339 340 341 static fstyp_module_t * 342 fstyp_find_module_by_name(struct fstyp_handle *h, const char *fsname) 343 { 344 fstyp_module_t *mp; 345 346 for (mp = h->modules; mp != NULL; mp = mp->next) { 347 if (strcmp(mp->fsname, fsname) == 0) { 348 return (mp); 349 } 350 } 351 return (NULL); 352 } 353 354 /* 355 * Allocate and initialize module structure. Do not load just yet. 356 * A pointer to the existing module is returned, if such is found. 357 */ 358 static int 359 fstyp_init_module(struct fstyp_handle *h, char *mdir, char *fsname, 360 fstyp_module_t **mpp) 361 { 362 char *pathname; 363 struct stat sb; 364 fstyp_module_t *mp; 365 366 /* if it's already inited, just return the pointer */ 367 if ((mp = fstyp_find_module_by_name(h, fsname)) != NULL) { 368 if (mpp != NULL) { 369 *mpp = mp; 370 } 371 return (0); 372 } 373 374 /* allocate pathname buffer */ 375 if ((pathname = calloc(1, h->name_max)) == NULL) { 376 return (FSTYP_ERR_NOMEM); 377 } 378 379 /* locate module */ 380 (void) snprintf(pathname, h->name_max, "%s/fstyp.so.%d", mdir, 381 FSTYP_VERSION); 382 if (stat(pathname, &sb) < 0) { 383 return (FSTYP_ERR_MOD_NOT_FOUND); 384 } 385 386 if ((mp = calloc(1, sizeof (fstyp_module_t))) == NULL) { 387 free(pathname); 388 return (FSTYP_ERR_NOMEM); 389 } 390 391 mp->pathname = pathname; 392 (void) strlcpy(mp->fsname, fsname, sizeof (mp->fsname)); 393 394 /* append to list */ 395 if (h->modules_tail == NULL) { 396 h->modules = h->modules_tail = mp; 397 } else { 398 h->modules_tail->next = mp; 399 h->modules_tail = mp; 400 } 401 402 if (mpp != NULL) { 403 *mpp = mp; 404 } 405 return (0); 406 } 407 408 /* 409 * Free module resources. NOTE: this does not update the module list. 410 */ 411 static void 412 fstyp_fini_module(struct fstyp_handle *h, fstyp_module_t *mp) 413 { 414 if (h->ident == mp) { 415 h->ident = NULL; 416 } 417 fstyp_unload_module(h, mp); 418 if (mp->pathname != NULL) { 419 free(mp->pathname); 420 } 421 free(mp); 422 } 423 424 /* 425 * Look for .so's and save them in the list. 426 */ 427 static int 428 fstyp_init_all_modules(struct fstyp_handle *h) 429 { 430 char *mdir; 431 DIR *dirp; 432 struct dirent *dp_mem, *dp; 433 434 if ((mdir = calloc(1, h->name_max)) == NULL) { 435 return (FSTYP_ERR_NOMEM); 436 } 437 dp = dp_mem = calloc(1, sizeof (struct dirent) + h->name_max + 1); 438 if (dp == NULL) { 439 return (FSTYP_ERR_NOMEM); 440 } 441 if ((dirp = opendir(h->libfs_dir)) == NULL) { 442 free(mdir); 443 free(dp_mem); 444 return (FSTYP_ERR_MOD_DIR_OPEN); 445 } 446 447 while ((readdir_r(dirp, dp, &dp) == 0) && (dp != NULL)) { 448 if (dp->d_name[0] == '.') { 449 continue; 450 } 451 (void) snprintf(mdir, h->name_max, 452 "%s/%s", h->libfs_dir, dp->d_name); 453 (void) fstyp_init_module(h, mdir, dp->d_name, NULL); 454 } 455 456 free(mdir); 457 free(dp_mem); 458 (void) closedir(dirp); 459 return (0); 460 } 461 462 static void 463 fstyp_fini_all_modules(struct fstyp_handle *h) 464 { 465 fstyp_module_t *mp, *mp_next; 466 467 for (mp = h->modules; mp != NULL; mp = mp_next) { 468 mp_next = mp->next; 469 fstyp_fini_module(h, mp); 470 } 471 h->modules = h->modules_tail = h->ident = NULL; 472 } 473 474 475 /* 476 * Load the .so module. 477 */ 478 static int 479 fstyp_load_module(struct fstyp_handle *h, fstyp_module_t *mp) 480 { 481 int error; 482 483 if (mp->dl_handle != NULL) { 484 return (0); 485 } 486 487 if ((mp->dl_handle = dlopen(mp->pathname, RTLD_LAZY)) == NULL) { 488 return (FSTYP_ERR_MOD_OPEN); 489 } 490 491 mp->ops.fstyp_init = (int (*)(int, off64_t, fstyp_mod_handle_t *)) 492 dlsym(mp->dl_handle, "fstyp_mod_init"); 493 mp->ops.fstyp_fini = (void (*)(fstyp_mod_handle_t)) 494 dlsym(mp->dl_handle, "fstyp_mod_fini"); 495 mp->ops.fstyp_ident = (int (*)(fstyp_mod_handle_t)) 496 dlsym(mp->dl_handle, "fstyp_mod_ident"); 497 mp->ops.fstyp_get_attr = (int (*)(fstyp_mod_handle_t, nvlist_t **)) 498 dlsym(mp->dl_handle, "fstyp_mod_get_attr"); 499 mp->ops.fstyp_dump = (int (*)(fstyp_mod_handle_t, FILE *, FILE *)) 500 dlsym(mp->dl_handle, "fstyp_mod_dump"); 501 502 if (((mp->ops.fstyp_init) == NULL) || 503 ((mp->ops.fstyp_fini) == NULL) || 504 ((mp->ops.fstyp_ident) == NULL) || 505 ((mp->ops.fstyp_get_attr) == NULL)) { 506 fstyp_unload_module(h, mp); 507 return (FSTYP_ERR_MOD_INVALID); 508 } 509 510 error = mp->ops.fstyp_init(h->fd, h->offset, &mp->mod_handle); 511 if (error != 0) { 512 fstyp_unload_module(h, mp); 513 return (error); 514 } 515 516 return (0); 517 } 518 519 /*ARGSUSED*/ 520 static void 521 fstyp_unload_module(struct fstyp_handle *h, fstyp_module_t *mp) 522 { 523 if (mp->mod_handle != NULL) { 524 mp->ops.fstyp_fini(mp->mod_handle); 525 mp->mod_handle = NULL; 526 } 527 if (mp->dl_handle != NULL) { 528 (void) dlclose(mp->dl_handle); 529 mp->dl_handle = NULL; 530 } 531 } 532