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