xref: /illumos-gate/usr/src/cmd/fs.d/fslib.c (revision 03831d35)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include	<stdio.h>
30 #include	<stdarg.h>
31 #include	<stdlib.h>
32 #include	<unistd.h>
33 #include	<libintl.h>
34 #include	<string.h>
35 #include	<fcntl.h>
36 #include	<errno.h>
37 #include	<syslog.h>
38 #include	<alloca.h>
39 #include	<sys/vfstab.h>
40 #include	<sys/mnttab.h>
41 #include	<sys/mntent.h>
42 #include	<sys/mount.h>
43 #include	<sys/filio.h>
44 #include	<sys/fs/ufs_filio.h>
45 #include	<sys/stat.h>
46 #include	<sys/param.h>
47 #include	<zone.h>
48 #include	<signal.h>
49 #include	<strings.h>
50 #include	"fslib.h"
51 
52 /* LINTLIBRARY */
53 
54 #define	BUFLEN		256
55 
56 #define	TIME_MAX 16
57 
58 /*
59  * Reads all of the entries from the in-kernel mnttab, and returns the
60  * linked list of the entries.
61  */
62 mntlist_t *
63 fsgetmntlist(void)
64 {
65 	FILE *mfp;
66 	mntlist_t *mntl;
67 	char buf[BUFLEN];
68 
69 	if ((mfp = fopen(MNTTAB, "r")) == NULL) {
70 		(void) snprintf(buf, BUFLEN, "fsgetmntlist: fopen %s", MNTTAB);
71 		perror(buf);
72 		return (NULL);
73 	}
74 
75 	mntl = fsmkmntlist(mfp);
76 
77 	(void) fclose(mfp);
78 	return (mntl);
79 }
80 
81 
82 static struct extmnttab zmnttab = { 0 };
83 
84 struct extmnttab *
85 fsdupmnttab(struct extmnttab *mnt)
86 {
87 	struct extmnttab *new;
88 
89 	new = (struct extmnttab *)malloc(sizeof (*new));
90 	if (new == NULL)
91 		goto alloc_failed;
92 
93 	*new = zmnttab;
94 	/*
95 	 * Allocate an extra byte for the mountpoint
96 	 * name in case a space needs to be added.
97 	 */
98 	new->mnt_mountp = (char *)malloc(strlen(mnt->mnt_mountp) + 2);
99 	if (new->mnt_mountp == NULL)
100 		goto alloc_failed;
101 	(void) strcpy(new->mnt_mountp, mnt->mnt_mountp);
102 
103 	if ((new->mnt_special = strdup(mnt->mnt_special)) == NULL)
104 		goto alloc_failed;
105 
106 	if ((new->mnt_fstype = strdup(mnt->mnt_fstype)) == NULL)
107 		goto alloc_failed;
108 
109 	if (mnt->mnt_mntopts != NULL)
110 		if ((new->mnt_mntopts = strdup(mnt->mnt_mntopts)) == NULL)
111 			goto alloc_failed;
112 
113 	if (mnt->mnt_time != NULL)
114 		if ((new->mnt_time = strdup(mnt->mnt_time)) == NULL)
115 			goto alloc_failed;
116 
117 	new->mnt_major = mnt->mnt_major;
118 	new->mnt_minor = mnt->mnt_minor;
119 	return (new);
120 
121 alloc_failed:
122 	(void) fprintf(stderr, gettext("fsdupmnttab: Out of memory\n"));
123 	fsfreemnttab(new);
124 	return (NULL);
125 }
126 
127 /*
128  * Free a single mnttab structure
129  */
130 void
131 fsfreemnttab(struct extmnttab *mnt)
132 {
133 
134 	if (mnt) {
135 		if (mnt->mnt_special)
136 			free(mnt->mnt_special);
137 		if (mnt->mnt_mountp)
138 			free(mnt->mnt_mountp);
139 		if (mnt->mnt_fstype)
140 			free(mnt->mnt_fstype);
141 		if (mnt->mnt_mntopts)
142 			free(mnt->mnt_mntopts);
143 		if (mnt->mnt_time)
144 			free(mnt->mnt_time);
145 		free(mnt);
146 	}
147 }
148 
149 void
150 fsfreemntlist(mntlist_t *mntl)
151 {
152 	mntlist_t *mntl_tmp;
153 
154 	while (mntl) {
155 		fsfreemnttab(mntl->mntl_mnt);
156 		mntl_tmp = mntl;
157 		mntl = mntl->mntl_next;
158 		free(mntl_tmp);
159 	}
160 }
161 
162 /*
163  * Read the mnttab file and return it as a list of mnttab structs.
164  * Returns NULL if there was a memory failure.
165  */
166 mntlist_t *
167 fsmkmntlist(FILE *mfp)
168 {
169 	struct extmnttab 	mnt;
170 	mntlist_t 	*mhead, *mtail;
171 	int 		ret;
172 
173 	mhead = mtail = NULL;
174 
175 	resetmnttab(mfp);
176 	while ((ret = getextmntent(mfp, &mnt, sizeof (struct extmnttab)))
177 		!= -1) {
178 		mntlist_t	*mp;
179 
180 		if (ret != 0)		/* bad entry */
181 			continue;
182 
183 		mp = (mntlist_t *)malloc(sizeof (*mp));
184 		if (mp == NULL)
185 			goto alloc_failed;
186 		if (mhead == NULL)
187 			mhead = mp;
188 		else
189 			mtail->mntl_next = mp;
190 		mtail = mp;
191 		mp->mntl_next = NULL;
192 		mp->mntl_flags = 0;
193 		if ((mp->mntl_mnt = fsdupmnttab(&mnt)) == NULL)
194 			goto alloc_failed;
195 	}
196 	return (mhead);
197 
198 alloc_failed:
199 	fsfreemntlist(mhead);
200 	return (NULL);
201 }
202 
203 /*
204  * Return the last entry that matches mntin's special
205  * device and/or mountpt.
206  * Helps to be robust here, so we check for NULL pointers.
207  */
208 mntlist_t *
209 fsgetmlast(mntlist_t *ml, struct mnttab *mntin)
210 {
211 	mntlist_t 	*delete = NULL;
212 
213 	for (; ml; ml = ml->mntl_next) {
214 		if (mntin->mnt_mountp && mntin->mnt_special) {
215 			/*
216 			 * match if and only if both are equal.
217 			 */
218 			if ((strcmp(ml->mntl_mnt->mnt_mountp,
219 					mntin->mnt_mountp) == 0) &&
220 			    (strcmp(ml->mntl_mnt->mnt_special,
221 					mntin->mnt_special) == 0))
222 				delete = ml;
223 		} else if (mntin->mnt_mountp) {
224 			if (strcmp(ml->mntl_mnt->mnt_mountp,
225 					mntin->mnt_mountp) == 0)
226 				delete = ml;
227 		} else if (mntin->mnt_special) {
228 			if (strcmp(ml->mntl_mnt->mnt_special,
229 					mntin->mnt_special) == 0)
230 				delete = ml;
231 	    }
232 	}
233 	return (delete);
234 }
235 
236 
237 /*
238  * Returns the mountlevel of the pathname in cp.  As examples,
239  * / => 1, /bin => 2, /bin/ => 2, ////bin////ls => 3, sdf => 0, etc...
240  */
241 int
242 fsgetmlevel(char *cp)
243 {
244 	int	mlevel;
245 	char	*cp1;
246 
247 	if (cp == NULL || *cp == NULL || *cp != '/')
248 		return (0);	/* this should never happen */
249 
250 	mlevel = 1;			/* root (/) is the minimal case */
251 
252 	for (cp1 = cp + 1; *cp1; cp++, cp1++)
253 		if (*cp == '/' && *cp1 != '/')	/* "///" counts as 1 */
254 			mlevel++;
255 
256 	return (mlevel);
257 }
258 
259 /*
260  * Returns non-zero if string s is a member of the strings in ps.
261  */
262 int
263 fsstrinlist(const char *s, const char **ps)
264 {
265 	const char *cp;
266 	cp = *ps;
267 	while (cp) {
268 		if (strcmp(s, cp) == 0)
269 			return (1);
270 		ps++;
271 		cp = *ps;
272 	}
273 	return (0);
274 }
275 
276 static char *empty_opt_vector[] = {
277 	NULL
278 };
279 /*
280  * Compare the mount options that were requested by the caller to
281  * the options actually supported by the file system.  If any requested
282  * options are not supported, print a warning message.
283  *
284  * WARNING: this function modifies the string pointed to by
285  *	the requested_opts argument.
286  *
287  * Arguments:
288  *	requested_opts - the string containing the requested options.
289  *	actual_opts - the string returned by mount(2), which lists the
290  *		options actually supported.  It is normal for this
291  *		string to contain more options than the requested options.
292  *		(The actual options may contain the default options, which
293  *		may not have been included in the requested options.)
294  *	special - device being mounted (only used in error messages).
295  *	mountp - mount point (only used in error messages).
296  */
297 void
298 cmp_requested_to_actual_options(char *requested_opts, char *actual_opts,
299 	char *special, char *mountp)
300 {
301 	char	*option_ptr, *actopt, *equalptr;
302 	int	found;
303 	char	*actual_opt_hold, *bufp;
304 
305 	if (requested_opts == NULL)
306 		return;
307 
308 	bufp = alloca(strlen(actual_opts) + 1);
309 
310 	while (*requested_opts != '\0') {
311 		(void) getsubopt(&requested_opts, empty_opt_vector,
312 			&option_ptr);
313 
314 		/*
315 		 * Truncate any "=<value>" string from the end of
316 		 * the option.
317 		 */
318 		if ((equalptr = strchr(option_ptr, '=')) != NULL)
319 			*equalptr = '\0';
320 
321 		if (*option_ptr == '\0')
322 			continue;
323 
324 		/*
325 		 * Search for the requested option in the list of options
326 		 * actually supported.
327 		 */
328 		found = 0;
329 
330 		/*
331 		 * Need to use a copy of actual_opts because getsubopt
332 		 * is destructive and we need to scan the actual_opts
333 		 * string more than once.
334 		 *
335 		 * We also need to reset actual_opt_hold to the
336 		 * beginning of the buffer because getsubopt changes
337 		 * actual_opt_hold (the pointer).
338 		 */
339 		actual_opt_hold = bufp;
340 		if (actual_opts != NULL)
341 			(void) strcpy(actual_opt_hold, actual_opts);
342 		else
343 			*actual_opt_hold = '\0';
344 
345 		while (*actual_opt_hold != '\0') {
346 			(void) getsubopt(&actual_opt_hold, empty_opt_vector,
347 				&actopt);
348 
349 			/* Truncate the "=<value>", if any. */
350 			if ((equalptr = strchr(actopt, '=')) != NULL)
351 				*equalptr = '\0';
352 
353 			if ((strcmp(option_ptr, actopt)) == 0) {
354 				found = 1;
355 				break;
356 			}
357 		}
358 
359 		if (found == 0) {
360 			/*
361 			 * That we're ignoring the option is always
362 			 * truthful; the old message that the option
363 			 * was unknown is often not correct.
364 			 */
365 			(void) fprintf(stderr, gettext(
366 			    "mount: %s on %s - WARNING ignoring option "
367 			    "\"%s\"\n"), special, mountp, option_ptr);
368 		}
369 	}
370 }
371 /*
372  * FUNCTION:	fsgetmaxphys(int *, int *)
373  *
374  * INPUT:	int *maxphys - a pointer to an integer that will hold
375  *			the value for the system maxphys value.
376  *		int *error - 0 means completed successfully
377  *			     otherwise this indicates the errno value.
378  *
379  * RETURNS:	int	- 0 if maxphys not found
380  *			- 1 if maxphys is found
381  */
382 int
383 fsgetmaxphys(int *maxphys, int *error) {
384 
385 	int	gotit = 0;
386 	int	fp = open("/", O_RDONLY);
387 
388 	*error = 0;
389 
390 	/*
391 	 * For some reason cannot open root as read only. Need a valid file
392 	 * descriptor to call the ufs private ioctl. If this open failes,
393 	 * just assume we cannot get maxphys in this case.
394 	 */
395 	if (fp == -1) {
396 		return (gotit);
397 	}
398 
399 	if (ioctl(fp, _FIOGETMAXPHYS, maxphys) == -1) {
400 		*error = errno;
401 		(void) close(fp);
402 		return (gotit);
403 	}
404 
405 	(void) close(fp);
406 	gotit = 1;
407 	return (gotit);
408 
409 }
410 
411 /*
412  * The below is limited support for zone-aware commands.
413  */
414 struct zone_summary {
415 	zoneid_t	zoneid;
416 	char		rootpath[MAXPATHLEN];
417 	size_t		rootpathlen;
418 };
419 
420 struct zone_summary *
421 fs_get_zone_summaries(void)
422 {
423 	uint_t numzones = 0, oldnumzones = 0;
424 	uint_t i, j;
425 	zoneid_t *ids = NULL;
426 	struct zone_summary *summaries;
427 	zoneid_t myzoneid = getzoneid();
428 
429 	for (;;) {
430 		if (zone_list(ids, &numzones) < 0) {
431 			    perror("unable to retrieve list of zones");
432 			    if (ids != NULL)
433 				    free(ids);
434 			    return (NULL);
435 		}
436 		if (numzones <= oldnumzones)
437 			break;
438 		if (ids != NULL)
439 			free(ids);
440 		ids = malloc(numzones * sizeof (*ids));
441 		if (ids == NULL) {
442 			perror("malloc failed");
443 			return (NULL);
444 		}
445 		oldnumzones = numzones;
446 	}
447 
448 	summaries = malloc((numzones + 1) * sizeof (*summaries));
449 	if (summaries == NULL) {
450 		free(ids);
451 		perror("malloc failed");
452 		return (NULL);
453 	}
454 
455 
456 	for (i = 0, j = 0; i < numzones; i++) {
457 		ssize_t len;
458 
459 		if (ids[i] == myzoneid)
460 			continue;
461 		len = zone_getattr(ids[i], ZONE_ATTR_ROOT,
462 		    summaries[j].rootpath, sizeof (summaries[j].rootpath));
463 		if (len < 0) {
464 			/*
465 			 * Zone must have gone away. Skip.
466 			 */
467 			continue;
468 		}
469 		/*
470 		 * Adding a trailing '/' to the zone's rootpath allows us to
471 		 * use strncmp() to see if a given path resides within that
472 		 * zone.
473 		 *
474 		 * As an example, if the zone's rootpath is "/foo/root",
475 		 * "/foo/root/usr" resides within the zone, while
476 		 * "/foo/rootpath" doesn't.
477 		 */
478 		(void) strlcat(summaries[j].rootpath, "/",
479 		    sizeof (summaries[j].rootpath));
480 		summaries[j].rootpathlen = len;
481 		summaries[j].zoneid = ids[i];
482 		j++;
483 	}
484 	summaries[j].zoneid = -1;
485 	free(ids);
486 	return (summaries);
487 }
488 
489 static zoneid_t
490 fs_find_zone(const struct zone_summary *summaries, const char *mntpt)
491 {
492 	uint_t i;
493 
494 	for (i = 0; summaries[i].zoneid != -1; i++) {
495 		if (strncmp(mntpt, summaries[i].rootpath,
496 		    summaries[i].rootpathlen) == 0)
497 			return (summaries[i].zoneid);
498 	}
499 	/*
500 	 * (-1) is the special token we return to the caller if the mount
501 	 * wasn't found in any other mounts on the system.  This means it's
502 	 * only visible to our zone.
503 	 *
504 	 * Odd choice of constant, I know, but it beats calling getzoneid() a
505 	 * million times.
506 	 */
507 	return (-1);
508 }
509 
510 boolean_t
511 fs_mount_in_other_zone(const struct zone_summary *summaries, const char *mntpt)
512 {
513 	return (fs_find_zone(summaries, mntpt) != -1);
514 }
515 
516 /*
517  * List of standard options.
518  */
519 static const char *stdopts[] = {
520 	MNTOPT_RO,			MNTOPT_RW,
521 	MNTOPT_SUID,			MNTOPT_NOSUID,
522 	MNTOPT_DEVICES,			MNTOPT_NODEVICES,
523 	MNTOPT_SETUID,			MNTOPT_NOSETUID,
524 	MNTOPT_NBMAND,			MNTOPT_NONBMAND,
525 	MNTOPT_EXEC,			MNTOPT_NOEXEC,
526 };
527 
528 #define	NSTDOPT		(sizeof (stdopts) / sizeof (stdopts[0]))
529 
530 static int
531 optindx(const char *opt)
532 {
533 	int i;
534 
535 	for (i = 0; i < NSTDOPT; i++) {
536 		if (strcmp(opt, stdopts[i]) == 0)
537 			return (i);
538 	}
539 	return (-1);
540 }
541 
542 /*
543  * INPUT:	filesystem option not recognized by the fs specific option
544  *		parsing code.
545  * OUTPUT:	True if and only if the option is one of the standard VFS
546  *		layer options.
547  */
548 boolean_t
549 fsisstdopt(const char *opt)
550 {
551 	return (optindx(opt) != -1);
552 }
553