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