mount_tmpfs.c revision 1.11 1 /* $NetBSD: mount_tmpfs.c,v 1.11 2006/02/11 05:49:48 christos 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.11 2006/02/11 05:49:48 christos Exp $");
43 #endif /* not lint */
44
45 #define __POOL_EXPOSE
46 #include <sys/param.h>
47 #include <sys/mount.h>
48 #include <sys/stat.h>
49
50 #include <fs/tmpfs/tmpfs.h>
51
52 #include <ctype.h>
53 #include <err.h>
54 #include <errno.h>
55 #include <grp.h>
56 #include <mntopts.h>
57 #include <pwd.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 int gidset, modeset, uidset; /* Ought to be 'bool'. */
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 = 0; gid = 0;
105 uidset = 0; uid = 0;
106 modeset = 0; 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 = 1;
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 = 1;
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 = 1;
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;
321 long long tmp, tmp2;
322 size_t len;
323
324 len = strlen(str);
325 if (len < 1)
326 return 0;
327
328 multiplier = 1;
329
330 unit = str[len - 1];
331 if (isalpha((int)unit)) {
332 switch (tolower((int)unit)) {
333 case 'b':
334 multiplier = 1;
335 break;
336
337 case 'k':
338 multiplier = 1024;
339 break;
340
341 case 'm':
342 multiplier = 1024 * 1024;
343 break;
344
345 case 'g':
346 multiplier = 1024 * 1024 * 1024;
347 break;
348
349 default:
350 return 0; /* Invalid suffix. */
351 }
352
353 delimit = &str[len - 1];
354 } else
355 delimit = NULL;
356
357 errno = 0;
358 tmp = strtoll(str, &ep, 10);
359 if (str[0] == '\0' || (ep != delimit && *ep != '\0'))
360 return 0; /* Not a number. */
361 else if (errno == ERANGE && (tmp == LONG_MAX || tmp == LONG_MIN))
362 return 0; /* Out of range. */
363
364 tmp2 = tmp * multiplier;
365 tmp2 = tmp2 / multiplier;
366 if (tmp != tmp2)
367 return 0; /* Out of range. */
368 *size = tmp * multiplier;
369
370 return 1;
371 }
372
373 /* --------------------------------------------------------------------- */
374
375 /*
376 * Obtains a UID number based on the contents of 'str'. If it is a string
377 * and is found in passwd(5), its corresponding ID is used. Otherwise, an
378 * attempt is made to convert the string to a number and use that as a UID.
379 * In case of success, true is returned and *uid holds the UID value.
380 * Otherwise, false is returned and *uid is untouched.
381 */
382 static int
383 dehumanize_user(const char *str, uid_t *uid)
384 {
385 int error;
386 struct passwd *pw;
387
388 pw = getpwnam(str);
389 if (pw != NULL) {
390 *uid = pw->pw_uid;
391 error = 1;
392 } else {
393 char *ep;
394 unsigned long tmp;
395
396 errno = 0;
397 tmp = strtoul(str, &ep, 0);
398 if (str[0] == '\0' || *ep != '\0')
399 error = 0; /* Not a number. */
400 else if (errno == ERANGE &&
401 (tmp == LONG_MAX || tmp == LONG_MIN))
402 error = 0; /* Out of range. */
403 else {
404 *uid = (uid_t)tmp;
405 error = 1;
406 }
407 }
408
409 return error;
410 }
411
412 /* --------------------------------------------------------------------- */
413
414 #ifndef MOUNT_NOMAIN
415 int
416 main(int argc, char *argv[])
417 {
418
419 return mount_tmpfs(argc, argv);
420 }
421 #endif
422