mount_tmpfs.c revision 1.7 1 /* $NetBSD: mount_tmpfs.c,v 1.7 2005/09/25 18:55:51 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.7 2005/09/25 18:55:51 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 { 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 bool gidset, modeset, uidset;
86 int ch, mntflags;
87 gid_t gid;
88 uid_t uid;
89 mode_t mode;
90 off_t offtmp;
91 mntoptparse_t mo;
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 = false; gid = 0;
104 uidset = false; uid = 0;
105 modeset = false; 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 = true;
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 = true;
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 mo = getmntopts(optarg, mopts, &mntflags, 0);
139 freemntopts(mo);
140 break;
141
142 case 's':
143 if (!dehumanize_off(optarg, &offtmp)) {
144 errx(EXIT_FAILURE, "failed to parse size "
145 "'%s'", optarg);
146 /* NOTREACHED */
147 }
148 args.ta_size_max = offtmp;
149 break;
150
151 case 'u':
152 if (!dehumanize_user(optarg, &uid)) {
153 errx(EXIT_FAILURE, "failed to parse user "
154 "'%s'", optarg);
155 /* NOTREACHED */
156 }
157 uidset = true;
158 break;
159
160 case '?':
161 default:
162 usage();
163 /* NOTREACHED */
164 }
165 }
166 argc -= optind;
167 argv += optind;
168
169 if (argc != 2) {
170 usage();
171 /* NOTREACHED */
172 }
173
174 if (realpath(argv[1], canon_dir) == NULL) {
175 err(EXIT_FAILURE, "realpath %s", argv[0]);
176 /* NOTREACHED */
177 }
178
179 if (strncmp(argv[1], canon_dir, MAXPATHLEN) != 0) {
180 warnx("\"%s\" is a relative path", argv[0]);
181 warnx("using \"%s\" instead", canon_dir);
182 }
183
184 if (stat(canon_dir, &sb) == -1) {
185 err(EXIT_FAILURE, "cannot stat");
186 /* NOTREACHED */
187 }
188 args.ta_root_uid = uidset ? uid : sb.st_uid;
189 args.ta_root_gid = gidset ? gid : sb.st_gid;
190 args.ta_root_mode = modeset ? mode : sb.st_mode;
191
192 if (mount(MOUNT_TMPFS, canon_dir, mntflags, &args)) {
193 err(EXIT_FAILURE, "tmpfs on %s", canon_dir);
194 /* NOTREACHED */
195 }
196
197 return EXIT_SUCCESS;
198 }
199
200 /* --------------------------------------------------------------------- */
201
202 static void
203 usage(void)
204 {
205 (void)fprintf(stderr,
206 "Usage: %s [-g group] [-m mode] [-n nodes] [-o options] [-s size]\n"
207 " [-u user] tmpfs mountpoint\n", getprogname());
208 exit(1);
209 }
210
211 /* --------------------------------------------------------------------- */
212
213 /*
214 * Obtains a GID number based on the contents of 'str'. If it is a string
215 * and is found in group(5), its corresponding ID is used. Otherwise, an
216 * attempt is made to convert the string to a number and use that as a GID.
217 * In case of success, true is returned and *gid holds the GID value.
218 * Otherwise, false is returned and *gid is untouched.
219 */
220 static int
221 dehumanize_group(const char *str, gid_t *gid)
222 {
223 int error;
224 struct group *gr;
225
226 gr = getgrnam(str);
227 if (gr != NULL) {
228 *gid = gr->gr_gid;
229 error = 1;
230 } else {
231 char *ep;
232 unsigned long tmp;
233
234 errno = 0;
235 tmp = strtoul(str, &ep, 0);
236 if (str[0] == '\0' || *ep != '\0')
237 error = 0; /* Not a number. */
238 else if (errno == ERANGE &&
239 (tmp == LONG_MAX || tmp == LONG_MIN))
240 error = 0; /* Out of range. */
241 else {
242 *gid = (gid_t)tmp;
243 error = 1;
244 }
245 }
246
247 return error;
248 }
249
250 /* --------------------------------------------------------------------- */
251
252 /*
253 * Obtains a mode number based on the contents of 'str'.
254 * In case of success, true is returned and *mode holds the mode value.
255 * Otherwise, false is returned and *mode is untouched.
256 */
257 static int
258 dehumanize_mode(const char *str, mode_t *mode)
259 {
260 char *ep;
261 int error;
262 long tmp;
263
264 errno = 0;
265 tmp = strtol(str, &ep, 8);
266 if (str[0] == '\0' || *ep != '\0')
267 error = 0; /* Not a number. */
268 else if (errno == ERANGE &&
269 (tmp == LONG_MAX || tmp == LONG_MIN))
270 error = 0; /* Out of range. */
271 else {
272 *mode = (mode_t)tmp;
273 error = 1;
274 }
275
276 return error;
277 }
278
279 /* --------------------------------------------------------------------- */
280
281 /*
282 * Converts the number given in 'str', which may be given in a humanized
283 * form (as described in humanize_number(3), but with some limitations),
284 * to a file size (off_t) without units.
285 * In case of success, true is returned and *size holds the value.
286 * Otherwise, false is returned and *size is untouched.
287 */
288 static int
289 dehumanize_off(const char *str, off_t *size)
290 {
291 char *ep, unit;
292 const char *delimit;
293 long multiplier, tmp;
294 size_t len;
295
296 len = strlen(str);
297 if (len < 1)
298 return 0;
299
300 multiplier = 1;
301
302 unit = str[len - 1];
303 if (isalpha((int)unit)) {
304 switch (tolower((int)unit)) {
305 case 'b':
306 multiplier = 1;
307 break;
308
309 case 'k':
310 multiplier = 1024;
311 break;
312
313 case 'm':
314 multiplier = 1024 * 1024;
315 break;
316
317 case 'g':
318 multiplier = 1024 * 1024 * 1024;
319 break;
320
321 default:
322 return 0; /* Invalid suffix. */
323 }
324
325 delimit = &str[len - 1];
326 } else
327 delimit = NULL;
328
329 errno = 0;
330 tmp = strtol(str, &ep, 10);
331 if (str[0] == '\0' || (ep != delimit && *ep != '\0'))
332 return 0; /* Not a number. */
333 else if (errno == ERANGE && (tmp == LONG_MAX || tmp == LONG_MIN))
334 return 0; /* Out of range. */
335
336 *size = tmp * multiplier;
337
338 return 1;
339 }
340
341 /* --------------------------------------------------------------------- */
342
343 /*
344 * Obtains a UID number based on the contents of 'str'. If it is a string
345 * and is found in passwd(5), its corresponding ID is used. Otherwise, an
346 * attempt is made to convert the string to a number and use that as a UID.
347 * In case of success, true is returned and *uid holds the UID value.
348 * Otherwise, false is returned and *uid is untouched.
349 */
350 static int
351 dehumanize_user(const char *str, uid_t *uid)
352 {
353 int error;
354 struct passwd *pw;
355
356 pw = getpwnam(str);
357 if (pw != NULL) {
358 *uid = pw->pw_uid;
359 error = 1;
360 } else {
361 char *ep;
362 unsigned long tmp;
363
364 errno = 0;
365 tmp = strtoul(str, &ep, 0);
366 if (str[0] == '\0' || *ep != '\0')
367 error = 0; /* Not a number. */
368 else if (errno == ERANGE &&
369 (tmp == LONG_MAX || tmp == LONG_MIN))
370 error = 0; /* Out of range. */
371 else {
372 *uid = (uid_t)tmp;
373 error = 1;
374 }
375 }
376
377 return error;
378 }
379
380 /* --------------------------------------------------------------------- */
381
382 #ifndef MOUNT_NOMAIN
383 int
384 main(int argc, char *argv[])
385 {
386
387 return mount_tmpfs(argc, argv);
388 }
389 #endif
390