1/* See LICENSE file for copyright and license details. */
2
3#include <sys/mount.h>
4#include <sys/stat.h>
5#include <sys/types.h>
6#include <sys/wait.h>
7
8#include <errno.h>
9#include <limits.h>
10#include <mntent.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <unistd.h>
15
16#include "paths.h"
17#include "text.h"
18#include "util.h"
19#include "wexec.h"
20
21#define FSOPTS_MAXLEN 512
22
23struct {
24 const char *opt;
25 const char *notopt;
26 unsigned long v;
27} optnames[] = {
28 {"defaults", NULL, 0},
29 {"remount", NULL, MS_REMOUNT},
30 {"ro", "rw", MS_RDONLY},
31 {"sync", "async", MS_SYNCHRONOUS},
32 {"dirsync", NULL, MS_DIRSYNC},
33 {"nodev", "dev", MS_NODEV},
34 {"noatime", "atime", MS_NOATIME},
35 {"noauto", "auto", 0},
36 {"nodiratime", "diratime", MS_NODIRATIME},
37 {"noexec", "exec", MS_NOEXEC},
38 {"nosuid", "suid", MS_NOSUID},
39 {"mand", "nomand", MS_MANDLOCK},
40 {"relatime", "norelatime", MS_RELATIME},
41 {"bind", NULL, MS_BIND},
42 {NULL, NULL, 0}
43};
44
45static unsigned long argflags = 0;
46static char fsopts[FSOPTS_MAXLEN] = "";
47
48static char *
49findtype(const char *types, const char *t)
50{
51 const char *p;
52 size_t len;
53
54 for (len = strlen(t); (p = strstr(types, t)); types = p + len) {
55 if (!strncmp(p, t, len) && (p[len] == '\0' || p[len] == ','))
56 return (char *)p;
57 }
58 return NULL;
59}
60
61static void
62parseopts(const char *popts, unsigned long *flags, char *data, size_t datasiz)
63{
64 unsigned int i, validopt;
65 size_t optlen, dlen = 0;
66 const char *name, *e;
67
68 name = popts;
69 data[0] = '\0';
70 do {
71 if ((e = strstr(name, ",")))
72 optlen = e - name;
73 else
74 optlen = strlen(name);
75
76 validopt = 0;
77 for (i = 0; optnames[i].opt; i++) {
78 if (optnames[i].opt && !strncmp(name, optnames[i].opt, optlen)) {
79 *flags |= optnames[i].v;
80 validopt = 1;
81 break;
82 }
83 if (optnames[i].notopt && !strncmp(name, optnames[i].notopt, optlen)) {
84 *flags &= ~optnames[i].v;
85 validopt = 1;
86 break;
87 }
88 }
89
90 if (!validopt && optlen > 0) {
91 /* unknown option, pass as data option to mount() */
92 if (dlen + optlen + 2 >= datasiz)
93 return; /* prevent overflow */
94 if (dlen)
95 data[dlen++] = ',';
96 memcpy(&data[dlen], name, optlen);
97 dlen += optlen;
98 data[dlen] = '\0';
99 }
100 name = e + 1;
101 } while (e);
102}
103
104static int
105mounthelper(const char *fsname, const char *dir, const char *fstype)
106{
107 pid_t pid;
108 char eprog[PATH_MAX];
109 char const *eargv[10];
110 int status, i;
111
112 pid = fork();
113 switch (pid) {
114 case -1:
115 break;
116 case 0:
117 snprintf(eprog, sizeof(eprog), "mount.%s", fstype);
118
119 i = 0;
120 eargv[i++] = eprog;
121 if (argflags & MS_BIND)
122 eargv[i++] = "-B";
123 if (argflags & MS_MOVE)
124 eargv[i++] = "-M";
125 if (argflags & MS_REC)
126 eargv[i++] = "-R";
127
128 if (fsopts[0]) {
129 eargv[i++] = "-o";
130 eargv[i++] = fsopts;
131 }
132 eargv[i++] = fsname;
133 eargv[i++] = dir;
134 eargv[i] = NULL;
135
136 wexecvp_self(eprog, (char *const *)eargv);
137 if (errno == ENOENT)
138 _exit(1);
139 weprintf("wexecvp:");
140 _exit(1);
141 break;
142 default:
143 if (waitpid(pid, &status, 0) < 0) {
144 weprintf("waitpid:");
145 return -1;
146 }
147 if (WIFEXITED(status))
148 return WEXITSTATUS(status);
149 else if (WIFSIGNALED(status))
150 return 1;
151 break;
152 }
153 return 0;
154}
155
156static int
157mounted(const char *dir)
158{
159 FILE *fp;
160 struct mntent *me, mebuf;
161 struct stat st1, st2;
162 char linebuf[256];
163
164 if (stat(dir, &st1) < 0) {
165 weprintf("stat %s:", dir);
166 return 0;
167 }
168 if (!(fp = setmntent(ARUU_LINUX_PATH_PROC_MOUNTS, "r")))
169 eprintf("setmntent %s:", ARUU_LINUX_PATH_PROC_MOUNTS);
170
171 while ((me = getmntent_r(fp, &mebuf, linebuf, sizeof(linebuf)))) {
172 if (stat(me->mnt_dir, &st2) < 0) {
173 weprintf("stat %s:", me->mnt_dir);
174 continue;
175 }
176 if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
177 return 1;
178 }
179 endmntent(fp);
180
181 return 0;
182}
183
184static void
185usage(void)
186{
187 eprintf(
188 "usage: %s [-BMRan] [-t fstype] [-o options] [source] "
189 "[target]\n",
190 argv0
191 );
192}
193
194// ?man mount: mount a filesystem
195// ?man arguments: source [target]
196// ?man mount a filesystem to the directory tree
197int
198main(int argc, char *argv[])
199{
200 char *types = NULL, data[FSOPTS_MAXLEN] = "", *resolvpath = NULL;
201 char *files[] = {ARUU_LINUX_PATH_PROC_MOUNTS, "/etc/fstab", NULL};
202 const char *source, *target;
203 struct mntent *me = NULL;
204 int aflag = 0, status = 0, i, r;
205 unsigned long flags = 0;
206 FILE *fp;
207
208 ARGBEGIN
209 {
210 // ?man -B: specify option flag
211 case 'B':
212 argflags |= MS_BIND;
213 break;
214 // ?man -M: specify option flag
215 case 'M':
216 argflags |= MS_MOVE;
217 break;
218 // ?man -R: operate recursively on directories
219 case 'R':
220 argflags |= MS_REC;
221 break;
222 // ?man -a: print or show all entries
223 case 'a':
224 aflag = 1;
225 break;
226 // ?man -o:str: specify output file
227 case 'o':
228 estrlcat(fsopts, EARGF(usage()), sizeof(fsopts));
229 parseopts(fsopts, &flags, data, sizeof(data));
230 break;
231 // ?man -t:str: sort or specify timestamp
232 case 't':
233 types = EARGF(usage());
234 break;
235 // ?man -n: print line numbers or counts
236 case 'n':
237 break;
238 default:
239 usage();
240 }
241 ARGEND;
242
243 if (argc < 1 && aflag == 0) {
244 if (!(fp = fopen(files[0], "r")))
245 eprintf("fopen %s:", files[0]);
246 fconcat(fp, files[0], stdout, "<stdout>");
247 fclose(fp);
248 return 0;
249 }
250
251 if (aflag == 1)
252 goto mountall;
253
254 source = argv[0];
255 target = argv[1];
256
257 if (!target) {
258 target = argv[0];
259 source = NULL;
260 if (strcmp(target, "/") != 0) {
261 if (!(resolvpath = realpath(target, NULL)))
262 eprintf("realpath %s:", target);
263 target = resolvpath;
264 }
265 }
266
267 for (i = 0; files[i]; i++) {
268 if (!(fp = setmntent(files[i], "r"))) {
269 if (strcmp(files[i], ARUU_LINUX_PATH_PROC_MOUNTS) != 0)
270 weprintf("setmntent %s:", files[i]);
271 continue;
272 }
273 while ((me = getmntent(fp))) {
274 if (strcmp(me->mnt_dir, target) == 0 || strcmp(me->mnt_fsname, target) == 0
275 || (source && strcmp(me->mnt_dir, source) == 0)
276 || (source && strcmp(me->mnt_fsname, source) == 0)) {
277 if (!source) {
278 target = me->mnt_dir;
279 source = me->mnt_fsname;
280 }
281 if (!fsopts[0]) {
282 estrlcat(fsopts, me->mnt_opts, sizeof(fsopts));
283 parseopts(fsopts, &flags, data, sizeof(data));
284 }
285 if (!types)
286 types = me->mnt_type;
287 goto mountsingle;
288 }
289 }
290 endmntent(fp);
291 fp = NULL;
292 }
293 if (!source)
294 eprintf("can't find %s in /etc/fstab\n", target);
295
296mountsingle:
297 r = mounthelper(source, target, types);
298 if (r == -1)
299 status = 1;
300 if (r > 0 && mount(source, target, types, argflags | flags, data) < 0) {
301 weprintf("mount: %s:", source);
302 status = 1;
303 }
304 if (fp)
305 endmntent(fp);
306 free(resolvpath);
307 return status;
308
309mountall:
310 if (!(fp = setmntent("/etc/fstab", "r")))
311 eprintf("setmntent %s:", "/etc/fstab");
312 while ((me = getmntent(fp))) {
313 /* has "noauto" option or already mounted: skip */
314 if (hasmntopt(me, MNTOPT_NOAUTO) || mounted(me->mnt_dir))
315 continue;
316 flags = 0;
317 fsopts[0] = '\0';
318 if (strlcat(fsopts, me->mnt_opts, sizeof(fsopts)) >= sizeof(fsopts)) {
319 weprintf("%s: option string too long\n", me->mnt_dir);
320 status = 1;
321 continue;
322 }
323 parseopts(fsopts, &flags, data, sizeof(data));
324 /* if -t types specified:
325 * if non-match, skip
326 * if match and prefixed with "no", skip */
327 if (types
328 && ((types[0] == 'n' && types[1] == 'o' && findtype(types + 2, me->mnt_type))
329 || (!findtype(types, me->mnt_type))))
330 continue;
331
332 r = mounthelper(me->mnt_fsname, me->mnt_dir, me->mnt_type);
333 if (r > 0 && mount(me->mnt_fsname, me->mnt_dir, me->mnt_type, argflags | flags, data) < 0) {
334 weprintf("mount: %s:", me->mnt_fsname);
335 status = 1;
336 }
337 }
338 endmntent(fp);
339
340 return status;
341}