changeset 1334:9fd2bcedbeb5

mount: start on option parsing, implement loopback and bind mount autodetection.
author Rob Landley <rob@landley.net>
date Tue, 03 Jun 2014 06:27:24 -0500
parents fc1bb49e58a9
children d90f692a50d4
files lib/lib.h lib/xwrap.c toys/pending/mount.c
diffstat 3 files changed, 164 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/lib/lib.h	Mon Jun 02 21:16:20 2014 -0500
+++ b/lib/lib.h	Tue Jun 03 06:27:24 2014 -0500
@@ -94,6 +94,8 @@
 pid_t xfork(void);
 void xexec_optargs(int skip);
 void xexec(char **argv);
+pid_t xpopen(char **argv, int *pipes);
+int xpclose(pid_t pid, int *pipes);
 void xaccess(char *path, int flags);
 void xunlink(char *path);
 int xcreate(char *path, int flags, int mode);
--- a/lib/xwrap.c	Mon Jun 02 21:16:20 2014 -0500
+++ b/lib/xwrap.c	Tue Jun 03 06:27:24 2014 -0500
@@ -143,6 +143,67 @@
   perror_exit("exec %s", argv[0]);
 }
 
+// Spawn child process, capturing stdin/stdout.
+// argv[]: command to exec. If null, child returns to original program.
+// pipes[]: stdin, stdout of new process. If null, block and wait for child.
+// return: pid of child process
+pid_t xpopen(char **argv, int *pipes)
+{
+  int cestnepasun[4], pid;
+
+  // Make the pipes?
+  if (pipes) {
+    if (pipe(cestnepasun) || pipe(cestnepasun+2)) perror_exit("pipe");
+    pipes[0] = cestnepasun[1];
+    pipes[1] = cestnepasun[2];
+  }
+
+  // Child process
+  if (!(pid = xfork())) {
+    // Dance of the stdin/stdout redirection.
+    if (pipes) {
+      close(cestnepasun[1]);
+      close(cestnepasun[2]);
+      // if we had no stdin/out, pipe handles could overlap, so test for that
+      if (cestnepasun[0]) {
+        dup2(cestnepasun[0], 0);
+        close(cestnepasun[0]);
+      }
+      dup2(cestnepasun[3], 1);
+      dup2(cestnepasun[3], 2);
+      if (cestnepasun[3] > 2) close(cestnepasun[3]);
+    }
+    if (argv) {
+      if (CFG_TOYBOX) toy_exec(argv);
+      execvp(argv[0], argv);
+      _exit(127);
+    }
+    return 0;
+
+  // Parent process
+  } else {
+    if (pipes) {
+      close(cestnepasun[0]);
+      close(cestnepasun[3]);
+    }
+
+    return pid;
+  }
+}
+
+int xpclose(pid_t pid, int *pipes)
+{
+  int rc = 127;
+
+  if (pipes) {
+    close(pipes[0]);
+    close(pipes[1]);
+  }
+  waitpid(pid, &rc, 0);
+
+  return WIFEXITED(rc) ? WEXITSTATUS(rc) : WTERMSIG(rc) + 127;
+}
+
 void xaccess(char *path, int flags)
 {
   if (access(path, flags)) perror_exit("Can't access '%s'", path);
--- a/toys/pending/mount.c	Mon Jun 02 21:16:20 2014 -0500
+++ b/toys/pending/mount.c	Tue Jun 03 06:27:24 2014 -0500
@@ -6,7 +6,7 @@
  * Note: -hV is bad spec, haven't implemented -FsLU yet
  * no mtab (/proc/mounts does it) so -n is NOP.
 
-USE_MOUNT(NEWTOY(mount, ">2afnrvwt:o*[-rw]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
+USE_MOUNT(NEWTOY(mount, "?>2afnrvwt:o*[-rw]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
 
 config MOUNT
   bool "mount"
@@ -27,12 +27,6 @@
     OPTIONS is a comma separated list of options, which can also be supplied
     as --longopts.
 
-
-
-config MOUNT_AUTODETECT
-  help
-    usage: mount
-
     This mount autodetects loopback mounts (a file on a directory) and
     bind mounts (file on file, directory on directory), so you don't need
     to say --bind or --loop.
@@ -49,14 +43,99 @@
   char *opts;
 )
 
-static void do_mount(char *dev, char *dir, char *type, unsigned long flags, char *opts)
+// Strip flags out of comma separated list of options.
+// Return flags, 
+static long parse_opts(char *new, long flags, char **more)
+{
+  struct {
+    char *name;
+    long flags;
+  } opts[] = {
+    // NOPs (we autodetect --loop and --bind)
+    {"loop", 0}, {"bind", 0}, {"defaults", 0}, {"quiet", 0},
+//    {"noauto", 0}, {"swap", 0},
+    {"ro", MS_RDONLY}, {"rw", ~MS_RDONLY},
+    {"nosuid", MS_NOSUID}, {"suid", ~MS_NOSUID},
+    {"nodev", MS_NODEV}, {"dev", ~MS_NODEV},
+    {"noexec", MS_NOEXEC}, {"exec", ~MS_NOEXEC},
+    {"sync", MS_SYNCHRONOUS}, {"async", ~MS_SYNCHRONOUS},
+    {"noatime", MS_NOATIME}, {"atime", ~MS_NOATIME},
+    {"nodiratime", MS_NODIRATIME}, {"diratime", ~MS_NODIRATIME},
+    {"loud", ~MS_SILENT},
+    {"shared", MS_SHARED}, {"rshared", MS_SHARED|MS_REC},
+    {"slave", MS_SLAVE}, {"rslave", MS_SLAVE|MS_REC},
+    {"private", MS_PRIVATE}, {"rprivate", MS_SLAVE|MS_REC},
+    {"unbindable", MS_UNBINDABLE}, {"runbindable", MS_UNBINDABLE|MS_REC},
+    {"remount", MS_REMOUNT}, {"bind", MS_BIND}, {"move", MS_MOVE},
+    // mand dirsync rec iversion strictatime
+  };
+
+  for (;;) {
+    char *comma = strchr(new, ',');
+    int i;
+
+    if (comma) *comma = 0;
+
+    // If we recognize an option, apply flags
+    for (i = 0; i < ARRAY_LEN(opts); i++) if (!strcasecmp(opts[i].name, new)) {
+      long ll = opts[i].flags;
+
+      if (ll < 0) flags &= ll;
+      else flags |= ll;
+
+      break;
+    }
+
+    // If we didn't recognize it, keep string version
+    if (more && i == ARRAY_LEN(opts)) {
+      i = *more ? strlen(*more) : 0;
+      *more = xrealloc(*more, i + strlen(new) + 2);
+      if (i) (*more)[i++] = ',';
+      strcpy(i+*more, new);
+    }
+
+    if (!comma) break;
+    *comma = ',';
+    new = comma + 1;
+  }
+
+  return flags;
+}
+
+static void mount_filesystem(char *dev, char *dir, char *type,
+  unsigned long flags, char *opts)
 {
   FILE *fp = 0;
   int rc = EINVAL;
 
   if (toys.optflags & FLAG_f) return;
 
-  if (!TT.type) fp = xfopen("/proc/filesystems", "r");
+  if (!type) {
+    struct stat stdev, stdir;
+
+    if (!stat(dev, &stdev) && !stat(dir, &stdir)) {
+      if (S_ISREG(stdev.st_mode)) {
+        // Loopback mount?
+        if (S_ISDIR(stdir.st_mode)) {
+          char *losetup[] = {"losetup", "-fs", dev, 0};
+          int pipes[2], len;
+          pid_t pid;
+
+          if (flags & MS_RDONLY) losetup[1] = "-fsr";
+          pid = xpopen(losetup, pipes);
+          len = readall(pipes[1], toybuf, sizeof(toybuf)-1);
+          if (!xpclose(pid, pipes) && len > 1) {
+            if (toybuf[len-1] == '\n') --len;
+            toybuf[len] = 0;
+            dev = toybuf;
+          } else error_msg("losetup failed %d", len);
+        } else if (S_ISREG(stdir.st_mode)) flags |= MS_BIND;
+      } else if (S_ISDIR(stdev.st_mode) && S_ISDIR(stdir.st_mode))
+        flags |= MS_BIND;
+    }
+
+    if (!(flags & MS_BIND)) fp = xfopen("/proc/filesystems", "r");
+  }
 
   for (;;) {
     char *buf = 0;
@@ -77,6 +156,8 @@
       i = strlen(type);
       if (i) type[i-1] = 0;
     }
+    if (toys.optflags & FLAG_v)
+      printf("try '%s' type '%s' on '%s'\n", dev, type, dir);
     rc = mount(dev, dir, type, flags, opts);
     if (!fp || (rc && errno != EINVAL)) break;
     free(buf);
@@ -88,11 +169,20 @@
 
 void mount_main(void)
 {
+  long flags = MS_SILENT;
+  struct arg_list *o;
+  char *opts = 0;
+
   if (toys.optflags & FLAG_a) {
     fprintf(stderr, "not yet\n");
     return;
   }
 
+  if (toys.optflags & FLAG_r) flags |= MS_RDONLY;
+  if (toys.optflags & FLAG_w) flags &= ~MS_RDONLY;
+  for (o = TT.optlist; o; o = o->next)
+    flags = parse_opts(o->arg, flags, &opts);
+
   // show mounts
   if (!toys.optc) {
     struct mtab_list *mtl = xgetmountlist(0), *m;
@@ -112,5 +202,6 @@
     fprintf(stderr, "not yet\n");
     return;
   // two arguments
-  } else do_mount(toys.optargs[0], toys.optargs[1], TT.type, 0, "");
+  } else mount_filesystem(toys.optargs[0], toys.optargs[1], TT.type,
+                          flags, opts ? opts : "");
 }