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 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 }