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