source: branches/3.2/mindi-busybox/util-linux/mount.c @ 3232

Last change on this file since 3232 was 3232, checked in by bruno, 5 years ago
  • Update mindi-busybox to 1.21.1
File size: 58.8 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini mount implementation for busybox
4 *
5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net>
8 *
9 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10 */
11// Design notes: There is no spec for mount.  Remind me to write one.
12//
13// mount_main() calls singlemount() which calls mount_it_now().
14//
15// mount_main() can loop through /etc/fstab for mount -a
16// singlemount() can loop through /etc/filesystems for fstype detection.
17// mount_it_now() does the actual mount.
18//
19
20//usage:#define mount_trivial_usage
21//usage:       "[OPTIONS] [-o OPTS] DEVICE NODE"
22//usage:#define mount_full_usage "\n\n"
23//usage:       "Mount a filesystem. Filesystem autodetection requires /proc.\n"
24//usage:     "\n    -a      Mount all filesystems in fstab"
25//usage:    IF_FEATURE_MOUNT_FAKE(
26//usage:    IF_FEATURE_MTAB_SUPPORT(
27//usage:     "\n    -f      Update /etc/mtab, but don't mount"
28//usage:    )
29//usage:    IF_NOT_FEATURE_MTAB_SUPPORT(
30//usage:     "\n    -f      Dry run"
31//usage:    )
32//usage:    )
33//usage:    IF_FEATURE_MOUNT_HELPERS(
34//usage:     "\n    -i      Don't run mount helper"
35//usage:    )
36//usage:    IF_FEATURE_MTAB_SUPPORT(
37//usage:     "\n    -n      Don't update /etc/mtab"
38//usage:    )
39//usage:    IF_FEATURE_MOUNT_VERBOSE(
40//usage:     "\n    -v      Verbose"
41//usage:    )
42////usage:   "\n    -s      Sloppy (ignored)"
43//usage:     "\n    -r      Read-only mount"
44//usage:     "\n    -w      Read-write mount (default)"
45//usage:     "\n    -t FSTYPE[,...] Filesystem type(s)"
46//usage:     "\n    -O OPT      Mount only filesystems with option OPT (-a only)"
47//usage:     "\n-o OPT:"
48//usage:    IF_FEATURE_MOUNT_LOOP(
49//usage:     "\n    loop        Ignored (loop devices are autodetected)"
50//usage:    )
51//usage:    IF_FEATURE_MOUNT_FLAGS(
52//usage:     "\n    [a]sync     Writes are [a]synchronous"
53//usage:     "\n    [no]atime   Disable/enable updates to inode access times"
54//usage:     "\n    [no]diratime    Disable/enable atime updates to directories"
55//usage:     "\n    [no]relatime    Disable/enable atime updates relative to modification time"
56//usage:     "\n    [no]dev     (Dis)allow use of special device files"
57//usage:     "\n    [no]exec    (Dis)allow use of executable files"
58//usage:     "\n    [no]suid    (Dis)allow set-user-id-root programs"
59//usage:     "\n    [r]shared   Convert [recursively] to a shared subtree"
60//usage:     "\n    [r]slave    Convert [recursively] to a slave subtree"
61//usage:     "\n    [r]private  Convert [recursively] to a private subtree"
62//usage:     "\n    [un]bindable    Make mount point [un]able to be bind mounted"
63//usage:     "\n    [r]bind     Bind a file or directory [recursively] to another location"
64//usage:     "\n    move        Relocate an existing mount point"
65//usage:    )
66//usage:     "\n    remount     Remount a mounted filesystem, changing flags"
67//usage:     "\n    ro/rw       Same as -r/-w"
68//usage:     "\n"
69//usage:     "\nThere are filesystem-specific -o flags."
70//usage:
71//usage:#define mount_example_usage
72//usage:       "$ mount\n"
73//usage:       "/dev/hda3 on / type minix (rw)\n"
74//usage:       "proc on /proc type proc (rw)\n"
75//usage:       "devpts on /dev/pts type devpts (rw)\n"
76//usage:       "$ mount /dev/fd0 /mnt -t msdos -o ro\n"
77//usage:       "$ mount /tmp/diskimage /opt -t ext2 -o loop\n"
78//usage:       "$ mount cd_image.iso mydir\n"
79//usage:#define mount_notes_usage
80//usage:       "Returns 0 for success, number of failed mounts for -a, or errno for one mount."
81
82#include <mntent.h>
83#include <syslog.h>
84#include <sys/mount.h>
85// Grab more as needed from util-linux's mount/mount_constants.h
86#ifndef MS_DIRSYNC
87# define MS_DIRSYNC     (1 << 7) // Directory modifications are synchronous
88#endif
89#ifndef MS_UNION
90# define MS_UNION       (1 << 8)
91#endif
92#ifndef MS_BIND
93# define MS_BIND        (1 << 12)
94#endif
95#ifndef MS_MOVE
96# define MS_MOVE        (1 << 13)
97#endif
98#ifndef MS_RECURSIVE
99# define MS_RECURSIVE   (1 << 14)
100#endif
101#ifndef MS_SILENT
102# define MS_SILENT      (1 << 15)
103#endif
104// The shared subtree stuff, which went in around 2.6.15
105#ifndef MS_UNBINDABLE
106# define MS_UNBINDABLE  (1 << 17)
107#endif
108#ifndef MS_PRIVATE
109# define MS_PRIVATE     (1 << 18)
110#endif
111#ifndef MS_SLAVE
112# define MS_SLAVE       (1 << 19)
113#endif
114#ifndef MS_SHARED
115# define MS_SHARED      (1 << 20)
116#endif
117#ifndef MS_RELATIME
118# define MS_RELATIME    (1 << 21)
119#endif
120#ifndef MS_STRICTATIME
121# define MS_STRICTATIME (1 << 24)
122#endif
123
124/* Any ~MS_FOO value has this bit set: */
125#define BB_MS_INVERTED_VALUE (1u << 31)
126
127#include "libbb.h"
128#if ENABLE_FEATURE_MOUNT_LABEL
129# include "volume_id.h"
130#else
131# define resolve_mount_spec(fsname) ((void)0)
132#endif
133
134// Needed for nfs support only
135#include <sys/utsname.h>
136#undef TRUE
137#undef FALSE
138#if ENABLE_FEATURE_MOUNT_NFS
139/* This is just a warning of a common mistake.  Possibly this should be a
140 * uclibc faq entry rather than in busybox... */
141# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
142#  error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
143# endif
144# include <rpc/rpc.h>
145# include <rpc/pmap_prot.h>
146# include <rpc/pmap_clnt.h>
147#endif
148
149
150#if defined(__dietlibc__)
151// 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
152// dietlibc-0.30 does not have implementation of getmntent_r()
153static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
154        char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
155{
156    struct mntent* ment = getmntent(stream);
157    return memcpy(result, ment, sizeof(*ment));
158}
159#endif
160
161
162// Not real flags, but we want to be able to check for this.
163enum {
164    MOUNT_USERS  = (1 << 28) * ENABLE_DESKTOP,
165    MOUNT_NOAUTO = (1 << 29),
166    MOUNT_SWAP   = (1 << 30),
167};
168
169
170#define OPTION_STR "o:t:rwanfvsiO:"
171enum {
172    OPT_o = (1 << 0),
173    OPT_t = (1 << 1),
174    OPT_r = (1 << 2),
175    OPT_w = (1 << 3),
176    OPT_a = (1 << 4),
177    OPT_n = (1 << 5),
178    OPT_f = (1 << 6),
179    OPT_v = (1 << 7),
180    OPT_s = (1 << 8),
181    OPT_i = (1 << 9),
182    OPT_O = (1 << 10),
183};
184
185#if ENABLE_FEATURE_MTAB_SUPPORT
186#define USE_MTAB (!(option_mask32 & OPT_n))
187#else
188#define USE_MTAB 0
189#endif
190
191#if ENABLE_FEATURE_MOUNT_FAKE
192#define FAKE_IT (option_mask32 & OPT_f)
193#else
194#define FAKE_IT 0
195#endif
196
197#if ENABLE_FEATURE_MOUNT_HELPERS
198#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
199#else
200#define HELPERS_ALLOWED 0
201#endif
202
203
204// TODO: more "user" flag compatibility.
205// "user" option (from mount manpage):
206// Only the user that mounted a filesystem can unmount it again.
207// If any user should be able to unmount, then use users instead of user
208// in the fstab line.  The owner option is similar to the user option,
209// with the restriction that the user must be the owner of the special file.
210// This may be useful e.g. for /dev/fd if a login script makes
211// the console user owner of this device.
212
213// Standard mount options (from -o options or --options),
214// with corresponding flags
215static const int32_t mount_options[] = {
216    // MS_FLAGS set a bit.  ~MS_FLAGS disable that bit.  0 flags are NOPs.
217
218    IF_FEATURE_MOUNT_LOOP(
219        /* "loop" */ 0,
220    )
221
222    IF_FEATURE_MOUNT_FSTAB(
223        /* "defaults" */ 0,
224        /* "quiet" 0 - do not filter out, vfat wants to see it */
225        /* "noauto" */ MOUNT_NOAUTO,
226        /* "sw"     */ MOUNT_SWAP,
227        /* "swap"   */ MOUNT_SWAP,
228        IF_DESKTOP(/* "user"  */ MOUNT_USERS,)
229        IF_DESKTOP(/* "users" */ MOUNT_USERS,)
230        /* "_netdev" */ 0,
231        IF_DESKTOP(/* "comment=" */ 0,) /* systemd uses this in fstab */
232    )
233
234    IF_FEATURE_MOUNT_FLAGS(
235        // vfs flags
236        /* "nosuid"      */ MS_NOSUID,
237        /* "suid"        */ ~MS_NOSUID,
238        /* "dev"         */ ~MS_NODEV,
239        /* "nodev"       */ MS_NODEV,
240        /* "exec"        */ ~MS_NOEXEC,
241        /* "noexec"      */ MS_NOEXEC,
242        /* "sync"        */ MS_SYNCHRONOUS,
243        /* "dirsync"     */ MS_DIRSYNC,
244        /* "async"       */ ~MS_SYNCHRONOUS,
245        /* "atime"       */ ~MS_NOATIME,
246        /* "noatime"     */ MS_NOATIME,
247        /* "diratime"    */ ~MS_NODIRATIME,
248        /* "nodiratime"  */ MS_NODIRATIME,
249        /* "mand"        */ MS_MANDLOCK,
250        /* "nomand"      */ ~MS_MANDLOCK,
251        /* "relatime"    */ MS_RELATIME,
252        /* "norelatime"  */ ~MS_RELATIME,
253        /* "strictatime" */ MS_STRICTATIME,
254        /* "loud"        */ ~MS_SILENT,
255        /* "rbind"       */ MS_BIND|MS_RECURSIVE,
256
257        // action flags
258        /* "union"       */ MS_UNION,
259        /* "bind"        */ MS_BIND,
260        /* "move"        */ MS_MOVE,
261        /* "shared"      */ MS_SHARED,
262        /* "slave"       */ MS_SLAVE,
263        /* "private"     */ MS_PRIVATE,
264        /* "unbindable"  */ MS_UNBINDABLE,
265        /* "rshared"     */ MS_SHARED|MS_RECURSIVE,
266        /* "rslave"      */ MS_SLAVE|MS_RECURSIVE,
267        /* "rprivate"    */ MS_PRIVATE|MS_RECURSIVE,
268        /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
269    )
270
271    // Always understood.
272    /* "ro"      */ MS_RDONLY,  // vfs flag
273    /* "rw"      */ ~MS_RDONLY, // vfs flag
274    /* "remount" */ MS_REMOUNT  // action flag
275};
276
277static const char mount_option_str[] =
278    IF_FEATURE_MOUNT_LOOP(
279        "loop\0"
280    )
281    IF_FEATURE_MOUNT_FSTAB(
282        "defaults\0"
283        // "quiet\0" - do not filter out, vfat wants to see it
284        "noauto\0"
285        "sw\0"
286        "swap\0"
287        IF_DESKTOP("user\0")
288        IF_DESKTOP("users\0")
289        "_netdev\0"
290        IF_DESKTOP("comment=\0") /* systemd uses this in fstab */
291    )
292    IF_FEATURE_MOUNT_FLAGS(
293        // vfs flags
294        "nosuid\0"
295        "suid\0"
296        "dev\0"
297        "nodev\0"
298        "exec\0"
299        "noexec\0"
300        "sync\0"
301        "dirsync\0"
302        "async\0"
303        "atime\0"
304        "noatime\0"
305        "diratime\0"
306        "nodiratime\0"
307        "mand\0"
308        "nomand\0"
309        "relatime\0"
310        "norelatime\0"
311        "strictatime\0"
312        "loud\0"
313        "rbind\0"
314
315        // action flags
316        "union\0"
317        "bind\0"
318        "move\0"
319        "make-shared\0"
320        "make-slave\0"
321        "make-private\0"
322        "make-unbindable\0"
323        "make-rshared\0"
324        "make-rslave\0"
325        "make-rprivate\0"
326        "make-runbindable\0"
327    )
328
329    // Always understood.
330    "ro\0"        // vfs flag
331    "rw\0"        // vfs flag
332    "remount\0"   // action flag
333;
334
335
336struct globals {
337#if ENABLE_FEATURE_MOUNT_NFS
338    smalluint nfs_mount_version;
339#endif
340#if ENABLE_FEATURE_MOUNT_VERBOSE
341    unsigned verbose;
342#endif
343    llist_t *fslist;
344    char getmntent_buf[1];
345} FIX_ALIASING;
346enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
347#define G (*(struct globals*)&bb_common_bufsiz1)
348#define nfs_mount_version (G.nfs_mount_version)
349#if ENABLE_FEATURE_MOUNT_VERBOSE
350#define verbose           (G.verbose          )
351#else
352#define verbose           0
353#endif
354#define fslist            (G.fslist           )
355#define getmntent_buf     (G.getmntent_buf    )
356#define INIT_G() do { } while (0)
357
358#if ENABLE_FEATURE_MTAB_SUPPORT
359/*
360 * update_mtab_entry_on_move() is used to update entry in case of mount --move.
361 * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
362 * input mntent and replace it by new one.
363 */
364static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
365{
366    struct mntent *entries, *m;
367    int i, count;
368    FILE *mountTable;
369
370    mountTable = setmntent(bb_path_mtab_file, "r");
371    if (!mountTable) {
372        bb_perror_msg(bb_path_mtab_file);
373        return;
374    }
375
376    entries = NULL;
377    count = 0;
378    while ((m = getmntent(mountTable)) != NULL) {
379        entries = xrealloc_vector(entries, 3, count);
380        entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
381        entries[count].mnt_dir = xstrdup(m->mnt_dir);
382        entries[count].mnt_type = xstrdup(m->mnt_type);
383        entries[count].mnt_opts = xstrdup(m->mnt_opts);
384        entries[count].mnt_freq = m->mnt_freq;
385        entries[count].mnt_passno = m->mnt_passno;
386        count++;
387    }
388    endmntent(mountTable);
389
390    mountTable = setmntent(bb_path_mtab_file, "w");
391    if (mountTable) {
392        for (i = 0; i < count; i++) {
393            if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
394                addmntent(mountTable, &entries[i]);
395            else
396                addmntent(mountTable, mp);
397        }
398        endmntent(mountTable);
399    } else if (errno != EROFS)
400        bb_perror_msg(bb_path_mtab_file);
401
402    if (ENABLE_FEATURE_CLEAN_UP) {
403        for (i = 0; i < count; i++) {
404            free(entries[i].mnt_fsname);
405            free(entries[i].mnt_dir);
406            free(entries[i].mnt_type);
407            free(entries[i].mnt_opts);
408        }
409        free(entries);
410    }
411}
412#endif
413
414#if ENABLE_FEATURE_MOUNT_VERBOSE
415static int verbose_mount(const char *source, const char *target,
416        const char *filesystemtype,
417        unsigned long mountflags, const void *data)
418{
419    int rc;
420
421    errno = 0;
422    rc = mount(source, target, filesystemtype, mountflags, data);
423    if (verbose >= 2)
424        bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
425            source, target, filesystemtype,
426            mountflags, (char*)data, rc);
427    return rc;
428}
429#else
430#define verbose_mount(...) mount(__VA_ARGS__)
431#endif
432
433// Append mount options to string
434static void append_mount_options(char **oldopts, const char *newopts)
435{
436    if (*oldopts && **oldopts) {
437        // Do not insert options which are already there
438        while (newopts[0]) {
439            char *p;
440            int len = strlen(newopts);
441            p = strchr(newopts, ',');
442            if (p) len = p - newopts;
443            p = *oldopts;
444            while (1) {
445                if (!strncmp(p, newopts, len)
446                 && (p[len] == ',' || p[len] == '\0'))
447                    goto skip;
448                p = strchr(p,',');
449                if (!p) break;
450                p++;
451            }
452            p = xasprintf("%s,%.*s", *oldopts, len, newopts);
453            free(*oldopts);
454            *oldopts = p;
455 skip:
456            newopts += len;
457            while (newopts[0] == ',') newopts++;
458        }
459    } else {
460        if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
461        *oldopts = xstrdup(newopts);
462    }
463}
464
465// Use the mount_options list to parse options into flags.
466// Also update list of unrecognized options if unrecognized != NULL
467static unsigned long parse_mount_options(char *options, char **unrecognized)
468{
469    unsigned long flags = MS_SILENT;
470
471    // Loop through options
472    for (;;) {
473        unsigned i;
474        char *comma = strchr(options, ',');
475        const char *option_str = mount_option_str;
476
477        if (comma) *comma = '\0';
478
479// FIXME: use hasmntopt()
480        // Find this option in mount_options
481        for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
482            unsigned opt_len = strlen(option_str);
483
484            if (strncasecmp(option_str, options, opt_len) == 0
485             && (options[opt_len] == '\0'
486                /* or is it "comment=" thingy in fstab? */
487                IF_FEATURE_MOUNT_FSTAB(IF_DESKTOP( || option_str[opt_len-1] == '=' ))
488                )
489            ) {
490                unsigned long fl = mount_options[i];
491                if (fl & BB_MS_INVERTED_VALUE)
492                    flags &= fl;
493                else
494                    flags |= fl;
495                goto found;
496            }
497            option_str += opt_len + 1;
498        }
499        // We did not recognize this option.
500        // If "unrecognized" is not NULL, append option there.
501        // Note that we should not append *empty* option -
502        // in this case we want to pass NULL, not "", to "data"
503        // parameter of mount(2) syscall.
504        // This is crucial for filesystems that don't accept
505        // any arbitrary mount options, like cgroup fs:
506        // "mount -t cgroup none /mnt"
507        if (options[0] && unrecognized) {
508            // Add it to strflags, to pass on to kernel
509            char *p = *unrecognized;
510            unsigned len = p ? strlen(p) : 0;
511            *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
512
513            // Comma separated if it's not the first one
514            if (len) p[len++] = ',';
515            strcpy(p + len, options);
516        }
517 found:
518        if (!comma)
519            break;
520        // Advance to next option
521        *comma = ',';
522        options = ++comma;
523    }
524
525    return flags;
526}
527
528// Return a list of all block device backed filesystems
529static llist_t *get_block_backed_filesystems(void)
530{
531    static const char filesystems[2][sizeof("/proc/filesystems")] = {
532        "/etc/filesystems",
533        "/proc/filesystems",
534    };
535    char *fs, *buf;
536    llist_t *list = NULL;
537    int i;
538    FILE *f;
539
540    for (i = 0; i < 2; i++) {
541        f = fopen_for_read(filesystems[i]);
542        if (!f) continue;
543
544        while ((buf = xmalloc_fgetline(f)) != NULL) {
545            if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
546                goto next;
547            fs = skip_whitespace(buf);
548            if (*fs == '#' || *fs == '*' || !*fs)
549                goto next;
550
551            llist_add_to_end(&list, xstrdup(fs));
552 next:
553            free(buf);
554        }
555        if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
556    }
557
558    return list;
559}
560
561#if ENABLE_FEATURE_CLEAN_UP
562static void delete_block_backed_filesystems(void)
563{
564    llist_free(fslist, free);
565}
566#else
567void delete_block_backed_filesystems(void);
568#endif
569
570// Perform actual mount of specific filesystem at specific location.
571// NB: mp->xxx fields may be trashed on exit
572static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts)
573{
574    int rc = 0;
575
576    if (FAKE_IT) {
577        if (verbose >= 2)
578            bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
579                mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
580                vfsflags, filteropts);
581        goto mtab;
582    }
583
584    // Mount, with fallback to read-only if necessary.
585    for (;;) {
586        errno = 0;
587        rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
588                vfsflags, filteropts);
589
590        // If mount failed, try
591        // helper program mount.<mnt_type>
592        if (HELPERS_ALLOWED && rc && mp->mnt_type) {
593            char *args[8];
594            int errno_save = errno;
595            args[0] = xasprintf("mount.%s", mp->mnt_type);
596            rc = 1;
597            if (FAKE_IT)
598                args[rc++] = (char *)"-f";
599            if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
600                args[rc++] = (char *)"-n";
601            args[rc++] = mp->mnt_fsname;
602            args[rc++] = mp->mnt_dir;
603            if (filteropts) {
604                args[rc++] = (char *)"-o";
605                args[rc++] = filteropts;
606            }
607            args[rc] = NULL;
608            rc = spawn_and_wait(args);
609            free(args[0]);
610            if (!rc)
611                break;
612            errno = errno_save;
613        }
614
615        if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
616            break;
617        if (!(vfsflags & MS_SILENT))
618            bb_error_msg("%s is write-protected, mounting read-only",
619                        mp->mnt_fsname);
620        vfsflags |= MS_RDONLY;
621    }
622
623    // Abort entirely if permission denied.
624
625    if (rc && errno == EPERM)
626        bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
627
628    // If the mount was successful, and we're maintaining an old-style
629    // mtab file by hand, add the new entry to it now.
630 mtab:
631    if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
632        char *fsname;
633        FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
634        const char *option_str = mount_option_str;
635        int i;
636
637        if (!mountTable) {
638            bb_perror_msg(bb_path_mtab_file);
639            goto ret;
640        }
641
642        // Add vfs string flags
643        for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
644            if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
645                append_mount_options(&(mp->mnt_opts), option_str);
646            option_str += strlen(option_str) + 1;
647        }
648
649        // Remove trailing / (if any) from directory we mounted on
650        i = strlen(mp->mnt_dir) - 1;
651        while (i > 0 && mp->mnt_dir[i] == '/')
652            mp->mnt_dir[i--] = '\0';
653
654        // Convert to canonical pathnames as needed
655        mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
656        fsname = NULL;
657        if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
658            mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
659            mp->mnt_type = (char*)"bind";
660        }
661        mp->mnt_freq = mp->mnt_passno = 0;
662
663        // Write and close
664#if ENABLE_FEATURE_MTAB_SUPPORT
665        if (vfsflags & MS_MOVE)
666            update_mtab_entry_on_move(mp);
667        else
668#endif
669            addmntent(mountTable, mp);
670        endmntent(mountTable);
671
672        if (ENABLE_FEATURE_CLEAN_UP) {
673            free(mp->mnt_dir);
674            free(fsname);
675        }
676    }
677 ret:
678    return rc;
679}
680
681#if ENABLE_FEATURE_MOUNT_NFS
682
683/*
684 * Linux NFS mount
685 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
686 *
687 * Licensed under GPLv2, see file LICENSE in this source tree.
688 *
689 * Wed Feb  8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
690 * numbers to be specified on the command line.
691 *
692 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
693 * Omit the call to connect() for Linux version 1.3.11 or later.
694 *
695 * Wed Oct  1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
696 * Implemented the "bg", "fg" and "retry" mount options for NFS.
697 *
698 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
699 * - added Native Language Support
700 *
701 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
702 * plus NFSv3 stuff.
703 */
704
705#define MOUNTPORT 635
706#define MNTPATHLEN 1024
707#define MNTNAMLEN 255
708#define FHSIZE 32
709#define FHSIZE3 64
710
711typedef char fhandle[FHSIZE];
712
713typedef struct {
714    unsigned int fhandle3_len;
715    char *fhandle3_val;
716} fhandle3;
717
718enum mountstat3 {
719    MNT_OK = 0,
720    MNT3ERR_PERM = 1,
721    MNT3ERR_NOENT = 2,
722    MNT3ERR_IO = 5,
723    MNT3ERR_ACCES = 13,
724    MNT3ERR_NOTDIR = 20,
725    MNT3ERR_INVAL = 22,
726    MNT3ERR_NAMETOOLONG = 63,
727    MNT3ERR_NOTSUPP = 10004,
728    MNT3ERR_SERVERFAULT = 10006,
729};
730typedef enum mountstat3 mountstat3;
731
732struct fhstatus {
733    unsigned int fhs_status;
734    union {
735        fhandle fhs_fhandle;
736    } fhstatus_u;
737};
738typedef struct fhstatus fhstatus;
739
740struct mountres3_ok {
741    fhandle3 fhandle;
742    struct {
743        unsigned int auth_flavours_len;
744        char *auth_flavours_val;
745    } auth_flavours;
746};
747typedef struct mountres3_ok mountres3_ok;
748
749struct mountres3 {
750    mountstat3 fhs_status;
751    union {
752        mountres3_ok mountinfo;
753    } mountres3_u;
754};
755typedef struct mountres3 mountres3;
756
757typedef char *dirpath;
758
759typedef char *name;
760
761typedef struct mountbody *mountlist;
762
763struct mountbody {
764    name ml_hostname;
765    dirpath ml_directory;
766    mountlist ml_next;
767};
768typedef struct mountbody mountbody;
769
770typedef struct groupnode *groups;
771
772struct groupnode {
773    name gr_name;
774    groups gr_next;
775};
776typedef struct groupnode groupnode;
777
778typedef struct exportnode *exports;
779
780struct exportnode {
781    dirpath ex_dir;
782    groups ex_groups;
783    exports ex_next;
784};
785typedef struct exportnode exportnode;
786
787struct ppathcnf {
788    int pc_link_max;
789    short pc_max_canon;
790    short pc_max_input;
791    short pc_name_max;
792    short pc_path_max;
793    short pc_pipe_buf;
794    uint8_t pc_vdisable;
795    char pc_xxx;
796    short pc_mask[2];
797};
798typedef struct ppathcnf ppathcnf;
799
800#define MOUNTPROG 100005
801#define MOUNTVERS 1
802
803#define MOUNTPROC_NULL 0
804#define MOUNTPROC_MNT 1
805#define MOUNTPROC_DUMP 2
806#define MOUNTPROC_UMNT 3
807#define MOUNTPROC_UMNTALL 4
808#define MOUNTPROC_EXPORT 5
809#define MOUNTPROC_EXPORTALL 6
810
811#define MOUNTVERS_POSIX 2
812
813#define MOUNTPROC_PATHCONF 7
814
815#define MOUNT_V3 3
816
817#define MOUNTPROC3_NULL 0
818#define MOUNTPROC3_MNT 1
819#define MOUNTPROC3_DUMP 2
820#define MOUNTPROC3_UMNT 3
821#define MOUNTPROC3_UMNTALL 4
822#define MOUNTPROC3_EXPORT 5
823
824enum {
825#ifndef NFS_FHSIZE
826    NFS_FHSIZE = 32,
827#endif
828#ifndef NFS_PORT
829    NFS_PORT = 2049
830#endif
831};
832
833/*
834 * We want to be able to compile mount on old kernels in such a way
835 * that the binary will work well on more recent kernels.
836 * Thus, if necessary we teach nfsmount.c the structure of new fields
837 * that will come later.
838 *
839 * Moreover, the new kernel includes conflict with glibc includes
840 * so it is easiest to ignore the kernel altogether (at compile time).
841 */
842
843struct nfs2_fh {
844    char            data[32];
845};
846struct nfs3_fh {
847    unsigned short  size;
848    unsigned char   data[64];
849};
850
851struct nfs_mount_data {
852    int     version;    /* 1 */
853    int     fd;     /* 1 */
854    struct nfs2_fh  old_root;   /* 1 */
855    int     flags;      /* 1 */
856    int     rsize;      /* 1 */
857    int     wsize;      /* 1 */
858    int     timeo;      /* 1 */
859    int     retrans;    /* 1 */
860    int     acregmin;   /* 1 */
861    int     acregmax;   /* 1 */
862    int     acdirmin;   /* 1 */
863    int     acdirmax;   /* 1 */
864    struct sockaddr_in addr;    /* 1 */
865    char        hostname[256];  /* 1 */
866    int     namlen;     /* 2 */
867    unsigned int    bsize;      /* 3 */
868    struct nfs3_fh  root;       /* 4 */
869};
870
871/* bits in the flags field */
872enum {
873    NFS_MOUNT_SOFT = 0x0001,    /* 1 */
874    NFS_MOUNT_INTR = 0x0002,    /* 1 */
875    NFS_MOUNT_SECURE = 0x0004,  /* 1 */
876    NFS_MOUNT_POSIX = 0x0008,   /* 1 */
877    NFS_MOUNT_NOCTO = 0x0010,   /* 1 */
878    NFS_MOUNT_NOAC = 0x0020,    /* 1 */
879    NFS_MOUNT_TCP = 0x0040,     /* 2 */
880    NFS_MOUNT_VER3 = 0x0080,    /* 3 */
881    NFS_MOUNT_KERBEROS = 0x0100,    /* 3 */
882    NFS_MOUNT_NONLM = 0x0200,   /* 3 */
883    NFS_MOUNT_NOACL = 0x0800,   /* 4 */
884    NFS_MOUNT_NORDIRPLUS = 0x4000
885};
886
887
888/*
889 * We need to translate between nfs status return values and
890 * the local errno values which may not be the same.
891 *
892 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
893 * "after #include <errno.h> the symbol errno is reserved for any use,
894 *  it cannot even be used as a struct tag or field name".
895 */
896#ifndef EDQUOT
897# define EDQUOT ENOSPC
898#endif
899/* Convert each NFSERR_BLAH into EBLAH */
900static const uint8_t nfs_err_stat[] = {
901     1,  2,  5,  6, 13, 17,
902    19, 20, 21, 22, 27, 28,
903    30, 63, 66, 69, 70, 71
904};
905#if ( \
906    EPERM | ENOENT      | EIO      | ENXIO | EACCES| EEXIST | \
907    ENODEV| ENOTDIR     | EISDIR   | EINVAL| EFBIG | ENOSPC | \
908    EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
909typedef uint8_t nfs_err_type;
910#else
911typedef uint16_t nfs_err_type;
912#endif
913static const nfs_err_type nfs_err_errnum[] = {
914    EPERM , ENOENT      , EIO      , ENXIO , EACCES, EEXIST,
915    ENODEV, ENOTDIR     , EISDIR   , EINVAL, EFBIG , ENOSPC,
916    EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
917};
918static char *nfs_strerror(int status)
919{
920    int i;
921
922    for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
923        if (nfs_err_stat[i] == status)
924            return strerror(nfs_err_errnum[i]);
925    }
926    return xasprintf("unknown nfs status return value: %d", status);
927}
928
929static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
930{
931    return xdr_opaque(xdrs, objp, FHSIZE);
932}
933
934static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
935{
936    if (!xdr_u_int(xdrs, &objp->fhs_status))
937        return FALSE;
938    if (objp->fhs_status == 0)
939        return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
940    return TRUE;
941}
942
943static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
944{
945    return xdr_string(xdrs, objp, MNTPATHLEN);
946}
947
948static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
949{
950    return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
951            (unsigned int *) &objp->fhandle3_len,
952            FHSIZE3);
953}
954
955static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
956{
957    if (!xdr_fhandle3(xdrs, &objp->fhandle))
958        return FALSE;
959    return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
960            &(objp->auth_flavours.auth_flavours_len),
961            ~0,
962            sizeof(int),
963            (xdrproc_t) xdr_int);
964}
965
966static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
967{
968    return xdr_enum(xdrs, (enum_t *) objp);
969}
970
971static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
972{
973    if (!xdr_mountstat3(xdrs, &objp->fhs_status))
974        return FALSE;
975    if (objp->fhs_status == MNT_OK)
976        return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
977    return TRUE;
978}
979
980#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
981
982/*
983 * Unfortunately, the kernel prints annoying console messages
984 * in case of an unexpected nfs mount version (instead of
985 * just returning some error).  Therefore we'll have to try
986 * and figure out what version the kernel expects.
987 *
988 * Variables:
989 *  KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
990 *  NFS_MOUNT_VERSION: these nfsmount sources at compile time
991 *  nfs_mount_version: version this source and running kernel can handle
992 */
993static void
994find_kernel_nfs_mount_version(void)
995{
996    int kernel_version;
997
998    if (nfs_mount_version)
999        return;
1000
1001    nfs_mount_version = 4; /* default */
1002
1003    kernel_version = get_linux_version_code();
1004    if (kernel_version) {
1005        if (kernel_version < KERNEL_VERSION(2,2,18))
1006            nfs_mount_version = 3;
1007        /* else v4 since 2.3.99pre4 */
1008    }
1009}
1010
1011static void
1012get_mountport(struct pmap *pm_mnt,
1013    struct sockaddr_in *server_addr,
1014    long unsigned prog,
1015    long unsigned version,
1016    long unsigned proto,
1017    long unsigned port)
1018{
1019    struct pmaplist *pmap;
1020
1021    server_addr->sin_port = PMAPPORT;
1022/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
1023 * I understand it like "IPv6 for this is not 100% ready" */
1024    pmap = pmap_getmaps(server_addr);
1025
1026    if (version > MAX_NFSPROT)
1027        version = MAX_NFSPROT;
1028    if (!prog)
1029        prog = MOUNTPROG;
1030    pm_mnt->pm_prog = prog;
1031    pm_mnt->pm_vers = version;
1032    pm_mnt->pm_prot = proto;
1033    pm_mnt->pm_port = port;
1034
1035    while (pmap) {
1036        if (pmap->pml_map.pm_prog != prog)
1037            goto next;
1038        if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
1039            goto next;
1040        if (version > 2 && pmap->pml_map.pm_vers != version)
1041            goto next;
1042        if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1043            goto next;
1044        if (pmap->pml_map.pm_vers > MAX_NFSPROT
1045         || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1046         || (port && pmap->pml_map.pm_port != port)
1047        ) {
1048            goto next;
1049        }
1050        memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1051 next:
1052        pmap = pmap->pml_next;
1053    }
1054    if (!pm_mnt->pm_vers)
1055        pm_mnt->pm_vers = MOUNTVERS;
1056    if (!pm_mnt->pm_port)
1057        pm_mnt->pm_port = MOUNTPORT;
1058    if (!pm_mnt->pm_prot)
1059        pm_mnt->pm_prot = IPPROTO_TCP;
1060}
1061
1062#if BB_MMU
1063static int daemonize(void)
1064{
1065    int pid = fork();
1066    if (pid < 0) /* error */
1067        return -errno;
1068    if (pid > 0) /* parent */
1069        return 0;
1070    /* child */
1071    close(0);
1072    xopen(bb_dev_null, O_RDWR);
1073    xdup2(0, 1);
1074    xdup2(0, 2);
1075    setsid();
1076    openlog(applet_name, LOG_PID, LOG_DAEMON);
1077    logmode = LOGMODE_SYSLOG;
1078    return 1;
1079}
1080#else
1081static inline int daemonize(void) { return -ENOSYS; }
1082#endif
1083
1084/* TODO */
1085static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
1086{
1087    return 0;
1088}
1089
1090/* RPC strerror analogs are terminally idiotic:
1091 * *mandatory* prefix and \n at end.
1092 * This hopefully helps. Usage:
1093 * error_msg_rpc(clnt_*error*(" ")) */
1094static void error_msg_rpc(const char *msg)
1095{
1096    int len;
1097    while (msg[0] == ' ' || msg[0] == ':') msg++;
1098    len = strlen(msg);
1099    while (len && msg[len-1] == '\n') len--;
1100    bb_error_msg("%.*s", len, msg);
1101}
1102
1103/* NB: mp->xxx fields may be trashed on exit */
1104static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1105{
1106    CLIENT *mclient;
1107    char *hostname;
1108    char *pathname;
1109    char *mounthost;
1110    /* prior to 2.6.23, kernel took NFS options in a form of this struct
1111     * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1112     * then data pointer is interpreted as a string. */
1113    struct nfs_mount_data data;
1114    char *opt;
1115    struct hostent *hp;
1116    struct sockaddr_in server_addr;
1117    struct sockaddr_in mount_server_addr;
1118    int msock, fsock;
1119    union {
1120        struct fhstatus nfsv2;
1121        struct mountres3 nfsv3;
1122    } status;
1123    int daemonized;
1124    char *s;
1125    int port;
1126    int mountport;
1127    int proto;
1128#if BB_MMU
1129    smallint bg = 0;
1130#else
1131    enum { bg = 0 };
1132#endif
1133    int retry;
1134    int mountprog;
1135    int mountvers;
1136    int nfsprog;
1137    int nfsvers;
1138    int retval;
1139    /* these all are one-bit really. gcc 4.3.1 likes this combination: */
1140    smallint tcp;
1141    smallint soft;
1142    int intr;
1143    int posix;
1144    int nocto;
1145    int noac;
1146    int nordirplus;
1147    int nolock;
1148    int noacl;
1149
1150    find_kernel_nfs_mount_version();
1151
1152    daemonized = 0;
1153    mounthost = NULL;
1154    retval = ETIMEDOUT;
1155    msock = fsock = -1;
1156    mclient = NULL;
1157
1158    /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1159
1160    filteropts = xstrdup(filteropts); /* going to trash it later... */
1161
1162    hostname = xstrdup(mp->mnt_fsname);
1163    /* mount_main() guarantees that ':' is there */
1164    s = strchr(hostname, ':');
1165    pathname = s + 1;
1166    *s = '\0';
1167    /* Ignore all but first hostname in replicated mounts
1168     * until they can be fully supported. (mack@sgi.com) */
1169    s = strchr(hostname, ',');
1170    if (s) {
1171        *s = '\0';
1172        bb_error_msg("warning: multiple hostnames not supported");
1173    }
1174
1175    server_addr.sin_family = AF_INET;
1176    if (!inet_aton(hostname, &server_addr.sin_addr)) {
1177        hp = gethostbyname(hostname);
1178        if (hp == NULL) {
1179            bb_herror_msg("%s", hostname);
1180            goto fail;
1181        }
1182        if (hp->h_length != (int)sizeof(struct in_addr)) {
1183            bb_error_msg_and_die("only IPv4 is supported");
1184        }
1185        memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1186    }
1187
1188    memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1189
1190    /* add IP address to mtab options for use when unmounting */
1191
1192    if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1193        mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1194    } else {
1195        char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1196                    mp->mnt_opts[0] ? "," : "",
1197                    inet_ntoa(server_addr.sin_addr));
1198        free(mp->mnt_opts);
1199        mp->mnt_opts = tmp;
1200    }
1201
1202    /* Set default options.
1203     * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1204     * let the kernel decide.
1205     * timeo is filled in after we know whether it'll be TCP or UDP. */
1206    memset(&data, 0, sizeof(data));
1207    data.retrans  = 3;
1208    data.acregmin = 3;
1209    data.acregmax = 60;
1210    data.acdirmin = 30;
1211    data.acdirmax = 60;
1212    data.namlen   = NAME_MAX;
1213
1214    soft = 0;
1215    intr = 0;
1216    posix = 0;
1217    nocto = 0;
1218    nolock = 0;
1219    noac = 0;
1220    nordirplus = 0;
1221    noacl = 0;
1222    retry = 10000;      /* 10000 minutes ~ 1 week */
1223    tcp = 1;            /* nfs-utils uses tcp per default */
1224
1225    mountprog = MOUNTPROG;
1226    mountvers = 0;
1227    port = 0;
1228    mountport = 0;
1229    nfsprog = 100003;
1230    nfsvers = 0;
1231
1232    /* parse options */
1233    if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
1234        char *opteq = strchr(opt, '=');
1235        if (opteq) {
1236            int val, idx;
1237            static const char options[] ALIGN1 =
1238                /* 0 */ "rsize\0"
1239                /* 1 */ "wsize\0"
1240                /* 2 */ "timeo\0"
1241                /* 3 */ "retrans\0"
1242                /* 4 */ "acregmin\0"
1243                /* 5 */ "acregmax\0"
1244                /* 6 */ "acdirmin\0"
1245                /* 7 */ "acdirmax\0"
1246                /* 8 */ "actimeo\0"
1247                /* 9 */ "retry\0"
1248                /* 10 */ "port\0"
1249                /* 11 */ "mountport\0"
1250                /* 12 */ "mounthost\0"
1251                /* 13 */ "mountprog\0"
1252                /* 14 */ "mountvers\0"
1253                /* 15 */ "nfsprog\0"
1254                /* 16 */ "nfsvers\0"
1255                /* 17 */ "vers\0"
1256                /* 18 */ "proto\0"
1257                /* 19 */ "namlen\0"
1258                /* 20 */ "addr\0";
1259
1260            *opteq++ = '\0';
1261            idx = index_in_strings(options, opt);
1262            switch (idx) {
1263            case 12: // "mounthost"
1264                mounthost = xstrndup(opteq,
1265                        strcspn(opteq, " \t\n\r,"));
1266                continue;
1267            case 18: // "proto"
1268                if (!strncmp(opteq, "tcp", 3))
1269                    tcp = 1;
1270                else if (!strncmp(opteq, "udp", 3))
1271                    tcp = 0;
1272                else
1273                    bb_error_msg("warning: unrecognized proto= option");
1274                continue;
1275            case 20: // "addr" - ignore
1276                continue;
1277            case -1: // unknown
1278                if (vfsflags & MS_REMOUNT)
1279                    continue;
1280            }
1281
1282            val = xatoi_positive(opteq);
1283            switch (idx) {
1284            case 0: // "rsize"
1285                data.rsize = val;
1286                continue;
1287            case 1: // "wsize"
1288                data.wsize = val;
1289                continue;
1290            case 2: // "timeo"
1291                data.timeo = val;
1292                continue;
1293            case 3: // "retrans"
1294                data.retrans = val;
1295                continue;
1296            case 4: // "acregmin"
1297                data.acregmin = val;
1298                continue;
1299            case 5: // "acregmax"
1300                data.acregmax = val;
1301                continue;
1302            case 6: // "acdirmin"
1303                data.acdirmin = val;
1304                continue;
1305            case 7: // "acdirmax"
1306                data.acdirmax = val;
1307                continue;
1308            case 8: // "actimeo"
1309                data.acregmin = val;
1310                data.acregmax = val;
1311                data.acdirmin = val;
1312                data.acdirmax = val;
1313                continue;
1314            case 9: // "retry"
1315                retry = val;
1316                continue;
1317            case 10: // "port"
1318                port = val;
1319                continue;
1320            case 11: // "mountport"
1321                mountport = val;
1322                continue;
1323            case 13: // "mountprog"
1324                mountprog = val;
1325                continue;
1326            case 14: // "mountvers"
1327                mountvers = val;
1328                continue;
1329            case 15: // "nfsprog"
1330                nfsprog = val;
1331                continue;
1332            case 16: // "nfsvers"
1333            case 17: // "vers"
1334                nfsvers = val;
1335                continue;
1336            case 19: // "namlen"
1337                //if (nfs_mount_version >= 2)
1338                    data.namlen = val;
1339                //else
1340                //  bb_error_msg("warning: option namlen is not supported\n");
1341                continue;
1342            default:
1343                bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1344                goto fail;
1345            }
1346        }
1347        else { /* not of the form opt=val */
1348            static const char options[] ALIGN1 =
1349                "bg\0"
1350                "fg\0"
1351                "soft\0"
1352                "hard\0"
1353                "intr\0"
1354                "posix\0"
1355                "cto\0"
1356                "ac\0"
1357                "tcp\0"
1358                "udp\0"
1359                "lock\0"
1360                "rdirplus\0"
1361                "acl\0";
1362            int val = 1;
1363            if (!strncmp(opt, "no", 2)) {
1364                val = 0;
1365                opt += 2;
1366            }
1367            switch (index_in_strings(options, opt)) {
1368            case 0: // "bg"
1369#if BB_MMU
1370                bg = val;
1371#endif
1372                break;
1373            case 1: // "fg"
1374#if BB_MMU
1375                bg = !val;
1376#endif
1377                break;
1378            case 2: // "soft"
1379                soft = val;
1380                break;
1381            case 3: // "hard"
1382                soft = !val;
1383                break;
1384            case 4: // "intr"
1385                intr = val;
1386                break;
1387            case 5: // "posix"
1388                posix = val;
1389                break;
1390            case 6: // "cto"
1391                nocto = !val;
1392                break;
1393            case 7: // "ac"
1394                noac = !val;
1395                break;
1396            case 8: // "tcp"
1397                tcp = val;
1398                break;
1399            case 9: // "udp"
1400                tcp = !val;
1401                break;
1402            case 10: // "lock"
1403                if (nfs_mount_version >= 3)
1404                    nolock = !val;
1405                else
1406                    bb_error_msg("warning: option nolock is not supported");
1407                break;
1408            case 11: //rdirplus
1409                nordirplus = !val;
1410                break;
1411            case 12: // acl
1412                noacl = !val;
1413                break;
1414            default:
1415                bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1416                goto fail;
1417            }
1418        }
1419    }
1420    proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1421
1422    data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1423        | (intr ? NFS_MOUNT_INTR : 0)
1424        | (posix ? NFS_MOUNT_POSIX : 0)
1425        | (nocto ? NFS_MOUNT_NOCTO : 0)
1426        | (noac ? NFS_MOUNT_NOAC : 0)
1427        | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
1428        | (noacl ? NFS_MOUNT_NOACL : 0);
1429    if (nfs_mount_version >= 2)
1430        data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1431    if (nfs_mount_version >= 3)
1432        data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1433    if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1434        bb_error_msg("NFSv%d not supported", nfsvers);
1435        goto fail;
1436    }
1437    if (nfsvers && !mountvers)
1438        mountvers = (nfsvers < 3) ? 1 : nfsvers;
1439    if (nfsvers && nfsvers < mountvers) {
1440        mountvers = nfsvers;
1441    }
1442
1443    /* Adjust options if none specified */
1444    if (!data.timeo)
1445        data.timeo = tcp ? 70 : 7;
1446
1447    data.version = nfs_mount_version;
1448
1449    if (vfsflags & MS_REMOUNT)
1450        goto do_mount;
1451
1452    /*
1453     * If the previous mount operation on the same host was
1454     * backgrounded, and the "bg" for this mount is also set,
1455     * give up immediately, to avoid the initial timeout.
1456     */
1457    if (bg && we_saw_this_host_before(hostname)) {
1458        daemonized = daemonize();
1459        if (daemonized <= 0) { /* parent or error */
1460            retval = -daemonized;
1461            goto ret;
1462        }
1463    }
1464
1465    /* Create mount daemon client */
1466    /* See if the nfs host = mount host. */
1467    if (mounthost) {
1468        if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1469            mount_server_addr.sin_family = AF_INET;
1470            mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1471        } else {
1472            hp = gethostbyname(mounthost);
1473            if (hp == NULL) {
1474                bb_herror_msg("%s", mounthost);
1475                goto fail;
1476            }
1477            if (hp->h_length != (int)sizeof(struct in_addr)) {
1478                bb_error_msg_and_die("only IPv4 is supported");
1479            }
1480            mount_server_addr.sin_family = AF_INET;
1481            memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1482        }
1483    }
1484
1485    /*
1486     * The following loop implements the mount retries. When the mount
1487     * times out, and the "bg" option is set, we background ourself
1488     * and continue trying.
1489     *
1490     * The case where the mount point is not present and the "bg"
1491     * option is set, is treated as a timeout. This is done to
1492     * support nested mounts.
1493     *
1494     * The "retry" count specified by the user is the number of
1495     * minutes to retry before giving up.
1496     */
1497    {
1498        struct timeval total_timeout;
1499        struct timeval retry_timeout;
1500        struct pmap pm_mnt;
1501        time_t t;
1502        time_t prevt;
1503        time_t timeout;
1504
1505        retry_timeout.tv_sec = 3;
1506        retry_timeout.tv_usec = 0;
1507        total_timeout.tv_sec = 20;
1508        total_timeout.tv_usec = 0;
1509/* FIXME: use monotonic()? */
1510        timeout = time(NULL) + 60 * retry;
1511        prevt = 0;
1512        t = 30;
1513 retry:
1514        /* Be careful not to use too many CPU cycles */
1515        if (t - prevt < 30)
1516            sleep(30);
1517
1518        get_mountport(&pm_mnt, &mount_server_addr,
1519                mountprog,
1520                mountvers,
1521                proto,
1522                mountport);
1523        nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
1524
1525        /* contact the mount daemon via TCP */
1526        mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1527        msock = RPC_ANYSOCK;
1528
1529        switch (pm_mnt.pm_prot) {
1530        case IPPROTO_UDP:
1531            mclient = clntudp_create(&mount_server_addr,
1532                        pm_mnt.pm_prog,
1533                        pm_mnt.pm_vers,
1534                        retry_timeout,
1535                        &msock);
1536            if (mclient)
1537                break;
1538            mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1539            msock = RPC_ANYSOCK;
1540        case IPPROTO_TCP:
1541            mclient = clnttcp_create(&mount_server_addr,
1542                        pm_mnt.pm_prog,
1543                        pm_mnt.pm_vers,
1544                        &msock, 0, 0);
1545            break;
1546        default:
1547            mclient = NULL;
1548        }
1549        if (!mclient) {
1550            if (!daemonized && prevt == 0)
1551                error_msg_rpc(clnt_spcreateerror(" "));
1552        } else {
1553            enum clnt_stat clnt_stat;
1554
1555            /* Try to mount hostname:pathname */
1556            mclient->cl_auth = authunix_create_default();
1557
1558            /* Make pointers in xdr_mountres3 NULL so
1559             * that xdr_array allocates memory for us
1560             */
1561            memset(&status, 0, sizeof(status));
1562
1563            if (pm_mnt.pm_vers == 3)
1564                clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1565                        (xdrproc_t) xdr_dirpath,
1566                        (caddr_t) &pathname,
1567                        (xdrproc_t) xdr_mountres3,
1568                        (caddr_t) &status,
1569                        total_timeout);
1570            else
1571                clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1572                        (xdrproc_t) xdr_dirpath,
1573                        (caddr_t) &pathname,
1574                        (xdrproc_t) xdr_fhstatus,
1575                        (caddr_t) &status,
1576                        total_timeout);
1577
1578            if (clnt_stat == RPC_SUCCESS)
1579                goto prepare_kernel_data; /* we're done */
1580            if (errno != ECONNREFUSED) {
1581                error_msg_rpc(clnt_sperror(mclient, " "));
1582                goto fail;  /* don't retry */
1583            }
1584            /* Connection refused */
1585            if (!daemonized && prevt == 0) /* print just once */
1586                error_msg_rpc(clnt_sperror(mclient, " "));
1587            auth_destroy(mclient->cl_auth);
1588            clnt_destroy(mclient);
1589            mclient = NULL;
1590            close(msock);
1591            msock = -1;
1592        }
1593
1594        /* Timeout. We are going to retry... maybe */
1595        if (!bg)
1596            goto fail;
1597        if (!daemonized) {
1598            daemonized = daemonize();
1599            if (daemonized <= 0) { /* parent or error */
1600                retval = -daemonized;
1601                goto ret;
1602            }
1603        }
1604        prevt = t;
1605        t = time(NULL);
1606        if (t >= timeout)
1607            /* TODO error message */
1608            goto fail;
1609
1610        goto retry;
1611    }
1612
1613 prepare_kernel_data:
1614
1615    if (nfsvers == 2) {
1616        if (status.nfsv2.fhs_status != 0) {
1617            bb_error_msg("%s:%s failed, reason given by server: %s",
1618                hostname, pathname,
1619                nfs_strerror(status.nfsv2.fhs_status));
1620            goto fail;
1621        }
1622        memcpy(data.root.data,
1623                (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1624                NFS_FHSIZE);
1625        data.root.size = NFS_FHSIZE;
1626        memcpy(data.old_root.data,
1627                (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1628                NFS_FHSIZE);
1629    } else {
1630        fhandle3 *my_fhandle;
1631        if (status.nfsv3.fhs_status != 0) {
1632            bb_error_msg("%s:%s failed, reason given by server: %s",
1633                hostname, pathname,
1634                nfs_strerror(status.nfsv3.fhs_status));
1635            goto fail;
1636        }
1637        my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1638        memset(data.old_root.data, 0, NFS_FHSIZE);
1639        memset(&data.root, 0, sizeof(data.root));
1640        data.root.size = my_fhandle->fhandle3_len;
1641        memcpy(data.root.data,
1642                (char *) my_fhandle->fhandle3_val,
1643                my_fhandle->fhandle3_len);
1644
1645        data.flags |= NFS_MOUNT_VER3;
1646    }
1647
1648    /* Create nfs socket for kernel */
1649    if (tcp) {
1650        if (nfs_mount_version < 3) {
1651            bb_error_msg("NFS over TCP is not supported");
1652            goto fail;
1653        }
1654        fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1655    } else
1656        fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1657    if (fsock < 0) {
1658        bb_perror_msg("nfs socket");
1659        goto fail;
1660    }
1661    if (bindresvport(fsock, 0) < 0) {
1662        bb_perror_msg("nfs bindresvport");
1663        goto fail;
1664    }
1665    if (port == 0) {
1666        server_addr.sin_port = PMAPPORT;
1667        port = pmap_getport(&server_addr, nfsprog, nfsvers,
1668                    tcp ? IPPROTO_TCP : IPPROTO_UDP);
1669        if (port == 0)
1670            port = NFS_PORT;
1671    }
1672    server_addr.sin_port = htons(port);
1673
1674    /* Prepare data structure for kernel */
1675    data.fd = fsock;
1676    memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1677    strncpy(data.hostname, hostname, sizeof(data.hostname));
1678
1679    /* Clean up */
1680    auth_destroy(mclient->cl_auth);
1681    clnt_destroy(mclient);
1682    close(msock);
1683    msock = -1;
1684
1685    if (bg) {
1686        /* We must wait until mount directory is available */
1687        struct stat statbuf;
1688        int delay = 1;
1689        while (stat(mp->mnt_dir, &statbuf) == -1) {
1690            if (!daemonized) {
1691                daemonized = daemonize();
1692                if (daemonized <= 0) { /* parent or error */
1693/* FIXME: parent doesn't close fsock - ??! */
1694                    retval = -daemonized;
1695                    goto ret;
1696                }
1697            }
1698            sleep(delay);   /* 1, 2, 4, 8, 16, 30, ... */
1699            delay *= 2;
1700            if (delay > 30)
1701                delay = 30;
1702        }
1703    }
1704
1705    /* Perform actual mount */
1706 do_mount:
1707    retval = mount_it_now(mp, vfsflags, (char*)&data);
1708    goto ret;
1709
1710    /* Abort */
1711 fail:
1712    if (msock >= 0) {
1713        if (mclient) {
1714            auth_destroy(mclient->cl_auth);
1715            clnt_destroy(mclient);
1716        }
1717        close(msock);
1718    }
1719    if (fsock >= 0)
1720        close(fsock);
1721
1722 ret:
1723    free(hostname);
1724    free(mounthost);
1725    free(filteropts);
1726    return retval;
1727}
1728
1729#else // !ENABLE_FEATURE_MOUNT_NFS
1730
1731/* Linux 2.6.23+ supports nfs mounts with options passed as a string.
1732 * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
1733 * (However, note that then you lose any chances that NFS over IPv6 would work).
1734 */
1735static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1736{
1737    len_and_sockaddr *lsa;
1738    char *opts;
1739    char *end;
1740    char *dotted;
1741    int ret;
1742
1743# if ENABLE_FEATURE_IPV6
1744    end = strchr(mp->mnt_fsname, ']');
1745    if (end && end[1] == ':')
1746        end++;
1747    else
1748# endif
1749        /* mount_main() guarantees that ':' is there */
1750        end = strchr(mp->mnt_fsname, ':');
1751
1752    *end = '\0';
1753    lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
1754    *end = ':';
1755    dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1756    if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1757    opts = xasprintf("%s%saddr=%s",
1758        filteropts ? filteropts : "",
1759        filteropts ? "," : "",
1760        dotted
1761    );
1762    if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1763    ret = mount_it_now(mp, vfsflags, opts);
1764    if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1765
1766    return ret;
1767}
1768
1769#endif // !ENABLE_FEATURE_MOUNT_NFS
1770
1771// Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem
1772// type detection.  Returns 0 for success, nonzero for failure.
1773// NB: mp->xxx fields may be trashed on exit
1774static int singlemount(struct mntent *mp, int ignore_busy)
1775{
1776    int rc = -1;
1777    unsigned long vfsflags;
1778    char *loopFile = NULL, *filteropts = NULL;
1779    llist_t *fl = NULL;
1780    struct stat st;
1781
1782    errno = 0;
1783
1784    vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1785
1786    // Treat fstype "auto" as unspecified
1787    if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1788        mp->mnt_type = NULL;
1789
1790    // Might this be a virtual filesystem?
1791    if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1792        char *args[35];
1793        char *s;
1794        int n;
1795        // fsname: "cmd#arg1#arg2..."
1796        // WARNING: allows execution of arbitrary commands!
1797        // Try "mount 'sh#-c#sh' bogus_dir".
1798        // It is safe ONLY because non-root
1799        // cannot use two-argument mount command
1800        // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1801        // "mount: can't find sh#-c#sh in /etc/fstab"
1802        // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1803
1804        s = mp->mnt_fsname;
1805        n = 0;
1806        args[n++] = s;
1807        while (*s && n < 35 - 2) {
1808            if (*s++ == '#' && *s != '#') {
1809                s[-1] = '\0';
1810                args[n++] = s;
1811            }
1812        }
1813        args[n++] = mp->mnt_dir;
1814        args[n] = NULL;
1815        rc = spawn_and_wait(args);
1816        goto report_error;
1817    }
1818
1819    // Might this be an CIFS filesystem?
1820    if (ENABLE_FEATURE_MOUNT_CIFS
1821     && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1822     && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1823     && mp->mnt_fsname[0] == mp->mnt_fsname[1]
1824    ) {
1825        int len;
1826        char c;
1827        char *hostname, *share;
1828        char *dotted, *ip;
1829        len_and_sockaddr *lsa;
1830
1831        // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
1832
1833        hostname = mp->mnt_fsname + 2;
1834        len = strcspn(hostname, "/\\");
1835        share = hostname + len + 1;
1836        if (len == 0          // 3rd char is a [back]slash (IOW: empty hostname)
1837         || share[-1] == '\0' // no [back]slash after hostname
1838         || share[0] == '\0'  // empty share name
1839        ) {
1840            goto report_error;
1841        }
1842        c = share[-1];
1843        share[-1] = '\0';
1844        len = strcspn(share, "/\\");
1845
1846        // "unc=\\hostname\share" option is mandatory
1847        // after CIFS option parsing was rewritten in Linux 3.4.
1848        // Must use backslashes.
1849        // If /dir1/dir2 is present, also add "prefixpath=dir1/dir2"
1850        {
1851            char *unc = xasprintf(
1852                share[len] != '\0'  /* "/dir1/dir2" exists? */
1853                    ? "unc=\\\\%s\\%.*s,prefixpath=%s"
1854                    : "unc=\\\\%s\\%.*s",
1855                hostname,
1856                len, share,
1857                share + len + 1  /* "dir1/dir2" */
1858            );
1859            parse_mount_options(unc, &filteropts);
1860            if (ENABLE_FEATURE_CLEAN_UP) free(unc);
1861        }
1862
1863        lsa = host2sockaddr(hostname, 0);
1864        share[-1] = c;
1865        if (!lsa)
1866            goto report_error;
1867
1868        // Insert "ip=..." option into options
1869        dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1870        if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1871        ip = xasprintf("ip=%s", dotted);
1872        if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1873        parse_mount_options(ip, &filteropts);
1874        if (ENABLE_FEATURE_CLEAN_UP) free(ip);
1875
1876        mp->mnt_type = (char*)"cifs";
1877        rc = mount_it_now(mp, vfsflags, filteropts);
1878
1879        goto report_error;
1880    }
1881
1882    // Might this be an NFS filesystem?
1883    if ((!mp->mnt_type || strncmp(mp->mnt_type, "nfs", 3) == 0)
1884     && strchr(mp->mnt_fsname, ':') != NULL
1885    ) {
1886        if (!mp->mnt_type)
1887            mp->mnt_type = (char*)"nfs";
1888        rc = nfsmount(mp, vfsflags, filteropts);
1889        goto report_error;
1890    }
1891
1892    // Look at the file.  (Not found isn't a failure for remount, or for
1893    // a synthetic filesystem like proc or sysfs.)
1894    // (We use stat, not lstat, in order to allow
1895    // mount symlink_to_file_or_blkdev dir)
1896    if (!stat(mp->mnt_fsname, &st)
1897     && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1898    ) {
1899        // Do we need to allocate a loopback device for it?
1900        if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1901            loopFile = bb_simplify_path(mp->mnt_fsname);
1902            mp->mnt_fsname = NULL; // will receive malloced loop dev name
1903            if (set_loop(&mp->mnt_fsname, loopFile, 0, /*ro:*/ (vfsflags & MS_RDONLY)) < 0) {
1904                if (errno == EPERM || errno == EACCES)
1905                    bb_error_msg(bb_msg_perm_denied_are_you_root);
1906                else
1907                    bb_perror_msg("can't setup loop device");
1908                return errno;
1909            }
1910
1911        // Autodetect bind mounts
1912        } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1913            vfsflags |= MS_BIND;
1914    }
1915
1916    // If we know the fstype (or don't need to), jump straight
1917    // to the actual mount.
1918    if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
1919        char *next;
1920        for (;;) {
1921            next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
1922            if (next)
1923                *next = '\0';
1924            rc = mount_it_now(mp, vfsflags, filteropts);
1925            if (rc == 0 || !next)
1926                break;
1927            mp->mnt_type = next + 1;
1928        }
1929    } else {
1930        // Loop through filesystem types until mount succeeds
1931        // or we run out
1932
1933        // Initialize list of block backed filesystems.
1934        // This has to be done here so that during "mount -a",
1935        // mounts after /proc shows up can autodetect.
1936        if (!fslist) {
1937            fslist = get_block_backed_filesystems();
1938            if (ENABLE_FEATURE_CLEAN_UP && fslist)
1939                atexit(delete_block_backed_filesystems);
1940        }
1941
1942        for (fl = fslist; fl; fl = fl->link) {
1943            mp->mnt_type = fl->data;
1944            rc = mount_it_now(mp, vfsflags, filteropts);
1945            if (rc == 0)
1946                break;
1947        }
1948    }
1949
1950    // If mount failed, clean up loop file (if any).
1951    if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1952        del_loop(mp->mnt_fsname);
1953        if (ENABLE_FEATURE_CLEAN_UP) {
1954            free(loopFile);
1955            free(mp->mnt_fsname);
1956        }
1957    }
1958
1959 report_error:
1960    if (ENABLE_FEATURE_CLEAN_UP)
1961        free(filteropts);
1962
1963    if (errno == EBUSY && ignore_busy)
1964        return 0;
1965    if (rc != 0)
1966        bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1967    return rc;
1968}
1969
1970// -O support
1971//    -O interprets a list of filter options which select whether a mount
1972// point will be mounted: only mounts with options matching *all* filtering
1973// options will be selected.
1974//    By default each -O filter option must be present in the list of mount
1975// options, but if it is prefixed by "no" then it must be absent.
1976// For example,
1977//  -O a,nob,c  matches  -o a,c  but fails to match  -o a,b,c
1978//              (and also fails to match  -o a  because  -o c  is absent).
1979//
1980// It is different from -t in that each option is matched exactly; a leading
1981// "no" at the beginning of one option does not negate the rest.
1982static int match_opt(const char *fs_opt_in, const char *O_opt)
1983{
1984    if (!O_opt)
1985        return 1;
1986
1987    while (*O_opt) {
1988        const char *fs_opt = fs_opt_in;
1989        int O_len;
1990        int match;
1991
1992        // If option begins with "no" then treat as an inverted match:
1993        // matching is a failure
1994        match = 0;
1995        if (O_opt[0] == 'n' && O_opt[1] == 'o') {
1996            match = 1;
1997            O_opt += 2;
1998        }
1999        // Isolate the current O option
2000        O_len = strchrnul(O_opt, ',') - O_opt;
2001        // Check for a match against existing options
2002        while (1) {
2003            if (strncmp(fs_opt, O_opt, O_len) == 0
2004             && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
2005            ) {
2006                if (match)
2007                    return 0;  // "no" prefix, but option found
2008                match = 1;  // current O option found, go check next one
2009                break;
2010            }
2011            fs_opt = strchr(fs_opt, ',');
2012            if (!fs_opt)
2013                break;
2014            fs_opt++;
2015        }
2016        if (match == 0)
2017            return 0;     // match wanted but not found
2018        if (O_opt[O_len] == '\0') // end?
2019            break;
2020        // Step to the next O option
2021        O_opt += O_len + 1;
2022    }
2023    // If we get here then everything matched
2024    return 1;
2025}
2026
2027// Parse options, if necessary parse fstab/mtab, and call singlemount for
2028// each directory to be mounted.
2029int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
2030int mount_main(int argc UNUSED_PARAM, char **argv)
2031{
2032    char *cmdopts = xzalloc(1);
2033    char *fstype = NULL;
2034    char *O_optmatch = NULL;
2035    char *storage_path;
2036    llist_t *lst_o = NULL;
2037    const char *fstabname;
2038    FILE *fstab;
2039    int i, j;
2040    int rc = EXIT_SUCCESS;
2041    unsigned long cmdopt_flags;
2042    unsigned opt;
2043    struct mntent mtpair[2], *mtcur = mtpair;
2044    IF_NOT_DESKTOP(const int nonroot = 0;)
2045
2046    IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
2047
2048    INIT_G();
2049
2050    // Parse long options, like --bind and --move.  Note that -o option
2051    // and --option are synonymous.  Yes, this means --remount,rw works.
2052    for (i = j = 1; argv[i]; i++) {
2053        if (argv[i][0] == '-' && argv[i][1] == '-')
2054            append_mount_options(&cmdopts, argv[i] + 2);
2055        else
2056            argv[j++] = argv[i];
2057    }
2058    argv[j] = NULL;
2059
2060    // Parse remaining options
2061    // Max 2 params; -o is a list, -v is a counter
2062    opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
2063    opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
2064            IF_FEATURE_MOUNT_VERBOSE(, &verbose));
2065    while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
2066    if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2067    if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
2068    argv += optind;
2069
2070    // If we have no arguments, show currently mounted filesystems
2071    if (!argv[0]) {
2072        if (!(opt & OPT_a)) {
2073            FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2074
2075            if (!mountTable)
2076                bb_error_msg_and_die("no %s", bb_path_mtab_file);
2077
2078            while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
2079                                GETMNTENT_BUFSIZE))
2080            {
2081                // Don't show rootfs. FIXME: why??
2082                // util-linux 2.12a happily shows rootfs...
2083                //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
2084
2085                if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
2086                    printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2087                            mtpair->mnt_dir, mtpair->mnt_type,
2088                            mtpair->mnt_opts);
2089            }
2090            if (ENABLE_FEATURE_CLEAN_UP)
2091                endmntent(mountTable);
2092            return EXIT_SUCCESS;
2093        }
2094        storage_path = NULL;
2095    } else {
2096        // When we have two arguments, the second is the directory and we can
2097        // skip looking at fstab entirely.  We can always abspath() the directory
2098        // argument when we get it.
2099        if (argv[1]) {
2100            if (nonroot)
2101                bb_error_msg_and_die(bb_msg_you_must_be_root);
2102            mtpair->mnt_fsname = argv[0];
2103            mtpair->mnt_dir = argv[1];
2104            mtpair->mnt_type = fstype;
2105            mtpair->mnt_opts = cmdopts;
2106            resolve_mount_spec(&mtpair->mnt_fsname);
2107            rc = singlemount(mtpair, /*ignore_busy:*/ 0);
2108            return rc;
2109        }
2110        storage_path = bb_simplify_path(argv[0]); // malloced
2111    }
2112
2113    // Past this point, we are handling either "mount -a [opts]"
2114    // or "mount [opts] single_param"
2115
2116    cmdopt_flags = parse_mount_options(cmdopts, NULL);
2117    if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
2118        bb_error_msg_and_die(bb_msg_you_must_be_root);
2119
2120    // If we have a shared subtree flag, don't worry about fstab or mtab.
2121    if (ENABLE_FEATURE_MOUNT_FLAGS
2122     && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
2123    ) {
2124        // verbose_mount(source, target, type, flags, data)
2125        rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
2126        if (rc)
2127            bb_simple_perror_msg_and_die(argv[0]);
2128        return rc;
2129    }
2130
2131    // Open either fstab or mtab
2132    fstabname = "/etc/fstab";
2133    if (cmdopt_flags & MS_REMOUNT) {
2134        // WARNING. I am not sure this matches util-linux's
2135        // behavior. It's possible util-linux does not
2136        // take -o opts from mtab (takes only mount source).
2137        fstabname = bb_path_mtab_file;
2138    }
2139    fstab = setmntent(fstabname, "r");
2140    if (!fstab)
2141        bb_perror_msg_and_die("can't read '%s'", fstabname);
2142
2143    // Loop through entries until we find what we're looking for
2144    memset(mtpair, 0, sizeof(mtpair));
2145    for (;;) {
2146        struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
2147
2148        // Get next fstab entry
2149        if (!getmntent_r(fstab, mtcur, getmntent_buf
2150                    + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
2151                GETMNTENT_BUFSIZE/2)
2152        ) { // End of fstab/mtab is reached
2153            mtcur = mtother; // the thing we found last time
2154            break;
2155        }
2156
2157        // If we're trying to mount something specific and this isn't it,
2158        // skip it.  Note we must match the exact text in fstab (ala
2159        // "proc") or a full path from root
2160        if (argv[0]) {
2161
2162            // Is this what we're looking for?
2163            if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2164             && strcmp(storage_path, mtcur->mnt_fsname) != 0
2165             && strcmp(argv[0], mtcur->mnt_dir) != 0
2166             && strcmp(storage_path, mtcur->mnt_dir) != 0
2167            ) {
2168                continue; // no
2169            }
2170
2171            // Remember this entry.  Something later may have
2172            // overmounted it, and we want the _last_ match.
2173            mtcur = mtother;
2174
2175        // If we're mounting all
2176        } else {
2177            struct mntent *mp;
2178            // No, mount -a won't mount anything,
2179            // even user mounts, for mere humans
2180            if (nonroot)
2181                bb_error_msg_and_die(bb_msg_you_must_be_root);
2182
2183            // Does type match? (NULL matches always)
2184            if (!match_fstype(mtcur, fstype))
2185                continue;
2186
2187            // Skip noauto and swap anyway
2188            if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2189            // swap is bogus "fstype", parse_mount_options can't check fstypes
2190             || strcasecmp(mtcur->mnt_type, "swap") == 0
2191            ) {
2192                continue;
2193            }
2194
2195            // Does (at least one) option match?
2196            // (NULL matches always)
2197            if (!match_opt(mtcur->mnt_opts, O_optmatch))
2198                continue;
2199
2200            resolve_mount_spec(&mtcur->mnt_fsname);
2201
2202            // NFS mounts want this to be xrealloc-able
2203            mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2204
2205            // If nothing is mounted on this directory...
2206            // (otherwise repeated "mount -a" mounts everything again)
2207            mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2208            // We do not check fsname match of found mount point -
2209            // "/" may have fsname of "/dev/root" while fstab
2210            // says "/dev/something_else".
2211            if (mp) {
2212                if (verbose) {
2213                    bb_error_msg("according to %s, "
2214                        "%s is already mounted on %s",
2215                        bb_path_mtab_file,
2216                        mp->mnt_fsname, mp->mnt_dir);
2217                }
2218            } else {
2219                // ...mount this thing
2220                if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2221                    // Count number of failed mounts
2222                    rc++;
2223                }
2224            }
2225            free(mtcur->mnt_opts);
2226        }
2227    }
2228
2229    // End of fstab/mtab is reached.
2230    // Were we looking for something specific?
2231    if (argv[0]) { // yes
2232        unsigned long l;
2233
2234        // If we didn't find anything, complain
2235        if (!mtcur->mnt_fsname)
2236            bb_error_msg_and_die("can't find %s in %s",
2237                argv[0], fstabname);
2238
2239        // What happens when we try to "mount swap_partition"?
2240        // (fstab containts "swap_partition swap swap defaults 0 0")
2241        // util-linux-ng 2.13.1 does this:
2242        // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2243        // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2244        // lstat("swap", 0x7fff62a3a640)           = -1 ENOENT (No such file or directory)
2245        // write(2, "mount: mount point swap does not exist\n", 39) = 39
2246        // exit_group(32)                          = ?
2247#if 0
2248        // In case we want to simply skip swap partitions:
2249        l = parse_mount_options(mtcur->mnt_opts, NULL);
2250        if ((l & MOUNT_SWAP)
2251        // swap is bogus "fstype", parse_mount_options can't check fstypes
2252         || strcasecmp(mtcur->mnt_type, "swap") == 0
2253        ) {
2254            goto ret;
2255        }
2256#endif
2257        if (nonroot) {
2258            // fstab must have "users" or "user"
2259            l = parse_mount_options(mtcur->mnt_opts, NULL);
2260            if (!(l & MOUNT_USERS))
2261                bb_error_msg_and_die(bb_msg_you_must_be_root);
2262        }
2263
2264        //util-linux-2.12 does not do this check.
2265        //// If nothing is mounted on this directory...
2266        //// (otherwise repeated "mount FOO" mounts FOO again)
2267        //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2268        //if (mp) {
2269        //  bb_error_msg("according to %s, "
2270        //      "%s is already mounted on %s",
2271        //      bb_path_mtab_file,
2272        //      mp->mnt_fsname, mp->mnt_dir);
2273        //} else {
2274            // ...mount the last thing we found
2275            mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2276            append_mount_options(&(mtcur->mnt_opts), cmdopts);
2277            resolve_mount_spec(&mtpair->mnt_fsname);
2278            rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2279            if (ENABLE_FEATURE_CLEAN_UP)
2280                free(mtcur->mnt_opts);
2281        //}
2282    }
2283
2284 //ret:
2285    if (ENABLE_FEATURE_CLEAN_UP)
2286        endmntent(fstab);
2287    if (ENABLE_FEATURE_CLEAN_UP) {
2288        free(storage_path);
2289        free(cmdopts);
2290    }
2291
2292//TODO: exitcode should be ORed mask of (from "man mount"):
2293// 0 success
2294// 1 incorrect invocation or permissions
2295// 2 system error (out of memory, cannot fork, no more loop devices)
2296// 4 internal mount bug or missing nfs support in mount
2297// 8 user interrupt
2298//16 problems writing or locking /etc/mtab
2299//32 mount failure
2300//64 some mount succeeded
2301    return rc;
2302}
Note: See TracBrowser for help on using the repository browser.