mount_tmpfs.c revision 1.8 1 /* $NetBSD: mount_tmpfs.c,v 1.8 2005/09/25 19:04:49 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.8 2005/09/25 19:04:49 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 <stdbool.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 bool gidset, modeset, uidset;
87 int ch, mntflags;
88 gid_t gid;
89 uid_t uid;
90 mode_t mode;
91 off_t offtmp;
92 mntoptparse_t mo;
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 = false; gid = 0;
105 uidset = false; uid = 0;
106 modeset = false; 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 = true;
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 = true;
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 mo = getmntopts(optarg, mopts, &mntflags, 0);
140 freemntopts(mo);
141 break;
142
143 case 's':
144 if (!dehumanize_off(optarg, &offtmp)) {
145 errx(EXIT_FAILURE, "failed to parse size "
146 "'%s'", optarg);
147 /* NOTREACHED */
148 }
149 args.ta_size_max = offtmp;
150 break;
151
152 case 'u':
153 if (!dehumanize_user(optarg, &uid)) {
154 errx(EXIT_FAILURE, "failed to parse user "
155 "'%s'", optarg);
156 /* NOTREACHED */
157 }
158 uidset = true;
159 break;
160
161 case '?':
162 default:
163 usage();
164 /* NOTREACHED */
165 }
166 }
167 argc -= optind;
168 argv += optind;
169
170 if (argc != 2) {
171 usage();
172 /* NOTREACHED */
173 }
174
175 if (realpath(argv[1], canon_dir) == NULL) {
176 err(EXIT_FAILURE, "realpath %s", argv[0]);
177 /* NOTREACHED */
178 }
179
180 if (strncmp(argv[1], canon_dir, MAXPATHLEN) != 0) {
181 warnx("\"%s\" is a relative path", argv[0]);
182 warnx("using \"%s\" instead", canon_dir);
183 }
184
185 if (stat(canon_dir, &sb) == -1) {
186 err(EXIT_FAILURE, "cannot stat");
187 /* NOTREACHED */
188 }
189 args.ta_root_uid = uidset ? uid : sb.st_uid;
190 args.ta_root_gid = gidset ? gid : sb.st_gid;
191 args.ta_root_mode = modeset ? mode : sb.st_mode;
192
193 if (mount(MOUNT_TMPFS, canon_dir, mntflags, &args)) {
194 err(EXIT_FAILURE, "tmpfs on %s", canon_dir);
195 /* NOTREACHED */
196 }
197 if (mntflags & MNT_GETARGS) {
198 struct passwd *pw;
199 struct group *gr;
200
201 (void)printf("version=%d\n", args.ta_version);
202 (void)printf("size_max=%" PRIuMAX "\n",
203 (uintmax_t)args.ta_size_max);
204 (void)printf("nodes_max=%" PRIuMAX "\n",
205 (uintmax_t)args.ta_nodes_max);
206
207 pw = getpwuid(args.ta_root_uid);
208 if (pw == NULL)
209 (void)printf("root_uid=%" PRIuMAX "\n",
210 (uintmax_t)args.ta_root_uid);
211 else
212 (void)printf("root_uid=%s\n", pw->pw_name);
213
214 gr = getgrgid(args.ta_root_gid);
215 if (gr == NULL)
216 (void)printf("root_gid=%" PRIuMAX "\n",
217 (uintmax_t)args.ta_root_gid);
218 else
219 (void)printf("root_gid=%s\n", gr->gr_name);
220
221 (void)printf("root_mode=%o\n", args.ta_root_mode);
222 }
223
224 return EXIT_SUCCESS;
225 }
226
227 /* --------------------------------------------------------------------- */
228
229 static void
230 usage(void)
231 {
232 (void)fprintf(stderr,
233 "Usage: %s [-g group] [-m mode] [-n nodes] [-o options] [-s size]\n"
234 " [-u user] tmpfs mountpoint\n", getprogname());
235 exit(1);
236 }
237
238 /* --------------------------------------------------------------------- */
239
240 /*
241 * Obtains a GID number based on the contents of 'str'. If it is a string
242 * and is found in group(5), its corresponding ID is used. Otherwise, an
243 * attempt is made to convert the string to a number and use that as a GID.
244 * In case of success, true is returned and *gid holds the GID value.
245 * Otherwise, false is returned and *gid is untouched.
246 */
247 static int
248 dehumanize_group(const char *str, gid_t *gid)
249 {
250 int error;
251 struct group *gr;
252
253 gr = getgrnam(str);
254 if (gr != NULL) {
255 *gid = gr->gr_gid;
256 error = 1;
257 } else {
258 char *ep;
259 unsigned long tmp;
260
261 errno = 0;
262 tmp = strtoul(str, &ep, 0);
263 if (str[0] == '\0' || *ep != '\0')
264 error = 0; /* Not a number. */
265 else if (errno == ERANGE &&
266 (tmp == LONG_MAX || tmp == LONG_MIN))
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 (tmp == LONG_MAX || tmp == LONG_MIN))
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, tmp;
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 = strtol(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 *size = tmp * multiplier;
364
365 return 1;
366 }
367
368 /* --------------------------------------------------------------------- */
369
370 /*
371 * Obtains a UID number based on the contents of 'str'. If it is a string
372 * and is found in passwd(5), its corresponding ID is used. Otherwise, an
373 * attempt is made to convert the string to a number and use that as a UID.
374 * In case of success, true is returned and *uid holds the UID value.
375 * Otherwise, false is returned and *uid is untouched.
376 */
377 static int
378 dehumanize_user(const char *str, uid_t *uid)
379 {
380 int error;
381 struct passwd *pw;
382
383 pw = getpwnam(str);
384 if (pw != NULL) {
385 *uid = pw->pw_uid;
386 error = 1;
387 } else {
388 char *ep;
389 unsigned long tmp;
390
391 errno = 0;
392 tmp = strtoul(str, &ep, 0);
393 if (str[0] == '\0' || *ep != '\0')
394 error = 0; /* Not a number. */
395 else if (errno == ERANGE &&
396 (tmp == LONG_MAX || tmp == LONG_MIN))
397 error = 0; /* Out of range. */
398 else {
399 *uid = (uid_t)tmp;
400 error = 1;
401 }
402 }
403
404 return error;
405 }
406
407 /* --------------------------------------------------------------------- */
408
409 #ifndef MOUNT_NOMAIN
410 int
411 main(int argc, char *argv[])
412 {
413
414 return mount_tmpfs(argc, argv);
415 }
416 #endif
417