mount.c - ubase - suckless linux base utils | |
git clone git://git.suckless.org/ubase | |
Log | |
Files | |
Refs | |
README | |
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 da… | |
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] [t… | |
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) == … | |
263 if (!source) { | |
264 target = me->mnt_dir; | |
265 source = me->mnt_fsname; | |
266 } | |
267 if (!fsopts[0]) | |
268 estrlcat(fsopts, me->mnt_opts, s… | |
269 parseopts(fsopts, &flags, 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… | |
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)) >= siz… | |
304 weprintf("%s: option string too long\n", me->mnt… | |
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_typ… | |
319 if (r > 0 && mount(me->mnt_fsname, me->mnt_dir, me->mnt_… | |
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 } |