Home | History | Annotate | Line # | Download | only in mount_tmpfs
mount_tmpfs.c revision 1.3
      1 /*	$NetBSD: mount_tmpfs.c,v 1.3 2005/09/23 15:36:16 jmmv Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2005 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.3 2005/09/23 15:36:16 jmmv Exp $");
     43 #endif /* not lint */
     44 
     45 #include <sys/param.h>
     46 #include <sys/mount.h>
     47 
     48 #include <fs/tmpfs/tmpfs.h>
     49 
     50 #include <ctype.h>
     51 #include <err.h>
     52 #include <errno.h>
     53 #include <grp.h>
     54 #include <mntopts.h>
     55 #include <pwd.h>
     56 #include <stdio.h>
     57 #include <stdlib.h>
     58 #include <string.h>
     59 #include <unistd.h>
     60 
     61 /* --------------------------------------------------------------------- */
     62 
     63 static const struct mntopt mopts[] = {
     64 	MOPT_STDOPTS,
     65 	{ NULL }
     66 };
     67 
     68 /* --------------------------------------------------------------------- */
     69 
     70 static int	mount_tmpfs(int argc, char **argv);
     71 static void	usage(void);
     72 static int	dehumanize_group(const char *str, gid_t *gid);
     73 static int	dehumanize_mode(const char *str, mode_t *mode);
     74 static int	dehumanize_off(const char *str, off_t *size);
     75 static int	dehumanize_user(const char *str, uid_t *uid);
     76 
     77 /* --------------------------------------------------------------------- */
     78 
     79 int
     80 mount_tmpfs(int argc, char *argv[])
     81 {
     82 	char canon_dir[MAXPATHLEN];
     83 	int ch, mntflags;
     84 	off_t offtmp;
     85 	mntoptparse_t mo;
     86 	struct tmpfs_args args;
     87 
     88 	setprogname(argv[0]);
     89 
     90 	/* Set default values for mount point arguments. */
     91 	args.ta_version = TMPFS_ARGS_VERSION;
     92 	args.ta_size_max = 0;
     93 	args.ta_nodes_max = 0;
     94 	args.ta_root_uid = getuid();
     95 	args.ta_root_gid = getgid();
     96 	args.ta_root_mode = 0755;
     97 	mntflags = 0;
     98 
     99 	optind = optreset = 1;
    100 	while ((ch = getopt(argc, argv, "g:m:n:o:s:u:")) != -1 ) {
    101 		switch (ch) {
    102 		case 'g':
    103 			if (!dehumanize_group(optarg, &args.ta_root_gid)) {
    104 				errx(EXIT_FAILURE, "failed to parse group "
    105 				    "'%s'", optarg);
    106 				/* NOTREACHED */
    107 			}
    108 			break;
    109 
    110 		case 'm':
    111 			if (!dehumanize_mode(optarg, &args.ta_root_mode)) {
    112 				errx(EXIT_FAILURE, "failed to parse mode "
    113 				    "'%s'", optarg);
    114 				/* NOTREACHED */
    115 			}
    116 			break;
    117 
    118 		case 'n':
    119 			if (!dehumanize_off(optarg, &offtmp)) {
    120 				errx(EXIT_FAILURE, "failed to parse size "
    121 				    "'%s'", optarg);
    122 				/* NOTREACHED */
    123 			}
    124 			args.ta_nodes_max = offtmp;
    125 			break;
    126 
    127 		case 'o':
    128 			mo = getmntopts(optarg, mopts, &mntflags, 0);
    129 			freemntopts(mo);
    130 			break;
    131 
    132 		case 's':
    133 			if (!dehumanize_off(optarg, &offtmp)) {
    134 				errx(EXIT_FAILURE, "failed to parse size "
    135 				    "'%s'", optarg);
    136 				/* NOTREACHED */
    137 			}
    138 			args.ta_size_max = offtmp;
    139 			break;
    140 
    141 		case 'u':
    142 			if (!dehumanize_user(optarg, &args.ta_root_uid)) {
    143 				errx(EXIT_FAILURE, "failed to parse user "
    144 				    "'%s'", optarg);
    145 				/* NOTREACHED */
    146 			}
    147 			break;
    148 
    149 		case '?':
    150 		default:
    151 			usage();
    152 			/* NOTREACHED */
    153 		}
    154 	}
    155 	argc -= optind;
    156 	argv += optind;
    157 
    158 	if (argc != 2) {
    159 		usage();
    160 		/* NOTREACHED */
    161 	}
    162 
    163 	if (realpath(argv[1], canon_dir) == NULL) {
    164 		err(EXIT_FAILURE, "realpath %s", argv[0]);
    165 		/* NOTREACHED */
    166 	}
    167 
    168 	if (strncmp(argv[1], canon_dir, MAXPATHLEN) != 0) {
    169 		warnx("\"%s\" is a relative path", argv[0]);
    170 		warnx("using \"%s\" instead", canon_dir);
    171 	}
    172 
    173 	if (mount(MOUNT_TMPFS, canon_dir, mntflags, &args)) {
    174 		err(EXIT_FAILURE, "tmpfs on %s", canon_dir);
    175 		/* NOTREACHED */
    176 	}
    177 
    178 	return EXIT_SUCCESS;
    179 }
    180 
    181 /* --------------------------------------------------------------------- */
    182 
    183 static void
    184 usage(void)
    185 {
    186 	(void)fprintf(stderr,
    187 	    "Usage: %s [-g group] [-m mode] [-n nodes] [-o options] [-s size]\n"
    188 	    "           [-u user] tmpfs mountpoint\n", getprogname());
    189 	exit(1);
    190 }
    191 
    192 /* --------------------------------------------------------------------- */
    193 
    194 /*
    195  * Obtains a GID number based on the contents of 'str'.  If it is a string
    196  * and is found in group(5), its corresponding ID is used.  Otherwise, an
    197  * attempt is made to convert the string to a number and use that as a GID.
    198  * In case of success, true is returned and *gid holds the GID value.
    199  * Otherwise, false is returned and *gid is untouched.
    200  */
    201 static int
    202 dehumanize_group(const char *str, gid_t *gid)
    203 {
    204 	int error;
    205 	struct group *gr;
    206 
    207 	gr = getgrnam(str);
    208 	if (gr != NULL) {
    209 		*gid = gr->gr_gid;
    210 		error = 1;
    211 	} else {
    212 		char *ep;
    213 		long tmp;
    214 
    215 		errno = 0;
    216 		tmp = strtol(str, &ep, 10);
    217 		if (str[0] == '\0' || *ep != '\0')
    218 			error = 0; /* Not a number. */
    219 		else if (errno == ERANGE &&
    220 		    (tmp == LONG_MAX || tmp == LONG_MIN))
    221 			error = 0; /* Out of range. */
    222 		else {
    223 			*gid = (gid_t)tmp;
    224 			error = 1;
    225 		}
    226 	}
    227 
    228 	return error;
    229 }
    230 
    231 /* --------------------------------------------------------------------- */
    232 
    233 /*
    234  * Obtains a mode number based on the contents of 'str'.
    235  * In case of success, true is returned and *mode holds the mode value.
    236  * Otherwise, false is returned and *mode is untouched.
    237  */
    238 static int
    239 dehumanize_mode(const char *str, mode_t *mode)
    240 {
    241 	char *ep;
    242 	int error;
    243 	long tmp;
    244 
    245 	errno = 0;
    246 	tmp = strtol(str, &ep, 8);
    247 	if (str[0] == '\0' || *ep != '\0')
    248 		error = 0; /* Not a number. */
    249 	else if (errno == ERANGE &&
    250 	    (tmp == LONG_MAX || tmp == LONG_MIN))
    251 		error = 0; /* Out of range. */
    252 	else {
    253 		*mode = (mode_t)tmp;
    254 		error = 1;
    255 	}
    256 
    257 	return error;
    258 }
    259 
    260 /* --------------------------------------------------------------------- */
    261 
    262 /*
    263  * Converts the number given in 'str', which may be given in a humanized
    264  * form (as described in humanize_number(3), but with some limitations),
    265  * to a file size (off_t) without units.
    266  * In case of success, true is returned and *size holds the value.
    267  * Otherwise, false is returned and *size is untouched.
    268  */
    269 static int
    270 dehumanize_off(const char *str, off_t *size)
    271 {
    272 	char *ep, unit;
    273 	const char *delimit;
    274 	size_t len, multiplier, tmp;
    275 
    276 	len = strlen(str);
    277 	if (len < 1)
    278 		return 0;
    279 
    280 	multiplier = 1;
    281 
    282 	unit = str[len - 1];
    283 	if (isalpha((int)unit)) {
    284 		switch (unit) {
    285 		case 'b':
    286 			multiplier = 1;
    287 			break;
    288 
    289 		case 'k':
    290 			multiplier = 1024;
    291 			break;
    292 
    293 		case 'M':
    294 			multiplier = 1024 * 1024;
    295 			break;
    296 
    297 		case 'G':
    298 			multiplier = 1024 * 1024 * 1024;
    299 			break;
    300 
    301 		default:
    302 			return 0; /* Invalid suffix. */
    303 		}
    304 
    305 		delimit = &str[len - 1];
    306 	} else
    307 		delimit = NULL;
    308 
    309 	errno = 0;
    310 	tmp = strtol(str, &ep, 10);
    311 	if (str[0] == '\0' || (ep != delimit && *ep != '\0'))
    312 		return 0; /* Not a number. */
    313 	else if (errno == ERANGE &&
    314 	    (tmp == LONG_MAX || tmp == LONG_MIN))
    315 		return 0; /* Out of range. */
    316 
    317 	*size = tmp * multiplier;
    318 
    319 	return 1;
    320 }
    321 
    322 /* --------------------------------------------------------------------- */
    323 
    324 /*
    325  * Obtains a UID number based on the contents of 'str'.  If it is a string
    326  * and is found in passwd(5), its corresponding ID is used.  Otherwise, an
    327  * attempt is made to convert the string to a number and use that as a UID.
    328  * In case of success, true is returned and *uid holds the UID value.
    329  * Otherwise, false is returned and *uid is untouched.
    330  */
    331 static int
    332 dehumanize_user(const char *str, uid_t *uid)
    333 {
    334 	int error;
    335 	struct passwd *pw;
    336 
    337 	pw = getpwnam(str);
    338 	if (pw != NULL) {
    339 		*uid = pw->pw_uid;
    340 		error = 1;
    341 	} else {
    342 		char *ep;
    343 		long tmp;
    344 
    345 		errno = 0;
    346 		tmp = strtol(str, &ep, 10);
    347 		if (str[0] == '\0' || *ep != '\0')
    348 			error = 0; /* Not a number. */
    349 		else if (errno == ERANGE &&
    350 		    (tmp == LONG_MAX || tmp == LONG_MIN))
    351 			error = 0; /* Out of range. */
    352 		else {
    353 			*uid = (uid_t)tmp;
    354 			error = 1;
    355 		}
    356 	}
    357 
    358 	return error;
    359 }
    360 
    361 /* --------------------------------------------------------------------- */
    362 
    363 #ifndef MOUNT_NOMAIN
    364 int
    365 main(int argc, char *argv[])
    366 {
    367 
    368 	return mount_tmpfs(argc, argv);
    369 }
    370 #endif
    371