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