f5336d9852531adf10905a93412d79c3d611878c
[dragonfly.git] / sbin / mount_tmpfs / mount_tmpfs.c
1 /*      $NetBSD: mount_tmpfs.c,v 1.24 2008/08/05 20:57:45 pooka 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  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <sys/param.h>
34 #include <sys/mount.h>
35 #include <sys/stat.h>
36
37 #include <vfs/tmpfs/tmpfs_args.h>
38
39 #include <ctype.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <grp.h>
43 #include <mntopts.h>
44 #include <pwd.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <sysexits.h>
49 #include <unistd.h>
50 #include <inttypes.h>
51 #include <libutil.h>
52
53 #include "defs.h"
54 #include "mount_tmpfs.h"
55
56 /* --------------------------------------------------------------------- */
57
58 #define MOPT_TMPFSOPTS  \
59         { "gid=",       0,      MNT_GID, 1},    \
60         { "uid=",       0,      MNT_UID, 1},    \
61         { "mode=",      0,      MNT_MODE, 1},   \
62         { "inodes=",    0,      MNT_INODES, 1}, \
63         { "size=",      0,      MNT_SIZE, 1},   \
64         { "maxfilesize=",       0,      MNT_MAXFSIZE, 1}
65
66
67 static const struct mntopt mopts[] = {
68         MOPT_STDOPTS,
69         MOPT_TMPFSOPTS,
70         MOPT_NULL
71 };
72
73 static int Cflag;
74
75 /* --------------------------------------------------------------------- */
76
77 static gid_t    a_gid(char *);
78 static uid_t    a_uid(char *);
79 static mode_t   a_mask(char *);
80 static int64_t a_number(char *s);
81 static void     usage(void) __dead2;
82
83 /* --------------------------------------------------------------------- */
84
85 void
86 mount_tmpfs_parseargs(int argc, char *argv[],
87         struct tmpfs_args *args, int *mntflags,
88         char *canon_dev, char *canon_dir)
89 {
90         int gidset, modeset, uidset; /* Ought to be 'bool'. */
91         int ch;
92         gid_t gid;
93         uid_t uid;
94         mode_t mode;
95         struct stat sb;
96         int extend_flags = 0;
97         char *ptr, *delim;
98
99         /* Set default values for mount point arguments. */
100         memset(args, 0, sizeof(*args));
101         args->ta_version = TMPFS_ARGS_VERSION;
102         args->ta_size_max = 0;
103         args->ta_nodes_max = 0;
104         args->ta_maxfsize_max = 0;
105         *mntflags = 0;
106
107         gidset = 0; gid = 0;
108         uidset = 0; uid = 0;
109         modeset = 0; mode = 0;
110
111         optind = optreset = 1;
112         while ((ch = getopt(argc, argv, "Cf:g:m:n:o:s:u:")) != -1 ) {
113                 switch (ch) {
114                 case 'C':
115                         Cflag = 1;
116                         break;
117                 case 'f':
118                         args->ta_maxfsize_max = a_number(optarg);
119                         break;
120
121                 case 'g':
122                         gid = a_gid(optarg);
123                         gidset = 1;
124                         break;
125
126                 case 'm':
127                         mode = a_mask(optarg);
128                         modeset = 1;
129                         break;
130
131                 case 'n':
132                         args->ta_nodes_max = a_number(optarg);
133                         break;
134
135                 case 'o':
136                         getmntopts(optarg, mopts, mntflags, &extend_flags);
137                         if (extend_flags & MNT_GID) {
138                                 ptr = strstr(optarg, "gid=");
139                                 if(ptr) {
140                                         delim = strstr(ptr, ",");
141                                         if (delim) {
142                                                 *delim = '\0';
143                                                 gid = a_gid(ptr + 4);
144                                                 *delim = ',';
145                                         } else
146                                                 gid = a_gid(ptr + 4);
147                                         gidset = 1;
148                                 }
149                                 extend_flags ^= MNT_GID;
150                         }
151                         if (extend_flags & MNT_UID) {
152                                 ptr = strstr(optarg, "uid=");
153                                 if(ptr) {
154                                         delim = strstr(ptr, ",");
155                                         if (delim) {
156                                                 *delim = '\0';
157                                                 uid = a_uid(ptr + 4);
158                                                 *delim = ',';
159                                         } else
160                                                 uid = a_uid(ptr + 4);
161                                         uidset = 1;
162                                 }
163                                 extend_flags ^= MNT_UID;
164                         }
165                         if (extend_flags & MNT_MODE) {
166                                 ptr = strstr(optarg, "mode=");
167                                 if(ptr) {
168                                         delim = strstr(ptr, ",");
169                                         if (delim) {
170                                                 *delim = '\0';
171                                                 mode = a_mask(ptr + 5);
172                                                 *delim = ',';
173                                         } else
174                                                 mode = a_mask(ptr + 5);
175                                         modeset = 1;
176                                 }
177                                 extend_flags ^= MNT_MODE;
178                         }
179                         if (extend_flags & MNT_INODES) {
180                                 ptr = strstr(optarg, "inodes=");
181                                 if(ptr) {
182                                         delim = strstr(ptr, ",");
183                                         if (delim) {
184                                                 *delim = '\0';
185                                                 args->ta_nodes_max = a_number(ptr + 7);
186                                                 *delim = ',';
187                                         } else
188                                                 args->ta_nodes_max = a_number(ptr + 7);
189                                 }
190                                 extend_flags ^= MNT_INODES;
191                         }
192                         if (extend_flags & MNT_SIZE) {
193                                 ptr = strstr(optarg, "size=");
194                                 if(ptr) {
195                                         delim = strstr(ptr, ",");
196                                         if (delim) {
197                                                 *delim = '\0';
198                                                 args->ta_size_max = a_number(ptr + 5);
199                                                 *delim = ',';
200                                         } else
201                                                 args->ta_size_max = a_number(ptr + 5);
202                                 }
203                                 extend_flags ^= MNT_SIZE;
204                         }
205                         if (extend_flags & MNT_MAXFSIZE) {
206                                 ptr = strstr(optarg, "maxfilesize=");
207                                 if(ptr) {
208                                         delim = strstr(ptr, ",");
209                                         if (delim) {
210                                                 *delim = '\0';
211                                                 args->ta_maxfsize_max = a_number(ptr + 12);
212                                                 *delim = ',';
213                                         } else
214                                                 args->ta_maxfsize_max = a_number(ptr + 12);
215                                 }
216                                 extend_flags ^= MNT_MAXFSIZE;
217                         }
218                         break;
219
220                 case 's':
221                         args->ta_size_max = a_number(optarg);
222                         break;
223
224                 case 'u':
225                         uid = a_uid(optarg);
226                         uidset = 1;
227                         break;
228
229                 case '?':
230                 default:
231                         usage();
232                 }
233         }
234         argc -= optind;
235         argv += optind;
236
237         if (argc != 2)
238                 usage();
239
240         strlcpy(canon_dev, argv[0], MAXPATHLEN);
241         strlcpy(canon_dir, argv[1], MAXPATHLEN);
242
243         if (stat(canon_dir, &sb) == -1)
244                 err(EXIT_FAILURE, "cannot stat `%s'", canon_dir);
245
246         args->ta_root_uid = uidset ? uid : sb.st_uid;
247         args->ta_root_gid = gidset ? gid : sb.st_gid;
248         args->ta_root_mode = modeset ? mode : sb.st_mode;
249 }
250
251 /* --------------------------------------------------------------------- */
252
253 static gid_t
254 a_gid(char *s)
255 {
256         struct group *gr;
257         char *gname;
258         gid_t gid;
259
260         if ((gr = getgrnam(s)) != NULL)
261                 gid = gr->gr_gid;
262         else {
263                 for (gname = s; *s && isdigit(*s); ++s);
264                 if (!*s)
265                         gid = atoi(gname);
266                 else
267                         errx(EX_NOUSER, "unknown group id: %s", gname);
268         }
269         return (gid);
270 }
271
272 static uid_t
273 a_uid(char *s)
274 {
275         struct passwd *pw;
276         char *uname;
277         uid_t uid;
278
279         if ((pw = getpwnam(s)) != NULL)
280                 uid = pw->pw_uid;
281         else {
282                 for (uname = s; *s && isdigit(*s); ++s);
283                 if (!*s)
284                         uid = atoi(uname);
285                 else
286                         errx(EX_NOUSER, "unknown user id: %s", uname);
287         }
288         return (uid);
289 }
290
291 static mode_t
292 a_mask(char *s)
293 {
294         int done, rv = 0;
295         char *ep;
296
297         done = 0;
298         if (*s >= '0' && *s <= '7') {
299                 done = 1;
300                 rv = strtol(s, &ep, 8);
301         }
302         if (!done || rv < 0 || *ep)
303                 errx(EX_USAGE, "invalid file mode: %s", s);
304         return (rv);
305 }
306
307 static int64_t
308 a_number(char *s)
309 {
310         int64_t rv = 0;
311
312         if (dehumanize_number(s, &rv) < 0 || rv < 0)
313                 errx(EX_USAGE, "bad number for option: %s", s);
314         return (rv);
315 }
316
317 static void
318 usage(void)
319 {
320         fprintf(stderr,
321             "Usage: %s [-C] [-g group] [-m mode] [-n nodes] [-o options] [-s size]\n"
322             "           [-u user] [-f maxfilesize] tmpfs mountpoint\n", getprogname());
323         exit(1);
324 }
325
326 /* --------------------------------------------------------------------- */
327
328 int
329 mount_tmpfs(int argc, char *argv[])
330 {
331         struct tmpfs_args args;
332         char canon_dev[MAXPATHLEN], canon_dir[MAXPATHLEN];
333         int mntflags;
334         struct vfsconf vfc;
335         int error;
336         fsnode_t copyroot = NULL, copyhlinks;
337
338         mount_tmpfs_parseargs(argc, argv, &args, &mntflags,
339             canon_dev, canon_dir);
340
341         error = getvfsbyname("tmpfs", &vfc);
342         if (error && vfsisloadable("tmpfs")) {
343                 if(vfsload("tmpfs"))
344                         err(EX_OSERR, "vfsload(%s)", "tmpfs");
345                 endvfsent();
346                 error = getvfsbyname("tmpfs", &vfc);
347         }
348         if (error)
349                 errx(EX_OSERR, "%s filesystem not available", "tmpfs");
350
351         if (Cflag)
352                 copyroot = FSCopy(&copyhlinks, canon_dir);
353
354         if (mount(vfc.vfc_name, canon_dir, mntflags, &args) == -1)
355                 err(EXIT_FAILURE, "tmpfs on %s", canon_dir);
356
357         if (Cflag)
358                 FSPaste(canon_dir, copyroot, copyhlinks);
359
360         return EXIT_SUCCESS;
361 }
362
363 #ifndef MOUNT_NOMAIN
364 int
365 main(int argc, char *argv[])
366 {
367         setprogname(argv[0]);
368         return mount_tmpfs(argc, argv);
369 }
370 #endif