/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* default module directory */ const char *default_libfs_dir = "/usr/lib/fs"; #define FSTYP_VERSION 1 #ifndef TEXT_DOMAIN #define TEXT_DOMAIN "SYS_TEST" #endif typedef struct fstyp_ops { int (*fstyp_init)(int fd, off64_t offset, fstyp_mod_handle_t *handle); void (*fstyp_fini)(fstyp_mod_handle_t handle); int (*fstyp_ident)(fstyp_mod_handle_t handle); int (*fstyp_get_attr)(fstyp_mod_handle_t handle, nvlist_t **attr); int (*fstyp_dump)(fstyp_mod_handle_t handle, FILE *fout, FILE *ferr); } fstyp_ops_t; typedef struct fstyp_module { struct fstyp_module *next; char fsname[FSTYPSZ + 1]; char *pathname; /* absolute module pathname */ void *dl_handle; /* can be NULL if not loaded */ fstyp_ops_t ops; fstyp_mod_handle_t mod_handle; } fstyp_module_t; struct fstyp_handle { char *libfs_dir; /* directory to look for modules */ char *module_dir; /* specific module directory */ fstyp_module_t *modules; /* list of modules */ fstyp_module_t *modules_tail; /* last module in the list */ fstyp_module_t *ident; /* identified module */ int fd; off64_t offset; long name_max; }; #define NELEM(a) sizeof (a) / sizeof (*(a)) /* local functions */ static int fstyp_ident_all(struct fstyp_handle *h, const char **ident); static int fstyp_ident_one(struct fstyp_handle *h, const char *fsname, const char **ident); static fstyp_module_t *fstyp_find_module_by_name(struct fstyp_handle *h, const char *fsname); static int fstyp_init_module(struct fstyp_handle *h, char *mdir, char *fsname, fstyp_module_t **mpp); static void fstyp_fini_module(struct fstyp_handle *h, fstyp_module_t *mp); static int fstyp_init_all_modules(struct fstyp_handle *h); static void fstyp_fini_all_modules(struct fstyp_handle *h); static int fstyp_load_module(struct fstyp_handle *h, fstyp_module_t *mp); static void fstyp_unload_module(struct fstyp_handle *h, fstyp_module_t *); /* * Locate and initialize all modules. * If 'module_dir' is specified, only initialize module from this dir. */ int fstyp_init(int fd, off64_t offset, char *module_dir, fstyp_handle_t *handle) { struct fstyp_handle *h; int error; if ((h = calloc(1, sizeof (struct fstyp_handle))) == NULL) { return (FSTYP_ERR_NOMEM); } if ((module_dir != NULL) && ((h->module_dir = strdup(module_dir)) == NULL)) { free(h); return (FSTYP_ERR_NOMEM); } h->fd = fd; h->offset = offset; h->libfs_dir = (char *)default_libfs_dir; if ((h->name_max = pathconf(h->libfs_dir, _PC_NAME_MAX)) < 0) { h->name_max = MAXNAMELEN; } h->name_max++; if (h->module_dir == NULL) { error = fstyp_init_all_modules(h); } else { error = fstyp_init_module(h, h->module_dir, "", NULL); } if (error != 0) { fstyp_fini(h); return (error); } *handle = h; return (0); } void fstyp_fini(struct fstyp_handle *h) { if (h != NULL) { fstyp_fini_all_modules(h); if (h->module_dir != NULL) { free(h->module_dir); } free(h); } } /* * Identify the filesystem, return result in 'ident'. * If 'fsname' is specified, only attempt that filesystem. */ int fstyp_ident(struct fstyp_handle *h, const char *fsname, const char **ident) { if (fsname == NULL) { return (fstyp_ident_all(h, ident)); } else { return (fstyp_ident_one(h, fsname, ident)); } } /* * use all modules for identification */ static int fstyp_ident_all(struct fstyp_handle *h, const char **ident) { fstyp_module_t *mp; if (h->ident != NULL) { *ident = &h->ident->fsname[0]; return (0); } for (mp = h->modules; mp != NULL; mp = mp->next) { if ((fstyp_load_module(h, mp) == 0) && (mp->ops.fstyp_ident(mp->mod_handle) == 0)) { if (h->ident != NULL) { h->ident = NULL; *ident = NULL; return (FSTYP_ERR_MULT_MATCH); } else { h->ident = mp; *ident = &mp->fsname[0]; return (0); } } } return (FSTYP_ERR_NO_MATCH); } /* * use only the specified module for identification */ static int fstyp_ident_one(struct fstyp_handle *h, const char *fsname, const char **ident) { fstyp_module_t *mp; int error = FSTYP_ERR_NO_MATCH; if (h->ident != NULL) { if (strcmp(h->ident->fsname, fsname) == 0) { *ident = (char *)fsname; return (0); } else { return (FSTYP_ERR_NO_MATCH); } } if (strlen(fsname) > FSTYPSZ) { return (FSTYP_ERR_NAME_TOO_LONG); } if (h->module_dir == NULL) { mp = fstyp_find_module_by_name(h, fsname); } else { mp = h->modules; } if (mp == NULL) { return (FSTYP_ERR_MOD_NOT_FOUND); } if (((error = fstyp_load_module(h, mp)) == 0) && ((error = mp->ops.fstyp_ident(mp->mod_handle)) == 0)) { h->ident = mp; *ident = (char *)fsname; return (0); } return (error); } /* * Get the list of fs attributes. */ int fstyp_get_attr(struct fstyp_handle *h, nvlist_t **attr) { fstyp_module_t *mp = h->ident; if (mp == NULL) { return (FSTYP_ERR_NO_MATCH); } return (mp->ops.fstyp_get_attr(mp->mod_handle, attr)); } /* * Dump free-form filesystem information. */ int fstyp_dump(struct fstyp_handle *h, FILE *fout, FILE *ferr) { fstyp_module_t *mp = h->ident; if (mp == NULL) { return (FSTYP_ERR_NO_MATCH); } if (mp->ops.fstyp_dump == NULL) { return (FSTYP_ERR_NOP); } return (mp->ops.fstyp_dump(mp->mod_handle, fout, ferr)); } /* ARGSUSED */ const char * fstyp_strerror(struct fstyp_handle *h, int error) { char *str; switch (error) { case FSTYP_ERR_OK: str = dgettext(TEXT_DOMAIN, "success"); break; case FSTYP_ERR_NO_MATCH: str = dgettext(TEXT_DOMAIN, "no matches"); break; case FSTYP_ERR_MULT_MATCH: str = dgettext(TEXT_DOMAIN, "multiple matches"); break; case FSTYP_ERR_HANDLE: str = dgettext(TEXT_DOMAIN, "invalid handle"); break; case FSTYP_ERR_OFFSET: str = dgettext(TEXT_DOMAIN, "invalid or unsupported offset"); break; case FSTYP_ERR_NO_PARTITION: str = dgettext(TEXT_DOMAIN, "partition not found"); break; case FSTYP_ERR_NOP: str = dgettext(TEXT_DOMAIN, "no such operation"); break; case FSTYP_ERR_DEV_OPEN: str = dgettext(TEXT_DOMAIN, "cannot open device"); break; case FSTYP_ERR_IO: str = dgettext(TEXT_DOMAIN, "i/o error"); break; case FSTYP_ERR_NOMEM: str = dgettext(TEXT_DOMAIN, "out of memory"); break; case FSTYP_ERR_MOD_NOT_FOUND: str = dgettext(TEXT_DOMAIN, "module not found"); break; case FSTYP_ERR_MOD_DIR_OPEN: str = dgettext(TEXT_DOMAIN, "cannot open module directory"); break; case FSTYP_ERR_MOD_OPEN: str = dgettext(TEXT_DOMAIN, "cannot open module"); break; case FSTYP_ERR_MOD_VERSION: str = dgettext(TEXT_DOMAIN, "invalid module version"); break; case FSTYP_ERR_MOD_INVALID: str = dgettext(TEXT_DOMAIN, "invalid module"); break; case FSTYP_ERR_NAME_TOO_LONG: str = dgettext(TEXT_DOMAIN, "filesystem name too long"); break; default: str = dgettext(TEXT_DOMAIN, "undefined error"); break; } return (str); } static fstyp_module_t * fstyp_find_module_by_name(struct fstyp_handle *h, const char *fsname) { fstyp_module_t *mp; for (mp = h->modules; mp != NULL; mp = mp->next) { if (strcmp(mp->fsname, fsname) == 0) { return (mp); } } return (NULL); } /* * Allocate and initialize module structure. Do not load just yet. * A pointer to the existing module is returned, if such is found. */ static int fstyp_init_module(struct fstyp_handle *h, char *mdir, char *fsname, fstyp_module_t **mpp) { char *pathname; struct stat sb; fstyp_module_t *mp; /* if it's already inited, just return the pointer */ if ((mp = fstyp_find_module_by_name(h, fsname)) != NULL) { if (mpp != NULL) { *mpp = mp; } return (0); } /* allocate pathname buffer */ if ((pathname = calloc(1, h->name_max)) == NULL) { return (FSTYP_ERR_NOMEM); } /* locate module */ (void) snprintf(pathname, h->name_max, "%s/fstyp.so.%d", mdir, FSTYP_VERSION); if (stat(pathname, &sb) < 0) { return (FSTYP_ERR_MOD_NOT_FOUND); } if ((mp = calloc(1, sizeof (fstyp_module_t))) == NULL) { free(pathname); return (FSTYP_ERR_NOMEM); } mp->pathname = pathname; (void) strlcpy(mp->fsname, fsname, sizeof (mp->fsname)); /* append to list */ if (h->modules_tail == NULL) { h->modules = h->modules_tail = mp; } else { h->modules_tail->next = mp; h->modules_tail = mp; } if (mpp != NULL) { *mpp = mp; } return (0); } /* * Free module resources. NOTE: this does not update the module list. */ static void fstyp_fini_module(struct fstyp_handle *h, fstyp_module_t *mp) { if (h->ident == mp) { h->ident = NULL; } fstyp_unload_module(h, mp); if (mp->pathname != NULL) { free(mp->pathname); } free(mp); } /* * Look for .so's and save them in the list. */ static int fstyp_init_all_modules(struct fstyp_handle *h) { char *mdir; DIR *dirp; struct dirent *dp_mem, *dp; if ((mdir = calloc(1, h->name_max)) == NULL) { return (FSTYP_ERR_NOMEM); } dp = dp_mem = calloc(1, sizeof (struct dirent) + h->name_max + 1); if (dp == NULL) { return (FSTYP_ERR_NOMEM); } if ((dirp = opendir(h->libfs_dir)) == NULL) { free(mdir); free(dp_mem); return (FSTYP_ERR_MOD_DIR_OPEN); } while ((readdir_r(dirp, dp, &dp) == 0) && (dp != NULL)) { if (dp->d_name[0] == '.') { continue; } (void) snprintf(mdir, h->name_max, "%s/%s", h->libfs_dir, dp->d_name); (void) fstyp_init_module(h, mdir, dp->d_name, NULL); } free(mdir); free(dp_mem); (void) closedir(dirp); return (0); } static void fstyp_fini_all_modules(struct fstyp_handle *h) { fstyp_module_t *mp, *mp_next; for (mp = h->modules; mp != NULL; mp = mp_next) { mp_next = mp->next; fstyp_fini_module(h, mp); } h->modules = h->modules_tail = h->ident = NULL; } /* * Load the .so module. */ static int fstyp_load_module(struct fstyp_handle *h, fstyp_module_t *mp) { int error; if (mp->dl_handle != NULL) { return (0); } if ((mp->dl_handle = dlopen(mp->pathname, RTLD_LAZY)) == NULL) { return (FSTYP_ERR_MOD_OPEN); } mp->ops.fstyp_init = (int (*)(int, off64_t, fstyp_mod_handle_t *)) dlsym(mp->dl_handle, "fstyp_mod_init"); mp->ops.fstyp_fini = (void (*)(fstyp_mod_handle_t)) dlsym(mp->dl_handle, "fstyp_mod_fini"); mp->ops.fstyp_ident = (int (*)(fstyp_mod_handle_t)) dlsym(mp->dl_handle, "fstyp_mod_ident"); mp->ops.fstyp_get_attr = (int (*)(fstyp_mod_handle_t, nvlist_t **)) dlsym(mp->dl_handle, "fstyp_mod_get_attr"); mp->ops.fstyp_dump = (int (*)(fstyp_mod_handle_t, FILE *, FILE *)) dlsym(mp->dl_handle, "fstyp_mod_dump"); if (((mp->ops.fstyp_init) == NULL) || ((mp->ops.fstyp_fini) == NULL) || ((mp->ops.fstyp_ident) == NULL) || ((mp->ops.fstyp_get_attr) == NULL)) { fstyp_unload_module(h, mp); return (FSTYP_ERR_MOD_INVALID); } error = mp->ops.fstyp_init(h->fd, h->offset, &mp->mod_handle); if (error != 0) { fstyp_unload_module(h, mp); return (error); } return (0); } /*ARGSUSED*/ static void fstyp_unload_module(struct fstyp_handle *h, fstyp_module_t *mp) { if (mp->mod_handle != NULL) { mp->ops.fstyp_fini(mp->mod_handle); mp->mod_handle = NULL; } if (mp->dl_handle != NULL) { (void) dlclose(mp->dl_handle); mp->dl_handle = NULL; } }