master xplshn/aruu / cmd / linux / mount.c
  1/* See LICENSE file for copyright and license details. */
  2
  3#include <sys/mount.h>
  4#include <sys/stat.h>
  5#include <sys/types.h>
  6#include <sys/wait.h>
  7
  8#include <errno.h>
  9#include <limits.h>
 10#include <mntent.h>
 11#include <stdio.h>
 12#include <stdlib.h>
 13#include <string.h>
 14#include <unistd.h>
 15
 16#include "paths.h"
 17#include "text.h"
 18#include "util.h"
 19#include "wexec.h"
 20
 21#define FSOPTS_MAXLEN 512
 22
 23struct {
 24  const char   *opt;
 25  const char   *notopt;
 26  unsigned long v;
 27} optnames[] = {
 28    {"defaults", NULL, 0},
 29    {"remount", NULL, MS_REMOUNT},
 30    {"ro", "rw", MS_RDONLY},
 31    {"sync", "async", MS_SYNCHRONOUS},
 32    {"dirsync", NULL, MS_DIRSYNC},
 33    {"nodev", "dev", MS_NODEV},
 34    {"noatime", "atime", MS_NOATIME},
 35    {"noauto", "auto", 0},
 36    {"nodiratime", "diratime", MS_NODIRATIME},
 37    {"noexec", "exec", MS_NOEXEC},
 38    {"nosuid", "suid", MS_NOSUID},
 39    {"mand", "nomand", MS_MANDLOCK},
 40    {"relatime", "norelatime", MS_RELATIME},
 41    {"bind", NULL, MS_BIND},
 42    {NULL, NULL, 0}
 43};
 44
 45static unsigned long argflags              = 0;
 46static char          fsopts[FSOPTS_MAXLEN] = "";
 47
 48static char *
 49findtype(const char *types, const char *t)
 50{
 51  const char *p;
 52  size_t      len;
 53
 54  for (len = strlen(t); (p = strstr(types, t)); types = p + len) {
 55    if (!strncmp(p, t, len) && (p[len] == '\0' || p[len] == ','))
 56      return (char *)p;
 57  }
 58  return NULL;
 59}
 60
 61static void
 62parseopts(const char *popts, unsigned long *flags, char *data, size_t datasiz)
 63{
 64  unsigned int i, validopt;
 65  size_t       optlen, dlen = 0;
 66  const char  *name, *e;
 67
 68  name    = popts;
 69  data[0] = '\0';
 70  do {
 71    if ((e = strstr(name, ",")))
 72      optlen = e - name;
 73    else
 74      optlen = strlen(name);
 75
 76    validopt = 0;
 77    for (i = 0; optnames[i].opt; i++) {
 78      if (optnames[i].opt && !strncmp(name, optnames[i].opt, optlen)) {
 79        *flags |= optnames[i].v;
 80        validopt = 1;
 81        break;
 82      }
 83      if (optnames[i].notopt && !strncmp(name, optnames[i].notopt, optlen)) {
 84        *flags &= ~optnames[i].v;
 85        validopt = 1;
 86        break;
 87      }
 88    }
 89
 90    if (!validopt && optlen > 0) {
 91      /* unknown option, pass as data option to mount() */
 92      if (dlen + optlen + 2 >= datasiz)
 93        return; /* prevent overflow */
 94      if (dlen)
 95        data[dlen++] = ',';
 96      memcpy(&data[dlen], name, optlen);
 97      dlen += optlen;
 98      data[dlen] = '\0';
 99    }
100    name = e + 1;
101  } while (e);
102}
103
104static int
105mounthelper(const char *fsname, const char *dir, const char *fstype)
106{
107  pid_t       pid;
108  char        eprog[PATH_MAX];
109  char const *eargv[10];
110  int         status, i;
111
112  pid = fork();
113  switch (pid) {
114    case -1:
115      break;
116    case 0:
117      snprintf(eprog, sizeof(eprog), "mount.%s", fstype);
118
119      i          = 0;
120      eargv[i++] = eprog;
121      if (argflags & MS_BIND)
122        eargv[i++] = "-B";
123      if (argflags & MS_MOVE)
124        eargv[i++] = "-M";
125      if (argflags & MS_REC)
126        eargv[i++] = "-R";
127
128      if (fsopts[0]) {
129        eargv[i++] = "-o";
130        eargv[i++] = fsopts;
131      }
132      eargv[i++] = fsname;
133      eargv[i++] = dir;
134      eargv[i]   = NULL;
135
136      wexecvp_self(eprog, (char *const *)eargv);
137      if (errno == ENOENT)
138        _exit(1);
139      weprintf("wexecvp:");
140      _exit(1);
141      break;
142    default:
143      if (waitpid(pid, &status, 0) < 0) {
144        weprintf("waitpid:");
145        return -1;
146      }
147      if (WIFEXITED(status))
148        return WEXITSTATUS(status);
149      else if (WIFSIGNALED(status))
150        return 1;
151      break;
152  }
153  return 0;
154}
155
156static int
157mounted(const char *dir)
158{
159  FILE          *fp;
160  struct mntent *me, mebuf;
161  struct stat    st1, st2;
162  char           linebuf[256];
163
164  if (stat(dir, &st1) < 0) {
165    weprintf("stat %s:", dir);
166    return 0;
167  }
168  if (!(fp = setmntent(ARUU_LINUX_PATH_PROC_MOUNTS, "r")))
169    eprintf("setmntent %s:", ARUU_LINUX_PATH_PROC_MOUNTS);
170
171  while ((me = getmntent_r(fp, &mebuf, linebuf, sizeof(linebuf)))) {
172    if (stat(me->mnt_dir, &st2) < 0) {
173      weprintf("stat %s:", me->mnt_dir);
174      continue;
175    }
176    if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
177      return 1;
178  }
179  endmntent(fp);
180
181  return 0;
182}
183
184static void
185usage(void)
186{
187  eprintf(
188      "usage: %s [-BMRan] [-t fstype] [-o options] [source] "
189      "[target]\n",
190      argv0
191  );
192}
193
194// ?man mount: mount a filesystem
195// ?man arguments: source [target]
196// ?man mount a filesystem to the directory tree
197int
198main(int argc, char *argv[])
199{
200  char          *types = NULL, data[FSOPTS_MAXLEN] = "", *resolvpath = NULL;
201  char          *files[] = {ARUU_LINUX_PATH_PROC_MOUNTS, "/etc/fstab", NULL};
202  const char    *source, *target;
203  struct mntent *me    = NULL;
204  int            aflag = 0, status = 0, i, r;
205  unsigned long  flags = 0;
206  FILE          *fp;
207
208  ARGBEGIN
209  {
210    // ?man -B: specify option flag
211    case 'B':
212      argflags |= MS_BIND;
213      break;
214    // ?man -M: specify option flag
215    case 'M':
216      argflags |= MS_MOVE;
217      break;
218    // ?man -R: operate recursively on directories
219    case 'R':
220      argflags |= MS_REC;
221      break;
222    // ?man -a: print or show all entries
223    case 'a':
224      aflag = 1;
225      break;
226    // ?man -o:str: specify output file
227    case 'o':
228      estrlcat(fsopts, EARGF(usage()), sizeof(fsopts));
229      parseopts(fsopts, &flags, data, sizeof(data));
230      break;
231    // ?man -t:str: sort or specify timestamp
232    case 't':
233      types = EARGF(usage());
234      break;
235    // ?man -n: print line numbers or counts
236    case 'n':
237      break;
238    default:
239      usage();
240  }
241  ARGEND;
242
243  if (argc < 1 && aflag == 0) {
244    if (!(fp = fopen(files[0], "r")))
245      eprintf("fopen %s:", files[0]);
246    fconcat(fp, files[0], stdout, "<stdout>");
247    fclose(fp);
248    return 0;
249  }
250
251  if (aflag == 1)
252    goto mountall;
253
254  source = argv[0];
255  target = argv[1];
256
257  if (!target) {
258    target = argv[0];
259    source = NULL;
260    if (strcmp(target, "/") != 0) {
261      if (!(resolvpath = realpath(target, NULL)))
262        eprintf("realpath %s:", target);
263      target = resolvpath;
264    }
265  }
266
267  for (i = 0; files[i]; i++) {
268    if (!(fp = setmntent(files[i], "r"))) {
269      if (strcmp(files[i], ARUU_LINUX_PATH_PROC_MOUNTS) != 0)
270        weprintf("setmntent %s:", files[i]);
271      continue;
272    }
273    while ((me = getmntent(fp))) {
274      if (strcmp(me->mnt_dir, target) == 0 || strcmp(me->mnt_fsname, target) == 0
275          || (source && strcmp(me->mnt_dir, source) == 0)
276          || (source && strcmp(me->mnt_fsname, source) == 0)) {
277        if (!source) {
278          target = me->mnt_dir;
279          source = me->mnt_fsname;
280        }
281        if (!fsopts[0]) {
282          estrlcat(fsopts, me->mnt_opts, sizeof(fsopts));
283          parseopts(fsopts, &flags, data, sizeof(data));
284        }
285        if (!types)
286          types = me->mnt_type;
287        goto mountsingle;
288      }
289    }
290    endmntent(fp);
291    fp = NULL;
292  }
293  if (!source)
294    eprintf("can't find %s in /etc/fstab\n", target);
295
296mountsingle:
297  r = mounthelper(source, target, types);
298  if (r == -1)
299    status = 1;
300  if (r > 0 && mount(source, target, types, argflags | flags, data) < 0) {
301    weprintf("mount: %s:", source);
302    status = 1;
303  }
304  if (fp)
305    endmntent(fp);
306  free(resolvpath);
307  return status;
308
309mountall:
310  if (!(fp = setmntent("/etc/fstab", "r")))
311    eprintf("setmntent %s:", "/etc/fstab");
312  while ((me = getmntent(fp))) {
313    /* has "noauto" option or already mounted: skip */
314    if (hasmntopt(me, MNTOPT_NOAUTO) || mounted(me->mnt_dir))
315      continue;
316    flags     = 0;
317    fsopts[0] = '\0';
318    if (strlcat(fsopts, me->mnt_opts, sizeof(fsopts)) >= sizeof(fsopts)) {
319      weprintf("%s: option string too long\n", me->mnt_dir);
320      status = 1;
321      continue;
322    }
323    parseopts(fsopts, &flags, data, sizeof(data));
324    /* if -t types specified:
325     * if non-match, skip
326     * if match and prefixed with "no", skip */
327    if (types
328        && ((types[0] == 'n' && types[1] == 'o' && findtype(types + 2, me->mnt_type))
329            || (!findtype(types, me->mnt_type))))
330      continue;
331
332    r = mounthelper(me->mnt_fsname, me->mnt_dir, me->mnt_type);
333    if (r > 0 && mount(me->mnt_fsname, me->mnt_dir, me->mnt_type, argflags | flags, data) < 0) {
334      weprintf("mount: %s:", me->mnt_fsname);
335      status = 1;
336    }
337  }
338  endmntent(fp);
339
340  return status;
341}