Home | History | Annotate | Line # | Download | only in mount_tmpfs
mount_tmpfs.c revision 1.13
      1 /*	$NetBSD: mount_tmpfs.c,v 1.13 2006/03/26 16:15:15 jmmv Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
      9  * 2005 program.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  * 3. All advertising materials mentioning features or use of this software
     20  *    must display the following acknowledgement:
     21  *        This product includes software developed by the NetBSD
     22  *        Foundation, Inc. and its contributors.
     23  * 4. Neither the name of The NetBSD Foundation nor the names of its
     24  *    contributors may be used to endorse or promote products derived
     25  *    from this software without specific prior written permission.
     26  *
     27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     37  * POSSIBILITY OF SUCH DAMAGE.
     38  */
     39 
     40 #include <sys/cdefs.h>
     41 #ifndef lint
     42 __RCSID("$NetBSD: mount_tmpfs.c,v 1.13 2006/03/26 16:15:15 jmmv Exp $");
     43 #endif /* not lint */
     44 
     45 #define __POOL_EXPOSE
     46 #include <sys/param.h>
     47 #include <sys/mount.h>
     48 #include <sys/stat.h>
     49 
     50 #include <fs/tmpfs/tmpfs.h>
     51 
     52 #include <ctype.h>
     53 #include <err.h>
     54 #include <errno.h>
     55 #include <grp.h>
     56 #include <mntopts.h>
     57 #include <pwd.h>
     58 #include <stdio.h>
     59 #include <stdlib.h>
     60 #include <string.h>
     61 #include <unistd.h>
     62 
     63 /* --------------------------------------------------------------------- */
     64 
     65 static const struct mntopt mopts[] = {
     66 	MOPT_STDOPTS,
     67 	MOPT_GETARGS,
     68 	{ NULL }
     69 };
     70 
     71 /* --------------------------------------------------------------------- */
     72 
     73 static int	mount_tmpfs(int argc, char **argv);
     74 static void	usage(void);
     75 static int	dehumanize_group(const char *str, gid_t *gid);
     76 static int	dehumanize_mode(const char *str, mode_t *mode);
     77 static int	dehumanize_off(const char *str, off_t *size);
     78 static int	dehumanize_user(const char *str, uid_t *uid);
     79 
     80 /* --------------------------------------------------------------------- */
     81 
     82 int
     83 mount_tmpfs(int argc, char *argv[])
     84 {
     85 	char canon_dir[MAXPATHLEN];
     86 	int gidset, modeset, uidset; /* Ought to be 'bool'. */
     87 	int ch, mntflags;
     88 	gid_t gid;
     89 	uid_t uid;
     90 	mode_t mode;
     91 	off_t offtmp;
     92 	mntoptparse_t mp;
     93 	struct tmpfs_args args;
     94 	struct stat sb;
     95 
     96 	setprogname(argv[0]);
     97 
     98 	/* Set default values for mount point arguments. */
     99 	args.ta_version = TMPFS_ARGS_VERSION;
    100 	args.ta_size_max = 0;
    101 	args.ta_nodes_max = 0;
    102 	mntflags = 0;
    103 
    104 	gidset = 0; gid = 0;
    105 	uidset = 0; uid = 0;
    106 	modeset = 0; mode = 0;
    107 
    108 	optind = optreset = 1;
    109 	while ((ch = getopt(argc, argv, "g:m:n:o:s:u:")) != -1 ) {
    110 		switch (ch) {
    111 		case 'g':
    112 			if (!dehumanize_group(optarg, &gid)) {
    113 				errx(EXIT_FAILURE, "failed to parse group "
    114 				    "'%s'", optarg);
    115 				/* NOTREACHED */
    116 			}
    117 			gidset = 1;
    118 			break;
    119 
    120 		case 'm':
    121 			if (!dehumanize_mode(optarg, &mode)) {
    122 				errx(EXIT_FAILURE, "failed to parse mode "
    123 				    "'%s'", optarg);
    124 				/* NOTREACHED */
    125 			}
    126 			modeset = 1;
    127 			break;
    128 
    129 		case 'n':
    130 			if (!dehumanize_off(optarg, &offtmp)) {
    131 				errx(EXIT_FAILURE, "failed to parse size "
    132 				    "'%s'", optarg);
    133 				/* NOTREACHED */
    134 			}
    135 			args.ta_nodes_max = offtmp;
    136 			break;
    137 
    138 		case 'o':
    139 			mp = getmntopts(optarg, mopts, &mntflags, 0);
    140 			if (mp == NULL)
    141 				err(1, "getmntopts");
    142 			freemntopts(mp);
    143 			break;
    144 
    145 		case 's':
    146 			if (!dehumanize_off(optarg, &offtmp)) {
    147 				errx(EXIT_FAILURE, "failed to parse size "
    148 				    "'%s'", optarg);
    149 				/* NOTREACHED */
    150 			}
    151 			args.ta_size_max = offtmp;
    152 			break;
    153 
    154 		case 'u':
    155 			if (!dehumanize_user(optarg, &uid)) {
    156 				errx(EXIT_FAILURE, "failed to parse user "
    157 				    "'%s'", optarg);
    158 				/* NOTREACHED */
    159 			}
    160 			uidset = 1;
    161 			break;
    162 
    163 		case '?':
    164 		default:
    165 			usage();
    166 			/* NOTREACHED */
    167 		}
    168 	}
    169 	argc -= optind;
    170 	argv += optind;
    171 
    172 	if (argc != 2) {
    173 		usage();
    174 		/* NOTREACHED */
    175 	}
    176 
    177 	if (realpath(argv[1], canon_dir) == NULL) {
    178 		err(EXIT_FAILURE, "realpath %s", argv[0]);
    179 		/* NOTREACHED */
    180 	}
    181 
    182 	if (strncmp(argv[1], canon_dir, MAXPATHLEN) != 0) {
    183 		warnx("\"%s\" is a relative path", argv[0]);
    184 		warnx("using \"%s\" instead", canon_dir);
    185 	}
    186 
    187 	if (stat(canon_dir, &sb) == -1) {
    188 		err(EXIT_FAILURE, "cannot stat");
    189 		/* NOTREACHED */
    190 	}
    191 	args.ta_root_uid = uidset ? uid : sb.st_uid;
    192 	args.ta_root_gid = gidset ? gid : sb.st_gid;
    193 	args.ta_root_mode = modeset ? mode : sb.st_mode;
    194 
    195 	if (mount(MOUNT_TMPFS, canon_dir, mntflags, &args)) {
    196 		err(EXIT_FAILURE, "tmpfs on %s", canon_dir);
    197 		/* NOTREACHED */
    198 	}
    199 	if (mntflags & MNT_GETARGS) {
    200 		struct passwd *pw;
    201 		struct group *gr;
    202 
    203 		(void)printf("version=%d\n", args.ta_version);
    204 		(void)printf("size_max=%" PRIuMAX "\n",
    205 		    (uintmax_t)args.ta_size_max);
    206 		(void)printf("nodes_max=%" PRIuMAX "\n",
    207 		    (uintmax_t)args.ta_nodes_max);
    208 
    209 		pw = getpwuid(args.ta_root_uid);
    210 		if (pw == NULL)
    211 			(void)printf("root_uid=%" PRIuMAX "\n",
    212 			    (uintmax_t)args.ta_root_uid);
    213 		else
    214 			(void)printf("root_uid=%s\n", pw->pw_name);
    215 
    216 		gr = getgrgid(args.ta_root_gid);
    217 		if (gr == NULL)
    218 			(void)printf("root_gid=%" PRIuMAX "\n",
    219 			    (uintmax_t)args.ta_root_gid);
    220 		else
    221 			(void)printf("root_gid=%s\n", gr->gr_name);
    222 
    223 		(void)printf("root_mode=%o\n", args.ta_root_mode);
    224 	}
    225 
    226 	return EXIT_SUCCESS;
    227 }
    228 
    229 /* --------------------------------------------------------------------- */
    230 
    231 static void
    232 usage(void)
    233 {
    234 	(void)fprintf(stderr,
    235 	    "Usage: %s [-g group] [-m mode] [-n nodes] [-o options] [-s size]\n"
    236 	    "           [-u user] tmpfs mountpoint\n", getprogname());
    237 	exit(1);
    238 }
    239 
    240 /* --------------------------------------------------------------------- */
    241 
    242 /*
    243  * Obtains a GID number based on the contents of 'str'.  If it is a string
    244  * and is found in group(5), its corresponding ID is used.  Otherwise, an
    245  * attempt is made to convert the string to a number and use that as a GID.
    246  * In case of success, true is returned and *gid holds the GID value.
    247  * Otherwise, false is returned and *gid is untouched.
    248  */
    249 static int
    250 dehumanize_group(const char *str, gid_t *gid)
    251 {
    252 	int error;
    253 	struct group *gr;
    254 
    255 	gr = getgrnam(str);
    256 	if (gr != NULL) {
    257 		*gid = gr->gr_gid;
    258 		error = 1;
    259 	} else {
    260 		char *ep;
    261 		unsigned long tmp;
    262 
    263 		errno = 0;
    264 		tmp = strtoul(str, &ep, 0);
    265 		if (str[0] == '\0' || *ep != '\0')
    266 			error = 0; /* Not a number. */
    267 		else if (errno == ERANGE)
    268 			error = 0; /* Out of range. */
    269 		else {
    270 			*gid = (gid_t)tmp;
    271 			error = 1;
    272 		}
    273 	}
    274 
    275 	return error;
    276 }
    277 
    278 /* --------------------------------------------------------------------- */
    279 
    280 /*
    281  * Obtains a mode number based on the contents of 'str'.
    282  * In case of success, true is returned and *mode holds the mode value.
    283  * Otherwise, false is returned and *mode is untouched.
    284  */
    285 static int
    286 dehumanize_mode(const char *str, mode_t *mode)
    287 {
    288 	char *ep;
    289 	int error;
    290 	long tmp;
    291 
    292 	errno = 0;
    293 	tmp = strtol(str, &ep, 8);
    294 	if (str[0] == '\0' || *ep != '\0')
    295 		error = 0; /* Not a number. */
    296 	else if (errno == ERANGE)
    297 		error = 0; /* Out of range. */
    298 	else {
    299 		*mode = (mode_t)tmp;
    300 		error = 1;
    301 	}
    302 
    303 	return error;
    304 }
    305 
    306 /* --------------------------------------------------------------------- */
    307 
    308 /*
    309  * Converts the number given in 'str', which may be given in a humanized
    310  * form (as described in humanize_number(3), but with some limitations),
    311  * to a file size (off_t) without units.
    312  * In case of success, true is returned and *size holds the value.
    313  * Otherwise, false is returned and *size is untouched.
    314  */
    315 static int
    316 dehumanize_off(const char *str, off_t *size)
    317 {
    318 	char *ep, unit;
    319 	const char *delimit;
    320 	long multiplier;
    321 	long long tmp, tmp2;
    322 	size_t len;
    323 
    324 	len = strlen(str);
    325 	if (len < 1)
    326 		return 0;
    327 
    328 	multiplier = 1;
    329 
    330 	unit = str[len - 1];
    331 	if (isalpha((int)unit)) {
    332 		switch (tolower((int)unit)) {
    333 		case 'b':
    334 			multiplier = 1;
    335 			break;
    336 
    337 		case 'k':
    338 			multiplier = 1024;
    339 			break;
    340 
    341 		case 'm':
    342 			multiplier = 1024 * 1024;
    343 			break;
    344 
    345 		case 'g':
    346 			multiplier = 1024 * 1024 * 1024;
    347 			break;
    348 
    349 		default:
    350 			return 0; /* Invalid suffix. */
    351 		}
    352 
    353 		delimit = &str[len - 1];
    354 	} else
    355 		delimit = NULL;
    356 
    357 	errno = 0;
    358 	tmp = strtoll(str, &ep, 10);
    359 	if (str[0] == '\0' || (ep != delimit && *ep != '\0'))
    360 		return 0; /* Not a number. */
    361 	else if (errno == ERANGE)
    362 		return 0; /* Out of range. */
    363 
    364 	tmp2 = tmp * multiplier;
    365 	tmp2 = tmp2 / multiplier;
    366 	if (tmp != tmp2)
    367 		return 0; /* Out of range. */
    368 	*size = tmp * multiplier;
    369 
    370 	return 1;
    371 }
    372 
    373 /* --------------------------------------------------------------------- */
    374 
    375 /*
    376  * Obtains a UID number based on the contents of 'str'.  If it is a string
    377  * and is found in passwd(5), its corresponding ID is used.  Otherwise, an
    378  * attempt is made to convert the string to a number and use that as a UID.
    379  * In case of success, true is returned and *uid holds the UID value.
    380  * Otherwise, false is returned and *uid is untouched.
    381  */
    382 static int
    383 dehumanize_user(const char *str, uid_t *uid)
    384 {
    385 	int error;
    386 	struct passwd *pw;
    387 
    388 	pw = getpwnam(str);
    389 	if (pw != NULL) {
    390 		*uid = pw->pw_uid;
    391 		error = 1;
    392 	} else {
    393 		char *ep;
    394 		unsigned long tmp;
    395 
    396 		errno = 0;
    397 		tmp = strtoul(str, &ep, 0);
    398 		if (str[0] == '\0' || *ep != '\0')
    399 			error = 0; /* Not a number. */
    400 		else if (errno == ERANGE)
    401 			error = 0; /* Out of range. */
    402 		else {
    403 			*uid = (uid_t)tmp;
    404 			error = 1;
    405 		}
    406 	}
    407 
    408 	return error;
    409 }
    410 
    411 /* --------------------------------------------------------------------- */
    412 
    413 #ifndef MOUNT_NOMAIN
    414 int
    415 main(int argc, char *argv[])
    416 {
    417 
    418 	return mount_tmpfs(argc, argv);
    419 }
    420 #endif
    421