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