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