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