mount_tmpfs.c revision 1.15 1 /* $NetBSD: mount_tmpfs.c,v 1.15 2006/10/16 03:37:43 christos 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.15 2006/10/16 03:37:43 christos 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 MOPT_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