1 /* $NetBSD: mount_tmpfs.c,v 1.14 2006/05/27 09:14:17 yamt 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.14 2006/05/27 09:14:17 yamt 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 mp; 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 mp = getmntopts(optarg, mopts, &mntflags, 0); 139 if (mp == NULL) 140 err(1, "getmntopts"); 141 freemntopts(mp); 142 break; 143 144 case 's': 145 if (!dehumanize_off(optarg, &offtmp)) { 146 errx(EXIT_FAILURE, "failed to parse size " 147 "'%s'", optarg); 148 /* NOTREACHED */ 149 } 150 args.ta_size_max = offtmp; 151 break; 152 153 case 'u': 154 if (!dehumanize_user(optarg, &uid)) { 155 errx(EXIT_FAILURE, "failed to parse user " 156 "'%s'", optarg); 157 /* NOTREACHED */ 158 } 159 uidset = 1; 160 break; 161 162 case '?': 163 default: 164 usage(); 165 /* NOTREACHED */ 166 } 167 } 168 argc -= optind; 169 argv += optind; 170 171 if (argc != 2) { 172 usage(); 173 /* NOTREACHED */ 174 } 175 176 if (realpath(argv[1], canon_dir) == NULL) { 177 err(EXIT_FAILURE, "realpath %s", argv[0]); 178 /* NOTREACHED */ 179 } 180 181 if (strncmp(argv[1], canon_dir, MAXPATHLEN) != 0) { 182 warnx("\"%s\" is a relative path", argv[0]); 183 warnx("using \"%s\" instead", canon_dir); 184 } 185 186 if (stat(canon_dir, &sb) == -1) { 187 err(EXIT_FAILURE, "cannot stat"); 188 /* NOTREACHED */ 189 } 190 args.ta_root_uid = uidset ? uid : sb.st_uid; 191 args.ta_root_gid = gidset ? gid : sb.st_gid; 192 args.ta_root_mode = modeset ? mode : sb.st_mode; 193 194 if (mount(MOUNT_TMPFS, canon_dir, mntflags, &args)) { 195 err(EXIT_FAILURE, "tmpfs on %s", canon_dir); 196 /* NOTREACHED */ 197 } 198 if (mntflags & MNT_GETARGS) { 199 struct passwd *pw; 200 struct group *gr; 201 202 (void)printf("version=%d\n", args.ta_version); 203 (void)printf("size_max=%" PRIuMAX "\n", 204 (uintmax_t)args.ta_size_max); 205 (void)printf("nodes_max=%" PRIuMAX "\n", 206 (uintmax_t)args.ta_nodes_max); 207 208 pw = getpwuid(args.ta_root_uid); 209 if (pw == NULL) 210 (void)printf("root_uid=%" PRIuMAX "\n", 211 (uintmax_t)args.ta_root_uid); 212 else 213 (void)printf("root_uid=%s\n", pw->pw_name); 214 215 gr = getgrgid(args.ta_root_gid); 216 if (gr == NULL) 217 (void)printf("root_gid=%" PRIuMAX "\n", 218 (uintmax_t)args.ta_root_gid); 219 else 220 (void)printf("root_gid=%s\n", gr->gr_name); 221 222 (void)printf("root_mode=%o\n", args.ta_root_mode); 223 } 224 225 return EXIT_SUCCESS; 226 } 227 228 /* --------------------------------------------------------------------- */ 229 230 static void 231 usage(void) 232 { 233 (void)fprintf(stderr, 234 "Usage: %s [-g group] [-m mode] [-n nodes] [-o options] [-s size]\n" 235 " [-u user] tmpfs mountpoint\n", getprogname()); 236 exit(1); 237 } 238 239 /* --------------------------------------------------------------------- */ 240 241 /* 242 * Obtains a GID number based on the contents of 'str'. If it is a string 243 * and is found in group(5), its corresponding ID is used. Otherwise, an 244 * attempt is made to convert the string to a number and use that as a GID. 245 * In case of success, true is returned and *gid holds the GID value. 246 * Otherwise, false is returned and *gid is untouched. 247 */ 248 static int 249 dehumanize_group(const char *str, gid_t *gid) 250 { 251 int error; 252 struct group *gr; 253 254 gr = getgrnam(str); 255 if (gr != NULL) { 256 *gid = gr->gr_gid; 257 error = 1; 258 } else { 259 char *ep; 260 unsigned long tmp; 261 262 errno = 0; 263 tmp = strtoul(str, &ep, 0); 264 if (str[0] == '\0' || *ep != '\0') 265 error = 0; /* Not a number. */ 266 else if (errno == ERANGE) 267 error = 0; /* Out of range. */ 268 else { 269 *gid = (gid_t)tmp; 270 error = 1; 271 } 272 } 273 274 return error; 275 } 276 277 /* --------------------------------------------------------------------- */ 278 279 /* 280 * Obtains a mode number based on the contents of 'str'. 281 * In case of success, true is returned and *mode holds the mode value. 282 * Otherwise, false is returned and *mode is untouched. 283 */ 284 static int 285 dehumanize_mode(const char *str, mode_t *mode) 286 { 287 char *ep; 288 int error; 289 long tmp; 290 291 errno = 0; 292 tmp = strtol(str, &ep, 8); 293 if (str[0] == '\0' || *ep != '\0') 294 error = 0; /* Not a number. */ 295 else if (errno == ERANGE) 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) 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 error = 0; /* Out of range. */ 401 else { 402 *uid = (uid_t)tmp; 403 error = 1; 404 } 405 } 406 407 return error; 408 } 409 410 /* --------------------------------------------------------------------- */ 411 412 #ifndef MOUNT_NOMAIN 413 int 414 main(int argc, char *argv[]) 415 { 416 417 return mount_tmpfs(argc, argv); 418 } 419 #endif 420