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