mount_tmpfs.c revision 1.3 1 /* $NetBSD: mount_tmpfs.c,v 1.3 2005/09/23 15:36:16 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.3 2005/09/23 15:36:16 jmmv Exp $");
43 #endif /* not lint */
44
45 #include <sys/param.h>
46 #include <sys/mount.h>
47
48 #include <fs/tmpfs/tmpfs.h>
49
50 #include <ctype.h>
51 #include <err.h>
52 #include <errno.h>
53 #include <grp.h>
54 #include <mntopts.h>
55 #include <pwd.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60
61 /* --------------------------------------------------------------------- */
62
63 static const struct mntopt mopts[] = {
64 MOPT_STDOPTS,
65 { NULL }
66 };
67
68 /* --------------------------------------------------------------------- */
69
70 static int mount_tmpfs(int argc, char **argv);
71 static void usage(void);
72 static int dehumanize_group(const char *str, gid_t *gid);
73 static int dehumanize_mode(const char *str, mode_t *mode);
74 static int dehumanize_off(const char *str, off_t *size);
75 static int dehumanize_user(const char *str, uid_t *uid);
76
77 /* --------------------------------------------------------------------- */
78
79 int
80 mount_tmpfs(int argc, char *argv[])
81 {
82 char canon_dir[MAXPATHLEN];
83 int ch, mntflags;
84 off_t offtmp;
85 mntoptparse_t mo;
86 struct tmpfs_args args;
87
88 setprogname(argv[0]);
89
90 /* Set default values for mount point arguments. */
91 args.ta_version = TMPFS_ARGS_VERSION;
92 args.ta_size_max = 0;
93 args.ta_nodes_max = 0;
94 args.ta_root_uid = getuid();
95 args.ta_root_gid = getgid();
96 args.ta_root_mode = 0755;
97 mntflags = 0;
98
99 optind = optreset = 1;
100 while ((ch = getopt(argc, argv, "g:m:n:o:s:u:")) != -1 ) {
101 switch (ch) {
102 case 'g':
103 if (!dehumanize_group(optarg, &args.ta_root_gid)) {
104 errx(EXIT_FAILURE, "failed to parse group "
105 "'%s'", optarg);
106 /* NOTREACHED */
107 }
108 break;
109
110 case 'm':
111 if (!dehumanize_mode(optarg, &args.ta_root_mode)) {
112 errx(EXIT_FAILURE, "failed to parse mode "
113 "'%s'", optarg);
114 /* NOTREACHED */
115 }
116 break;
117
118 case 'n':
119 if (!dehumanize_off(optarg, &offtmp)) {
120 errx(EXIT_FAILURE, "failed to parse size "
121 "'%s'", optarg);
122 /* NOTREACHED */
123 }
124 args.ta_nodes_max = offtmp;
125 break;
126
127 case 'o':
128 mo = getmntopts(optarg, mopts, &mntflags, 0);
129 freemntopts(mo);
130 break;
131
132 case 's':
133 if (!dehumanize_off(optarg, &offtmp)) {
134 errx(EXIT_FAILURE, "failed to parse size "
135 "'%s'", optarg);
136 /* NOTREACHED */
137 }
138 args.ta_size_max = offtmp;
139 break;
140
141 case 'u':
142 if (!dehumanize_user(optarg, &args.ta_root_uid)) {
143 errx(EXIT_FAILURE, "failed to parse user "
144 "'%s'", optarg);
145 /* NOTREACHED */
146 }
147 break;
148
149 case '?':
150 default:
151 usage();
152 /* NOTREACHED */
153 }
154 }
155 argc -= optind;
156 argv += optind;
157
158 if (argc != 2) {
159 usage();
160 /* NOTREACHED */
161 }
162
163 if (realpath(argv[1], canon_dir) == NULL) {
164 err(EXIT_FAILURE, "realpath %s", argv[0]);
165 /* NOTREACHED */
166 }
167
168 if (strncmp(argv[1], canon_dir, MAXPATHLEN) != 0) {
169 warnx("\"%s\" is a relative path", argv[0]);
170 warnx("using \"%s\" instead", canon_dir);
171 }
172
173 if (mount(MOUNT_TMPFS, canon_dir, mntflags, &args)) {
174 err(EXIT_FAILURE, "tmpfs on %s", canon_dir);
175 /* NOTREACHED */
176 }
177
178 return EXIT_SUCCESS;
179 }
180
181 /* --------------------------------------------------------------------- */
182
183 static void
184 usage(void)
185 {
186 (void)fprintf(stderr,
187 "Usage: %s [-g group] [-m mode] [-n nodes] [-o options] [-s size]\n"
188 " [-u user] tmpfs mountpoint\n", getprogname());
189 exit(1);
190 }
191
192 /* --------------------------------------------------------------------- */
193
194 /*
195 * Obtains a GID number based on the contents of 'str'. If it is a string
196 * and is found in group(5), its corresponding ID is used. Otherwise, an
197 * attempt is made to convert the string to a number and use that as a GID.
198 * In case of success, true is returned and *gid holds the GID value.
199 * Otherwise, false is returned and *gid is untouched.
200 */
201 static int
202 dehumanize_group(const char *str, gid_t *gid)
203 {
204 int error;
205 struct group *gr;
206
207 gr = getgrnam(str);
208 if (gr != NULL) {
209 *gid = gr->gr_gid;
210 error = 1;
211 } else {
212 char *ep;
213 long tmp;
214
215 errno = 0;
216 tmp = strtol(str, &ep, 10);
217 if (str[0] == '\0' || *ep != '\0')
218 error = 0; /* Not a number. */
219 else if (errno == ERANGE &&
220 (tmp == LONG_MAX || tmp == LONG_MIN))
221 error = 0; /* Out of range. */
222 else {
223 *gid = (gid_t)tmp;
224 error = 1;
225 }
226 }
227
228 return error;
229 }
230
231 /* --------------------------------------------------------------------- */
232
233 /*
234 * Obtains a mode number based on the contents of 'str'.
235 * In case of success, true is returned and *mode holds the mode value.
236 * Otherwise, false is returned and *mode is untouched.
237 */
238 static int
239 dehumanize_mode(const char *str, mode_t *mode)
240 {
241 char *ep;
242 int error;
243 long tmp;
244
245 errno = 0;
246 tmp = strtol(str, &ep, 8);
247 if (str[0] == '\0' || *ep != '\0')
248 error = 0; /* Not a number. */
249 else if (errno == ERANGE &&
250 (tmp == LONG_MAX || tmp == LONG_MIN))
251 error = 0; /* Out of range. */
252 else {
253 *mode = (mode_t)tmp;
254 error = 1;
255 }
256
257 return error;
258 }
259
260 /* --------------------------------------------------------------------- */
261
262 /*
263 * Converts the number given in 'str', which may be given in a humanized
264 * form (as described in humanize_number(3), but with some limitations),
265 * to a file size (off_t) without units.
266 * In case of success, true is returned and *size holds the value.
267 * Otherwise, false is returned and *size is untouched.
268 */
269 static int
270 dehumanize_off(const char *str, off_t *size)
271 {
272 char *ep, unit;
273 const char *delimit;
274 size_t len, multiplier, tmp;
275
276 len = strlen(str);
277 if (len < 1)
278 return 0;
279
280 multiplier = 1;
281
282 unit = str[len - 1];
283 if (isalpha((int)unit)) {
284 switch (unit) {
285 case 'b':
286 multiplier = 1;
287 break;
288
289 case 'k':
290 multiplier = 1024;
291 break;
292
293 case 'M':
294 multiplier = 1024 * 1024;
295 break;
296
297 case 'G':
298 multiplier = 1024 * 1024 * 1024;
299 break;
300
301 default:
302 return 0; /* Invalid suffix. */
303 }
304
305 delimit = &str[len - 1];
306 } else
307 delimit = NULL;
308
309 errno = 0;
310 tmp = strtol(str, &ep, 10);
311 if (str[0] == '\0' || (ep != delimit && *ep != '\0'))
312 return 0; /* Not a number. */
313 else if (errno == ERANGE &&
314 (tmp == LONG_MAX || tmp == LONG_MIN))
315 return 0; /* Out of range. */
316
317 *size = tmp * multiplier;
318
319 return 1;
320 }
321
322 /* --------------------------------------------------------------------- */
323
324 /*
325 * Obtains a UID number based on the contents of 'str'. If it is a string
326 * and is found in passwd(5), its corresponding ID is used. Otherwise, an
327 * attempt is made to convert the string to a number and use that as a UID.
328 * In case of success, true is returned and *uid holds the UID value.
329 * Otherwise, false is returned and *uid is untouched.
330 */
331 static int
332 dehumanize_user(const char *str, uid_t *uid)
333 {
334 int error;
335 struct passwd *pw;
336
337 pw = getpwnam(str);
338 if (pw != NULL) {
339 *uid = pw->pw_uid;
340 error = 1;
341 } else {
342 char *ep;
343 long tmp;
344
345 errno = 0;
346 tmp = strtol(str, &ep, 10);
347 if (str[0] == '\0' || *ep != '\0')
348 error = 0; /* Not a number. */
349 else if (errno == ERANGE &&
350 (tmp == LONG_MAX || tmp == LONG_MIN))
351 error = 0; /* Out of range. */
352 else {
353 *uid = (uid_t)tmp;
354 error = 1;
355 }
356 }
357
358 return error;
359 }
360
361 /* --------------------------------------------------------------------- */
362
363 #ifndef MOUNT_NOMAIN
364 int
365 main(int argc, char *argv[])
366 {
367
368 return mount_tmpfs(argc, argv);
369 }
370 #endif
371