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