freeramdisk: make NOEXEC
[oweals/busybox.git] / util-linux / mount.c
index fbc89c862f3f7a6fe6778f6a95c0cf424007a3d6..4d5c2243a0e9acbba2974cf89b8f59375d408446 100644 (file)
 //
 
 //config:config MOUNT
-//config:      bool "mount"
+//config:      bool "mount (30 kb)"
 //config:      default y
 //config:      select PLATFORM_LINUX
 //config:      help
-//config:        All files and filesystems in Unix are arranged into one big directory
-//config:        tree. The 'mount' utility is used to graft a filesystem onto a
-//config:        particular part of the tree. A filesystem can either live on a block
-//config:        device, or it can be accessible over the network, as is the case with
-//config:        NFS filesystems. Most people using BusyBox will also want to enable
-//config:        the 'mount' utility.
+//config:      All files and filesystems in Unix are arranged into one big directory
+//config:      tree. The 'mount' utility is used to graft a filesystem onto a
+//config:      particular part of the tree. A filesystem can either live on a block
+//config:      device, or it can be accessible over the network, as is the case with
+//config:      NFS filesystems.
 //config:
 //config:config FEATURE_MOUNT_FAKE
-//config:      bool "Support option -f"
+//config:      bool "Support -f (fake mount)"
 //config:      default y
 //config:      depends on MOUNT
 //config:      help
-//config:        Enable support for faking a file system mount.
+//config:      Enable support for faking a file system mount.
 //config:
 //config:config FEATURE_MOUNT_VERBOSE
-//config:      bool "Support option -v"
+//config:      bool "Support -v (verbose)"
 //config:      default y
 //config:      depends on MOUNT
 //config:      help
-//config:        Enable multi-level -v[vv...] verbose messages. Useful if you
-//config:        debug mount problems and want to see what is exactly passed
-//config:        to the kernel.
+//config:      Enable multi-level -v[vv...] verbose messages. Useful if you
+//config:      debug mount problems and want to see what is exactly passed
+//config:      to the kernel.
 //config:
 //config:config FEATURE_MOUNT_HELPERS
 //config:      bool "Support mount helpers"
 //config:      default n
 //config:      depends on MOUNT
 //config:      help
-//config:        Enable mounting of virtual file systems via external helpers.
-//config:        E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call
-//config:        "obexfs -b00.11.22.33.44.55 /mnt"
-//config:        Also "mount -t sometype [-o opts] fs /mnt" will try
-//config:        "sometype [-o opts] fs /mnt" if simple mount syscall fails.
-//config:        The idea is to use such virtual filesystems in /etc/fstab.
+//config:      Enable mounting of virtual file systems via external helpers.
+//config:      E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call
+//config:      "obexfs -b00.11.22.33.44.55 /mnt"
+//config:      Also "mount -t sometype [-o opts] fs /mnt" will try
+//config:      "sometype [-o opts] fs /mnt" if simple mount syscall fails.
+//config:      The idea is to use such virtual filesystems in /etc/fstab.
 //config:
 //config:config FEATURE_MOUNT_LABEL
 //config:      bool "Support specifying devices by label or UUID"
@@ -63,9 +62,8 @@
 //config:      depends on MOUNT
 //config:      select VOLUMEID
 //config:      help
-//config:        This allows for specifying a device by label or uuid, rather than by
-//config:        name. This feature utilizes the same functionality as blkid/findfs.
-//config:        This also enables label or uuid support for swapon.
+//config:      This allows for specifying a device by label or uuid, rather than by
+//config:      name. This feature utilizes the same functionality as blkid/findfs.
 //config:
 //config:config FEATURE_MOUNT_NFS
 //config:      bool "Support mounting NFS file systems on Linux < 2.6.23"
 //config:      select FEATURE_HAVE_RPC
 //config:      select FEATURE_SYSLOG
 //config:      help
-//config:        Enable mounting of NFS file systems on Linux kernels prior
-//config:        to version 2.6.23. Note that in this case mounting of NFS
-//config:        over IPv6 will not be possible.
+//config:      Enable mounting of NFS file systems on Linux kernels prior
+//config:      to version 2.6.23. Note that in this case mounting of NFS
+//config:      over IPv6 will not be possible.
 //config:
-//config:        Note that this option links in RPC support from libc,
-//config:        which is rather large (~10 kbytes on uclibc).
+//config:      Note that this option links in RPC support from libc,
+//config:      which is rather large (~10 kbytes on uclibc).
 //config:
 //config:config FEATURE_MOUNT_CIFS
 //config:      bool "Support mounting CIFS/SMB file systems"
 //config:      default y
 //config:      depends on MOUNT
 //config:      help
-//config:        Enable support for samba mounts.
+//config:      Enable support for samba mounts.
 //config:
 //config:config FEATURE_MOUNT_FLAGS
 //config:      depends on MOUNT
-//config:      bool "Support lots of -o flags in mount"
+//config:      bool "Support lots of -o flags"
 //config:      default y
 //config:      help
-//config:        Without this, mount only supports ro/rw/remount. With this, it
-//config:        supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime,
-//config:        noatime, diratime, nodiratime, loud, bind, move, shared, slave,
-//config:        private, unbindable, rshared, rslave, rprivate, and runbindable.
+//config:      Without this, mount only supports ro/rw/remount. With this, it
+//config:      supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime,
+//config:      noatime, diratime, nodiratime, loud, bind, move, shared, slave,
+//config:      private, unbindable, rshared, rslave, rprivate, and runbindable.
 //config:
 //config:config FEATURE_MOUNT_FSTAB
 //config:      depends on MOUNT
-//config:      bool "Support /etc/fstab and -a"
+//config:      bool "Support /etc/fstab and -a (mount all)"
 //config:      default y
 //config:      help
-//config:        Support mount all and looking for files in /etc/fstab.
+//config:      Support mount all and looking for files in /etc/fstab.
 //config:
 //config:config FEATURE_MOUNT_OTHERTAB
 //config:      depends on FEATURE_MOUNT_FSTAB
 //config:      bool "Support -T <alt_fstab>"
 //config:      default y
 //config:      help
-//config:        Support mount -T (specifying an alternate fstab)
+//config:      Support mount -T (specifying an alternate fstab)
+
+/* On full-blown systems, requires suid for user mounts.
+ * But it's not unthinkable to have it available in non-suid flavor on some systems,
+ * for viewing mount table.
+ * Therefore we use BB_SUID_MAYBE instead of BB_SUID_REQUIRE: */
+//applet:IF_MOUNT(APPLET(mount, BB_DIR_BIN, IF_DESKTOP(BB_SUID_MAYBE) IF_NOT_DESKTOP(BB_SUID_DROP)))
+
+//kbuild:lib-$(CONFIG_MOUNT) += mount.o
 
 //usage:#define mount_trivial_usage
 //usage:       "[OPTIONS] [-o OPT] DEVICE NODE"
 #define BB_MS_INVERTED_VALUE (1u << 31)
 
 #include "libbb.h"
+#include "common_bufsiz.h"
 #if ENABLE_FEATURE_MOUNT_LABEL
 # include "volume_id.h"
 #else
 /* This is just a warning of a common mistake.  Possibly this should be a
  * uclibc faq entry rather than in busybox... */
 # if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
-#  error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
+#  warning "You probably need to build uClibc with UCLIBC_HAS_RPC for NFS support"
+   /* not #error, since user may be using e.g. libtirpc instead */
 # endif
 # include <rpc/rpc.h>
 # include <rpc/pmap_prot.h>
@@ -259,13 +267,15 @@ static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
 
 // Not real flags, but we want to be able to check for this.
 enum {
-       MOUNT_USERS  = (1 << 28) * ENABLE_DESKTOP,
+       MOUNT_USERS  = (1 << 27) * ENABLE_DESKTOP,
+       MOUNT_NOFAIL = (1 << 28) * ENABLE_DESKTOP,
        MOUNT_NOAUTO = (1 << 29),
        MOUNT_SWAP   = (1 << 30),
+       MOUNT_FAKEFLAGS = MOUNT_USERS | MOUNT_NOFAIL | MOUNT_NOAUTO | MOUNT_SWAP
 };
 
 
-#define OPTION_STR "o:t:rwanfvsiO:" IF_FEATURE_MOUNT_OTHERTAB("T:")
+#define OPTION_STR "o:*t:rwanfvsiO:" IF_FEATURE_MOUNT_OTHERTAB("T:")
 enum {
        OPT_o = (1 << 0),
        OPT_t = (1 << 1),
@@ -326,6 +336,7 @@ static const int32_t mount_options[] = {
                /* "swap"   */ MOUNT_SWAP,
                IF_DESKTOP(/* "user"  */ MOUNT_USERS,)
                IF_DESKTOP(/* "users" */ MOUNT_USERS,)
+               IF_DESKTOP(/* "nofail" */ MOUNT_NOFAIL,)
                /* "_netdev" */ 0,
                IF_DESKTOP(/* "comment=" */ 0,) /* systemd uses this in fstab */
        )
@@ -373,7 +384,7 @@ static const int32_t mount_options[] = {
        /* "remount" */ MS_REMOUNT  // action flag
 };
 
-static const char mount_option_str[] =
+static const char mount_option_str[] ALIGN1 =
        IF_FEATURE_MOUNT_LOOP(
                "loop\0"
        )
@@ -385,6 +396,7 @@ static const char mount_option_str[] =
                "swap\0"
                IF_DESKTOP("user\0")
                IF_DESKTOP("users\0")
+               IF_DESKTOP("nofail\0")
                "_netdev\0"
                IF_DESKTOP("comment=\0") /* systemd uses this in fstab */
        )
@@ -443,7 +455,7 @@ struct globals {
        char getmntent_buf[1];
 } FIX_ALIASING;
 enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
 #define nfs_mount_version (G.nfs_mount_version)
 #if ENABLE_FEATURE_MOUNT_VERBOSE
 #define verbose           (G.verbose          )
@@ -452,7 +464,7 @@ enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_b
 #endif
 #define fslist            (G.fslist           )
 #define getmntent_buf     (G.getmntent_buf    )
-#define INIT_G() do { } while (0)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
 
 #if ENABLE_FEATURE_MTAB_SUPPORT
 /*
@@ -641,7 +653,7 @@ static llist_t *get_block_backed_filesystems(void)
                if (!f) continue;
 
                while ((buf = xmalloc_fgetline(f)) != NULL) {
-                       if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
+                       if (is_prefixed_with(buf, "nodev") && isspace(buf[5]))
                                goto next;
                        fs = skip_whitespace(buf);
                        if (*fs == '#' || *fs == '*' || !*fs)
@@ -672,6 +684,8 @@ static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filtero
 {
        int rc = 0;
 
+       vfsflags &= ~(unsigned long)MOUNT_FAKEFLAGS;
+
        if (FAKE_IT) {
                if (verbose >= 2)
                        bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
@@ -996,7 +1010,7 @@ enum {
 # define EDQUOT ENOSPC
 #endif
 /* Convert each NFSERR_BLAH into EBLAH */
-static const uint8_t nfs_err_stat[] = {
+static const uint8_t nfs_err_stat[] ALIGN1 = {
         1,  2,  5,  6, 13, 17,
        19, 20, 21, 22, 27, 28,
        30, 63, 66, 69, 70, 71
@@ -1009,7 +1023,7 @@ typedef uint8_t nfs_err_type;
 #else
 typedef uint16_t nfs_err_type;
 #endif
-static const nfs_err_type nfs_err_errnum[] = {
+static const nfs_err_type nfs_err_errnum[] ALIGN2 = {
        EPERM , ENOENT      , EIO      , ENXIO , EACCES, EEXIST,
        ENODEV, ENOTDIR     , EISDIR   , EINVAL, EFBIG , ENOSPC,
        EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
@@ -1364,9 +1378,9 @@ static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *fi
                                                strcspn(opteq, " \t\n\r,"));
                                continue;
                        case 18: // "proto"
-                               if (!strncmp(opteq, "tcp", 3))
+                               if (is_prefixed_with(opteq, "tcp"))
                                        tcp = 1;
-                               else if (!strncmp(opteq, "udp", 3))
+                               else if (is_prefixed_with(opteq, "udp"))
                                        tcp = 0;
                                else
                                        bb_error_msg("warning: unrecognized proto= option");
@@ -1459,7 +1473,7 @@ static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *fi
                                "rdirplus\0"
                                "acl\0";
                        int val = 1;
-                       if (!strncmp(opt, "no", 2)) {
+                       if (is_prefixed_with(opt, "no")) {
                                val = 0;
                                opt += 2;
                        }
@@ -1872,6 +1886,7 @@ static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
 // NB: mp->xxx fields may be trashed on exit
 static int singlemount(struct mntent *mp, int ignore_busy)
 {
+       int loopfd = -1;
        int rc = -1;
        unsigned long vfsflags;
        char *loopFile = NULL, *filteropts = NULL;
@@ -1924,7 +1939,6 @@ static int singlemount(struct mntent *mp, int ignore_busy)
                int len;
                char c;
                char *hostname, *share;
-               char *dotted, *ip;
                len_and_sockaddr *lsa;
 
                // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
@@ -1964,13 +1978,26 @@ static int singlemount(struct mntent *mp, int ignore_busy)
                if (!lsa)
                        goto report_error;
 
-               // Insert "ip=..." option into options
-               dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
-               if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
-               ip = xasprintf("ip=%s", dotted);
-               if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
-               parse_mount_options(ip, &filteropts);
-               if (ENABLE_FEATURE_CLEAN_UP) free(ip);
+               // If there is no "ip=..." option yet
+               if (!is_prefixed_with(filteropts, ",ip="+1)
+                && !strstr(filteropts, ",ip=")
+               ) {
+                       char *dotted, *ip;
+                       // Insert "ip=..." option into options
+                       dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
+                       if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
+                       ip = xasprintf("ip=%s", dotted);
+                       if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
+// Note: IPv6 scoped addresses ("host%iface", see RFC 4007) should be
+// handled by libc in getnameinfo() (inside xmalloc_sockaddr2dotted_noport()).
+// Currently, glibc does not support that (has no NI_NUMERICSCOPE),
+// musl apparently does. This results in "ip=numericIPv6%iface_name"
+// (instead of _numeric_ iface_id) with glibc.
+// This probably should be fixed in glibc, not here.
+// The workaround is to manually specify correct "ip=ADDR%n" option.
+                       parse_mount_options(ip, &filteropts);
+                       if (ENABLE_FEATURE_CLEAN_UP) free(ip);
+               }
 
                mp->mnt_type = (char*)"cifs";
                rc = mount_it_now(mp, vfsflags, filteropts);
@@ -1979,7 +2006,7 @@ static int singlemount(struct mntent *mp, int ignore_busy)
        }
 
        // Might this be an NFS filesystem?
-       if ((!mp->mnt_type || strncmp(mp->mnt_type, "nfs", 3) == 0)
+       if ((!mp->mnt_type || is_prefixed_with(mp->mnt_type, "nfs"))
         && strchr(mp->mnt_fsname, ':') != NULL
        ) {
                if (!mp->mnt_type)
@@ -1999,7 +2026,20 @@ static int singlemount(struct mntent *mp, int ignore_busy)
                if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
                        loopFile = bb_simplify_path(mp->mnt_fsname);
                        mp->mnt_fsname = NULL; // will receive malloced loop dev name
-                       if (set_loop(&mp->mnt_fsname, loopFile, 0, /*ro:*/ (vfsflags & MS_RDONLY)) < 0) {
+
+                       // mount always creates AUTOCLEARed loopdevs, so that umounting
+                       // drops them without any code in the userspace.
+                       // This happens since circa linux-2.6.25:
+                       // commit 96c5865559cee0f9cbc5173f3c949f6ce3525581
+                       // Date:    Wed Feb 6 01:36:27 2008 -0800
+                       // Subject: Allow auto-destruction of loop devices
+                       loopfd = set_loop(&mp->mnt_fsname,
+                                       loopFile,
+                                       0,
+                                       ((vfsflags & MS_RDONLY) ? BB_LO_FLAGS_READ_ONLY : 0)
+                                               | BB_LO_FLAGS_AUTOCLEAR
+                       );
+                       if (loopfd < 0) {
                                if (errno == EPERM || errno == EACCES)
                                        bb_error_msg(bb_msg_perm_denied_are_you_root);
                                else
@@ -2047,11 +2087,13 @@ static int singlemount(struct mntent *mp, int ignore_busy)
        }
 
        // If mount failed, clean up loop file (if any).
+       // (Newer kernels which support LO_FLAGS_AUTOCLEAR should not need this,
+       // merely "close(loopfd)" should do it?)
        if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
                del_loop(mp->mnt_fsname);
                if (ENABLE_FEATURE_CLEAN_UP) {
                        free(loopFile);
-                       free(mp->mnt_fsname);
+                       /* No, "rc != 0" needs it: free(mp->mnt_fsname); */
                }
        }
 
@@ -2059,8 +2101,13 @@ static int singlemount(struct mntent *mp, int ignore_busy)
        if (ENABLE_FEATURE_CLEAN_UP)
                free(filteropts);
 
+       if (loopfd >= 0)
+               close(loopfd);
+
        if (errno == EBUSY && ignore_busy)
                return 0;
+       if (errno == ENOENT && (vfsflags & MOUNT_NOFAIL))
+               return 0;
        if (rc != 0)
                bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
        return rc;
@@ -2158,7 +2205,7 @@ int mount_main(int argc UNUSED_PARAM, char **argv)
 
        // Parse remaining options
        // Max 2 params; -o is a list, -v is a counter
-       opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
+       opt_complementary = "?2" IF_FEATURE_MOUNT_VERBOSE("vv");
        opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
                        IF_FEATURE_MOUNT_OTHERTAB(, &fstabname)
                        IF_FEATURE_MOUNT_VERBOSE(, &verbose));
@@ -2283,7 +2330,7 @@ int mount_main(int argc UNUSED_PARAM, char **argv)
                                bb_error_msg_and_die(bb_msg_you_must_be_root);
 
                        // Does type match? (NULL matches always)
-                       if (!match_fstype(mtcur, fstype))
+                       if (!fstype_matches(mtcur->mnt_type, fstype))
                                continue;
 
                        // Skip noauto and swap anyway