Home | History | Annotate | Download | only in fs.d
      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