xref: /illumos-gate/usr/src/lib/libfstyp/common/libfstyp.c (revision 2a6e99a0f1f7d22c0396e8b2ce9b9babbd1056cf)
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