master xplshn/aruu / cmd / linux / mount.c
  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}