1414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley/* mount.c - mount filesystems
2414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley *
3414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley * Copyright 2014 Rob Landley <rob@landley.net>
4414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley *
5414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/mount.html
6414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley * Note: -hV is bad spec, haven't implemented -FsLU yet
7414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley * no mtab (/proc/mounts does it) so -n is NOP.
8414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
91dd3704c5ffea926f61a96bb7de7d9dbee52fa44Paul BarkerUSE_MOUNT(NEWTOY(mount, "?O:afnrvwt:o*[-rw]", TOYFLAG_BIN|TOYFLAG_STAYROOT))
10a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley//USE_NFSMOUNT(NEWTOY(nfsmount, "?<2>2", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
11414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
12414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landleyconfig MOUNT
13414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley  bool "mount"
149b4ea9cf9934d5639a7d1a9b50b90b8d5569ba7eRob Landley  default y
15414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley  help
16414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley    usage: mount [-afFrsvw] [-t TYPE] [-o OPTIONS...] [[DEVICE] DIR]
17414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
18414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley    Mount new filesystem(s) on directories. With no arguments, display existing
19414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley    mounts.
20414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
21414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley    -a	mount all entries in /etc/fstab (with -t, only entries of that TYPE)
22e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley    -O	only mount -a entries that have this option
23414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley    -f	fake it (don't actually mount)
24414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley    -r	read only (same as -o ro)
25414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley    -w	read/write (default, same as -o rw)
26414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley    -t	specify filesystem type
27414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley    -v	verbose
28414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
29414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley    OPTIONS is a comma separated list of options, which can also be supplied
30414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley    as --longopts.
31414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
32414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley    This mount autodetects loopback mounts (a file on a directory) and
33414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley    bind mounts (file on file, directory on directory), so you don't need
34e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley    to say --bind or --loop. You can also "mount -a /path" to mount everything
35e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley    in /etc/fstab under /path, even if it's noauto.
36360d57f843f5435a75270e54583430dcb8f62546Rob Landley
37a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley#config NFSMOUNT
38a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley#  bool "nfsmount"
39a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley#  default n
40a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley#  help
41a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley#    usage: nfsmount SHARE DIR
42a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley#
43a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley#    Invoke an eldrich horror from the dawn of time.
44414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley*/
45414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
46414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley#define FOR_mount
47414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley#include "toys.h"
48414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
49414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob LandleyGLOBALS(
50414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley  struct arg_list *optlist;
51414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley  char *type;
52e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley  char *bigO;
53414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
54414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley  unsigned long flags;
55414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley  char *opts;
56e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley  int okuser;
57414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley)
58414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
59776aa3cb927f889a0481c2530128940c75984febRob Landley// mount.tests should check for all of this:
60e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley// TODO detect existing identical mount (procfs with different dev name?)
61e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley// TODO user, users, owner, group, nofail
62e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley// TODO -p (passfd)
63e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley// TODO -a -t notype,type2
64e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley// TODO --subtree
65e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley// TODO --rbind, -R
66e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley// TODO make "mount --bind,ro old new" work (implicit -o remount)
67e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley// TODO mount -a
68e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley// TODO mount -o remount
69e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley// TODO fstab: lookup default options for mount
70e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley// TODO implement -v
7172e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley// TODO "mount -a -o remount,ro" should detect overmounts
7272e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley// TODO work out how that differs from "mount -ar"
7372e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley// TODO what if you --bind mount a block device somewhere (file, dir, dev)
7472e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley// TODO "touch servername; mount -t cifs servername path"
75679a21d674cf487992c0ccabc8666c417e59d390Rob Landley// TODO mount -o remount a user mount
764f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley// TODO mount image.img sub (auto-loopback) then umount image.img
77e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley
78e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley// Strip flags out of comma separated list of options, return flags,.
79e996bddf88a946a8ac8338c02e14add57e3d588cRob Landleystatic long flag_opts(char *new, long flags, char **more)
8044e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley{
8144e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley  struct {
8244e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    char *name;
8344e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    long flags;
8444e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley  } opts[] = {
8544e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    // NOPs (we autodetect --loop and --bind)
8644e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    {"loop", 0}, {"bind", 0}, {"defaults", 0}, {"quiet", 0},
87e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley    {"user", 0}, {"nouser", 0}, // checked in fstab, ignored in -o
8844e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    {"ro", MS_RDONLY}, {"rw", ~MS_RDONLY},
8944e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    {"nosuid", MS_NOSUID}, {"suid", ~MS_NOSUID},
9044e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    {"nodev", MS_NODEV}, {"dev", ~MS_NODEV},
9144e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    {"noexec", MS_NOEXEC}, {"exec", ~MS_NOEXEC},
9244e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    {"sync", MS_SYNCHRONOUS}, {"async", ~MS_SYNCHRONOUS},
9344e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    {"noatime", MS_NOATIME}, {"atime", ~MS_NOATIME},
9444e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    {"nodiratime", MS_NODIRATIME}, {"diratime", ~MS_NODIRATIME},
9544e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    {"loud", ~MS_SILENT},
9644e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    {"shared", MS_SHARED}, {"rshared", MS_SHARED|MS_REC},
9744e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    {"slave", MS_SLAVE}, {"rslave", MS_SLAVE|MS_REC},
9844e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    {"private", MS_PRIVATE}, {"rprivate", MS_SLAVE|MS_REC},
9944e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    {"unbindable", MS_UNBINDABLE}, {"runbindable", MS_UNBINDABLE|MS_REC},
100056d0a00de99639fb39d19e984dd66fe82be7820Rob Landley    {"remount", MS_REMOUNT}, {"move", MS_MOVE},
10144e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    // mand dirsync rec iversion strictatime
10244e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley  };
10344e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley
104e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley  if (new) for (;;) {
10544e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    char *comma = strchr(new, ',');
10644e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    int i;
10744e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley
10844e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    if (comma) *comma = 0;
10944e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley
11044e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    // If we recognize an option, apply flags
11144e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    for (i = 0; i < ARRAY_LEN(opts); i++) if (!strcasecmp(opts[i].name, new)) {
11244e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley      long ll = opts[i].flags;
11344e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley
11444e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley      if (ll < 0) flags &= ll;
11544e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley      else flags |= ll;
11644e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley
11744e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley      break;
11844e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    }
11944e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley
12044e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    // If we didn't recognize it, keep string version
12144e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    if (more && i == ARRAY_LEN(opts)) {
12244e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley      i = *more ? strlen(*more) : 0;
12344e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley      *more = xrealloc(*more, i + strlen(new) + 2);
12444e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley      if (i) (*more)[i++] = ',';
12544e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley      strcpy(i+*more, new);
12644e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    }
12744e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley
12844e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    if (!comma) break;
12944e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    *comma = ',';
13044e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    new = comma + 1;
13144e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley  }
13244e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley
13344e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley  return flags;
13444e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley}
13544e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley
13644e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landleystatic void mount_filesystem(char *dev, char *dir, char *type,
13744e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley  unsigned long flags, char *opts)
138414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley{
139414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley  FILE *fp = 0;
140414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley  int rc = EINVAL;
1414f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley  char *buf = 0;
142414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
143414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley  if (toys.optflags & FLAG_f) return;
144414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
145e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley  if (getuid()) {
146e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley    if (TT.okuser) TT.okuser = 0;
147e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley    else {
14870a84a356b1c56743618362867b9300007d11998Rob Landley      error_msg("'%s' not user mountable in fstab", dev);
14970a84a356b1c56743618362867b9300007d11998Rob Landley
150e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley      return;
151e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley    }
152e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley  }
153e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley
154056d0a00de99639fb39d19e984dd66fe82be7820Rob Landley  // Autodetect bind mount or filesystem type
155cc3bf66666cb0d2c8937b1a60c2be8d3f5a49d04Rob Landley
156cc3bf66666cb0d2c8937b1a60c2be8d3f5a49d04Rob Landley  if (type && !strcmp(type, "auto")) type = 0;
157cc3bf66666cb0d2c8937b1a60c2be8d3f5a49d04Rob Landley  if (flags & MS_MOVE) {
158cc3bf66666cb0d2c8937b1a60c2be8d3f5a49d04Rob Landley    if (type) error_exit("--move with -t");
159cc3bf66666cb0d2c8937b1a60c2be8d3f5a49d04Rob Landley  } else if (!type) {
16044e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    struct stat stdev, stdir;
16144e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley
162e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley    // file on file or dir on dir is a --bind mount.
163056d0a00de99639fb39d19e984dd66fe82be7820Rob Landley    if (!stat(dev, &stdev) && !stat(dir, &stdir)
164056d0a00de99639fb39d19e984dd66fe82be7820Rob Landley        && ((S_ISREG(stdev.st_mode) && S_ISREG(stdir.st_mode))
165056d0a00de99639fb39d19e984dd66fe82be7820Rob Landley            || (S_ISDIR(stdev.st_mode) && S_ISDIR(stdir.st_mode))))
166056d0a00de99639fb39d19e984dd66fe82be7820Rob Landley    {
167056d0a00de99639fb39d19e984dd66fe82be7820Rob Landley      flags |= MS_BIND;
168056d0a00de99639fb39d19e984dd66fe82be7820Rob Landley    } else fp = xfopen("/proc/filesystems", "r");
169e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley  } else if (!strcmp(type, "ignore")) return;
170e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley  else if (!strcmp(type, "swap"))
171360d57f843f5435a75270e54583430dcb8f62546Rob Landley    toys.exitval |= xrun((char *[]){"swapon", "--", dev, 0});
172414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
173414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley  for (;;) {
174776aa3cb927f889a0481c2530128940c75984febRob Landley    int fd = -1, ro = 0;
175776aa3cb927f889a0481c2530128940c75984febRob Landley
176414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley    // If type wasn't specified, try all of them in order.
1774f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley    if (fp && !buf) {
178414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley      size_t i;
179414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
180414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley      if (getline(&buf, &i, fp)<0) break;
181414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley      type = buf;
182414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley      // skip nodev devices
183414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley      if (!isspace(*type)) {
184414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley        free(buf);
1854f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley        buf = 0;
1864f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley
187414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley        continue;
188414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley      }
189414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley      // trim whitespace
190414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley      while (isspace(*type)) type++;
191414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley      i = strlen(type);
192414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley      if (i) type[i-1] = 0;
193414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley    }
19444e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley    if (toys.optflags & FLAG_v)
19544e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley      printf("try '%s' type '%s' on '%s'\n", dev, type, dir);
19672e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley    for (;;) {
19772e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley      rc = mount(dev, dir, type, flags, opts);
19872e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley      if ((rc != EACCES && rc != EROFS) || (flags & MS_RDONLY)) break;
199776aa3cb927f889a0481c2530128940c75984febRob Landley      if (rc == EROFS && fd == -1) {
200776aa3cb927f889a0481c2530128940c75984febRob Landley        if (-1 != (fd = open(dev, O_RDONLY))) {
201776aa3cb927f889a0481c2530128940c75984febRob Landley          ioctl(fd, BLKROSET, &ro);
202776aa3cb927f889a0481c2530128940c75984febRob Landley          close(fd);
203776aa3cb927f889a0481c2530128940c75984febRob Landley
204776aa3cb927f889a0481c2530128940c75984febRob Landley          continue;
205776aa3cb927f889a0481c2530128940c75984febRob Landley        }
206776aa3cb927f889a0481c2530128940c75984febRob Landley      }
20772e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley      fprintf(stderr, "'%s' is read-only", dev);
20872e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley      flags |= MS_RDONLY;
209056d0a00de99639fb39d19e984dd66fe82be7820Rob Landley    }
21025fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley
21125fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley    // Trying to autodetect loop mounts like bind mounts above (file on dir)
21225fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley    // isn't good enough because "mount -t ext2 fs.img dir" is valid, but if
21325fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley    // you _do_ accept loop mounts with -t how do you tell "-t cifs" isn't
21425fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley    // looking for a block device if it's not in /proc/filesystems yet
21525fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley    // because the module that won't be loaded until you try the mount, and
21625fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley    // if you can't then DEVICE existing as a file would cause a false
21725fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley    // positive loopback mount (so "touch servername" becomes a potential
21825fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley    // denial of service attack...)
21925fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley    //
22025fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley    // Solution: try the mount, let the kernel tell us it wanted a block
22125fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley    // device, then do the loopback setup and retry the mount.
22225fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley
2234f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley    if (rc && errno == ENOTBLK) {
22425fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley      char *losetup[] = {"losetup", "-fs", dev, 0};
225360d57f843f5435a75270e54583430dcb8f62546Rob Landley      int pipe, len;
22625fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley      pid_t pid;
22725fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley
22825fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley      if (flags & MS_RDONLY) losetup[1] = "-fsr";
229360d57f843f5435a75270e54583430dcb8f62546Rob Landley      pid = xpopen(losetup, &pipe, 1);
230360d57f843f5435a75270e54583430dcb8f62546Rob Landley      len = readall(pipe, toybuf, sizeof(toybuf)-1);
231360d57f843f5435a75270e54583430dcb8f62546Rob Landley      rc = xpclose(pid, pipe);
23225fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley      if (!rc && len > 1) {
23325fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley        if (toybuf[len-1] == '\n') --len;
23425fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley        toybuf[len] = 0;
23525fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley        dev = toybuf;
23672e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley
23725fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley        continue;
2384f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley      }
2394f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley      error_msg("losetup failed %d", rc);
2404f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley
2414f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley      break;
2424f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley    }
2434f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley
2444f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley    free(buf);
2454f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley    buf = 0;
2464f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley    if (!rc) break;
2474f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley    if (fp && (errno == EINVAL || errno == EBUSY)) continue;
2484f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley
2494f0c3de1bf0b6755ea5b5c9c6ae1db9651784794Rob Landley    perror_msg("'%s'->'%s'", dev, dir);
25025fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley
25125fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley    break;
252414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley  }
253414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley  if (fp) fclose(fp);
254414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley}
255414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
256414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landleyvoid mount_main(void)
257414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley{
258e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley  char *opts = 0, *dev = 0, *dir = 0, **ss;
25944e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley  long flags = MS_SILENT;
26044e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley  struct arg_list *o;
261a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley  struct mtab_list *mtl, *mtl2 = 0, *mm, *remount;
262414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
263a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley// TODO
26472e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley// remount
26572e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley//   - overmounts
26672e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley// shared subtree
26772e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley// -o parsed after fstab options
26872e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley// test if mountpoint already exists (-o noremount?)
26972e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley
27072e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley  // First pass; just accumulate string, don't parse flags yet. (This is so
27172e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley  // we can modify fstab entries with -a, or mtab with remount.)
27272e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley  for (o = TT.optlist; o; o = o->next) comma_collate(&opts, o->arg);
27372e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley  if (toys.optflags & FLAG_r) comma_collate(&opts, "ro");
27472e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley  if (toys.optflags & FLAG_w) comma_collate(&opts, "rw");
27544e68a1bec9b342c01e2f4e6ccf9f1e7a3ee8081Rob Landley
276e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley  // Treat each --option as -o option
277e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley  for (ss = toys.optargs; *ss; ss++) {
27872e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley    char *sss = *ss;
27972e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley
28072e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley    // If you realy, really want to mount a file named "--", we support it.
28172e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley    if (sss[0]=='-' && sss[1]=='-' && sss[2]) comma_collate(&opts, sss+2);
28272e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley    else if (!dev) dev = sss;
28372e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley    else if (!dir) dir = sss;
284e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley    // same message as lib/args.c ">2" which we can't use because --opts count
285e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley    else error_exit("Max 2 arguments\n");
286e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley  }
287414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
28872e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley  if ((toys.optflags & FLAG_a) && dir) error_exit("-a with >1 arg");
28972e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley
29072e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley  // For remount we need _last_ match (in case of overmounts), so traverse
291a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley  // in reverse order. (Yes I'm using remount as a boolean for a bit here,
292a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley  // the double cast is to get gcc to shut up about it.)
293a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley  remount = (void *)(long)comma_scan(opts, "remount", 1);
294a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley  if (((toys.optflags & FLAG_a) && !access("/proc/mounts", R_OK)) || remount) {
295a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley    mm = dlist_terminate(mtl = mtl2 = xgetmountlist(0));
296a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley    if (remount) remount = mm;
297a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley  }
298e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley
299e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley  // Do we need to do an /etc/fstab trawl?
30072e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley  // This covers -a, -o remount, one argument, all user mounts
30125fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley  if ((toys.optflags & FLAG_a) || (dev && (!dir || getuid() || remount))) {
302679a21d674cf487992c0ccabc8666c417e59d390Rob Landley    if (!remount) dlist_terminate(mtl = xgetmountlist("/etc/fstab"));
30325fe0e0bea85f1d851ce03a90c0f1bf41ab431f2Rob Landley
30472e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley    for (mm = remount ? remount : mtl; mm; mm = (remount ? mm->prev : mm->next))
305e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley    {
30672e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley      char *aopts = 0;
307a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley      struct mtab_list *mmm = 0;
308a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley      int aflags, noauto, len;
309e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley
31072e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley      // Check for noauto and get it out of the option list. (Unknown options
31172e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley      // that make it to the kernel give filesystem drivers indigestion.)
31272e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley      noauto = comma_scan(mm->opts, "noauto", 1);
313a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley
314e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley      if (toys.optflags & FLAG_a) {
31572e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley        // "mount -a /path" to mount all entries under /path
31672e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley        if (dev) {
31772e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley           len = strlen(dev);
31872e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley           if (strncmp(dev, mm->dir, len)
31972e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley               || (mm->dir[len] && mm->dir[len] != '/')) continue;
32072e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley        } else if (noauto) continue; // never present in the remount case
32172e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley        if (!mountlist_istype(mm,TT.type) || !comma_scanall(mm->opts,TT.bigO))
322e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley          continue;
323e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley      } else {
32472e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley        if (dir && strcmp(dir, mm->dir)) continue;
32572e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley        if (dev && strcmp(dev, mm->device) && (dir || strcmp(dev, mm->dir)))
326e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley          continue;
327e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley      }
328e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley
329a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley      // Don't overmount the same dev on the same directory
330a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley      // (Unless root explicitly says to in non -a mode.)
331a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley      if (mtl2 && !remount)
332a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley        for (mmm = mtl2; mmm; mmm = mmm->next)
333a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley          if (!strcmp(mm->dir, mmm->dir) && !strcmp(mm->device, mmm->device))
334a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley            break;
335a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley
336e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley      // user only counts from fstab, not opts.
337a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley      if (!mmm) {
338a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley        TT.okuser = comma_scan(mm->opts, "user", 1);
339a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley        aflags = flag_opts(mm->opts, flags, &aopts);
340a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley        aflags = flag_opts(opts, aflags, &aopts);
341e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley
342a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley        mount_filesystem(mm->device, mm->dir, mm->type, aflags, aopts);
343a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley      } // TODO else if (getuid()) error_msg("already there") ?
344e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley      free(aopts);
34572e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley
34672e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley      if (!(toys.optflags & FLAG_a)) break;
347e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley    }
348a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley    if (CFG_TOYBOX_FREE) {
349a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley      llist_traverse(mtl, free);
350a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley      llist_traverse(mtl2, free);
351a03a0449e810c26f001cdfc56e5527f51577a639Rob Landley    }
352679a21d674cf487992c0ccabc8666c417e59d390Rob Landley    if (!mm && !(toys.optflags & FLAG_a))
353679a21d674cf487992c0ccabc8666c417e59d390Rob Landley      error_exit("'%s' not in %s", dir ? dir : dev,
354679a21d674cf487992c0ccabc8666c417e59d390Rob Landley                 remount ? "/proc/mounts" : "fstab");
355e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley
35672e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley  // show mounts from /proc/mounts
35772e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley  } else if (!dev) {
358e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley    for (mtl = xgetmountlist(0); mtl && (mm = dlist_pop(&mtl)); free(mm)) {
359414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley      char *s = 0;
360414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
361e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley      if (TT.type && strcmp(TT.type, mm->type)) continue;
362e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley      if (*mm->device == '/') s = xabspath(mm->device, 0);
363414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley      xprintf("%s on %s type %s (%s)\n",
364e996bddf88a946a8ac8338c02e14add57e3d588cRob Landley              s ? s : mm->device, mm->dir, mm->type, mm->opts);
365414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley      free(s);
366414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley    }
367414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley
368414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley  // two arguments
36972e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley  } else {
37072e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley    char *more = 0;
37172e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley
37272e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley    mount_filesystem(dev, dir, TT.type, flag_opts(opts, flags, &more), more);
37372e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley    if (CFG_TOYBOX_FREE) free(more);
37472e84a2f3b758026dcb37abdb8c8383c9a07ec94Rob Landley  }
375414c0cf9f5e91f7d755ba935595bbd7f3fd7c29eRob Landley}
376