mount_tmpfs.c revision 1.2 1 /* $NetBSD: mount_tmpfs.c,v 1.2 2005/09/23 12:10:35 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.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __RCSID("$NetBSD: mount_tmpfs.c,v 1.2 2005/09/23 12:10:35 jmmv Exp $");
42 #endif /* not lint */
43
44 #include <sys/param.h>
45 #include <sys/mount.h>
46
47 #include <fs/tmpfs/tmpfs.h>
48
49 #include <ctype.h>
50 #include <err.h>
51 #include <errno.h>
52 #include <grp.h>
53 #include <mntopts.h>
54 #include <pwd.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59
60 /* --------------------------------------------------------------------- */
61
62 static const struct mntopt mopts[] = {
63 MOPT_STDOPTS,
64 { NULL }
65 };
66
67 /* --------------------------------------------------------------------- */
68
69 static int mount_tmpfs(int argc, char **argv);
70 static void usage(void);
71 static int dehumanize_group(const char *str, gid_t *gid);
72 static int dehumanize_mode(const char *str, mode_t *mode);
73 static int dehumanize_off(const char *str, off_t *size);
74 static int dehumanize_user(const char *str, uid_t *uid);
75
76 /* --------------------------------------------------------------------- */
77
78 int
79 mount_tmpfs(int argc, char *argv[])
80 {
81 char canon_dir[MAXPATHLEN];
82 int ch, mntflags;
83 off_t offtmp;
84 mntoptparse_t mo;
85 struct tmpfs_args args;
86
87 setprogname(argv[0]);
88
89 /* Set default values for mount point arguments. */
90 args.ta_version = TMPFS_ARGS_VERSION;
91 args.ta_size_max = 0;
92 args.ta_nodes_max = 0;
93 args.ta_root_uid = getuid();
94 args.ta_root_gid = getgid();
95 args.ta_root_mode = 0755;
96 mntflags = 0;
97
98 optind = optreset = 1;
99 while ((ch = getopt(argc, argv, "g:m:n:o:s:u:")) != -1 ) {
100 switch (ch) {
101 case 'g':
102 if (!dehumanize_group(optarg, &args.ta_root_gid)) {
103 errx(EXIT_FAILURE, "failed to parse group "
104 "'%s'", optarg);
105 /* NOTREACHED */
106 }
107 break;
108
109 case 'm':
110 if (!dehumanize_mode(optarg, &args.ta_root_mode)) {
111 errx(EXIT_FAILURE, "failed to parse mode "
112 "'%s'", optarg);
113 /* NOTREACHED */
114 }
115 break;
116
117 case 'n':
118 if (!dehumanize_off(optarg, &offtmp)) {
119 errx(EXIT_FAILURE, "failed to parse size "
120 "'%s'", optarg);
121 /* NOTREACHED */
122 }
123 args.ta_nodes_max = offtmp;
124 break;
125
126 case 'o':
127 mo = getmntopts(optarg, mopts, &mntflags, 0);
128 freemntopts(mo);
129 break;
130
131 case 's':
132 if (!dehumanize_off(optarg, &offtmp)) {
133 errx(EXIT_FAILURE, "failed to parse size "
134 "'%s'", optarg);
135 /* NOTREACHED */
136 }
137 args.ta_size_max = offtmp;
138 break;
139
140 case 'u':
141 if (!dehumanize_user(optarg, &args.ta_root_uid)) {
142 errx(EXIT_FAILURE, "failed to parse user "
143 "'%s'", optarg);
144 /* NOTREACHED */
145 }
146 break;
147
148 case '?':
149 default:
150 usage();
151 /* NOTREACHED */
152 }
153 }
154 argc -= optind;
155 argv += optind;
156
157 if (argc != 2) {
158 usage();
159 /* NOTREACHED */
160 }
161
162 if (realpath(argv[1], canon_dir) == NULL) {
163 err(EXIT_FAILURE, "realpath %s", argv[0]);
164 /* NOTREACHED */
165 }
166
167 if (strncmp(argv[1], canon_dir, MAXPATHLEN) != 0) {
168 warnx("\"%s\" is a relative path", argv[0]);
169 warnx("using \"%s\" instead", canon_dir);
170 }
171
172 if (mount(MOUNT_TMPFS, canon_dir, mntflags, &args)) {
173 err(EXIT_FAILURE, "tmpfs on %s", canon_dir);
174 /* NOTREACHED */
175 }
176
177 return EXIT_SUCCESS;
178 }
179
180 /* --------------------------------------------------------------------- */
181
182 static void
183 usage(void)
184 {
185 (void)fprintf(stderr,
186 "Usage: %s [-g group] [-m mode] [-n nodes] [-o options] [-s size]\n"
187 " [-u user] tmpfs mountpoint\n", getprogname());
188 exit(1);
189 }
190
191 /* --------------------------------------------------------------------- */
192
193 /*
194 * Obtains a GID number based on the contents of 'str'. If it is a string
195 * and is found in group(5), its corresponding ID is used. Otherwise, an
196 * attempt is made to convert the string to a number and use that as a GID.
197 * In case of success, true is returned and *gid holds the GID value.
198 * Otherwise, false is returned and *gid is untouched.
199 */
200 static int
201 dehumanize_group(const char *str, gid_t *gid)
202 {
203 int error;
204 struct group *gr;
205
206 gr = getgrnam(str);
207 if (gr != NULL) {
208 *gid = gr->gr_gid;
209 error = 1;
210 } else {
211 char *ep;
212 long tmp;
213
214 errno = 0;
215 tmp = strtol(str, &ep, 10);
216 if (str[0] == '\0' || *ep != '\0')
217 error = 0; /* Not a number. */
218 else if (errno == ERANGE &&
219 (tmp == LONG_MAX || tmp == LONG_MIN))
220 error = 0; /* Out of range. */
221 else {
222 *gid = (gid_t)tmp;
223 error = 1;
224 }
225 }
226
227 return error;
228 }
229
230 /* --------------------------------------------------------------------- */
231
232 /*
233 * Obtains a mode number based on the contents of 'str'.
234 * In case of success, true is returned and *mode holds the mode value.
235 * Otherwise, false is returned and *mode is untouched.
236 */
237 static int
238 dehumanize_mode(const char *str, mode_t *mode)
239 {
240 char *ep;
241 int error;
242 long tmp;
243
244 errno = 0;
245 tmp = strtol(str, &ep, 8);
246 if (str[0] == '\0' || *ep != '\0')
247 error = 0; /* Not a number. */
248 else if (errno == ERANGE &&
249 (tmp == LONG_MAX || tmp == LONG_MIN))
250 error = 0; /* Out of range. */
251 else {
252 *mode = (mode_t)tmp;
253 error = 1;
254 }
255
256 return error;
257 }
258
259 /* --------------------------------------------------------------------- */
260
261 /*
262 * Converts the number given in 'str', which may be given in a humanized
263 * form (as described in humanize_number(3), but with some limitations),
264 * to a file size (off_t) without units.
265 * In case of success, true is returned and *size holds the value.
266 * Otherwise, false is returned and *size is untouched.
267 */
268 static int
269 dehumanize_off(const char *str, off_t *size)
270 {
271 char *ep, unit;
272 const char *delimit;
273 size_t len, multiplier, tmp;
274
275 len = strlen(str);
276 if (len < 1)
277 return 0;
278
279 multiplier = 1;
280
281 unit = str[len - 1];
282 if (isalpha((int)unit)) {
283 switch (unit) {
284 case 'b':
285 multiplier = 1;
286 break;
287
288 case 'k':
289 multiplier = 1024;
290 break;
291
292 case 'M':
293 multiplier = 1024 * 1024;
294 break;
295
296 case 'G':
297 multiplier = 1024 * 1024 * 1024;
298 break;
299
300 default:
301 return 0; /* Invalid suffix. */
302 }
303
304 delimit = &str[len - 1];
305 } else
306 delimit = NULL;
307
308 errno = 0;
309 tmp = strtol(str, &ep, 10);
310 if (str[0] == '\0' || (ep != delimit && *ep != '\0'))
311 return 0; /* Not a number. */
312 else if (errno == ERANGE &&
313 (tmp == LONG_MAX || tmp == LONG_MIN))
314 return 0; /* Out of range. */
315
316 *size = tmp * multiplier;
317
318 return 1;
319 }
320
321 /* --------------------------------------------------------------------- */
322
323 /*
324 * Obtains a UID number based on the contents of 'str'. If it is a string
325 * and is found in passwd(5), its corresponding ID is used. Otherwise, an
326 * attempt is made to convert the string to a number and use that as a UID.
327 * In case of success, true is returned and *uid holds the UID value.
328 * Otherwise, false is returned and *uid is untouched.
329 */
330 static int
331 dehumanize_user(const char *str, uid_t *uid)
332 {
333 int error;
334 struct passwd *pw;
335
336 pw = getpwnam(str);
337 if (pw != NULL) {
338 *uid = pw->pw_uid;
339 error = 1;
340 } else {
341 char *ep;
342 long tmp;
343
344 errno = 0;
345 tmp = strtol(str, &ep, 10);
346 if (str[0] == '\0' || *ep != '\0')
347 error = 0; /* Not a number. */
348 else if (errno == ERANGE &&
349 (tmp == LONG_MAX || tmp == LONG_MIN))
350 error = 0; /* Out of range. */
351 else {
352 *uid = (uid_t)tmp;
353 error = 1;
354 }
355 }
356
357 return error;
358 }
359
360 /* --------------------------------------------------------------------- */
361
362 #ifndef MOUNT_NOMAIN
363 int
364 main(int argc, char *argv[])
365 {
366
367 return mount_tmpfs(argc, argv);
368 }
369 #endif
370