Home | History | Annotate | Download | only in common
      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