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