blob: 4eade0869d561316121bd788513f8724f555fa93 [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Eric Andersencc8ed391999-10-05 16:24:54 +00002/*
Eric Andersen596e5461999-10-07 08:30:23 +00003 * Mini mount implementation for busybox
4 *
Eric Andersenc4996011999-10-20 22:08:37 +00005 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
Eric Andersenc7bda1c2004-03-15 08:29:22 +00006 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
Rob Landleydc0955b2006-03-14 18:16:25 +00007 * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net>
Eric Andersen596e5461999-10-07 08:30:23 +00008 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02009 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Erik Andersenb7cc49d2000-01-13 06:38:14 +000010 */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000011// 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//
Pere Orga5bc8c002011-04-11 03:29:49 +020019
Isaac Dunham7b434a62015-03-11 16:07:24 +010020//config:config MOUNT
Denys Vlasenko4eed2c62017-07-18 22:01:24 +020021//config: bool "mount (30 kb)"
Isaac Dunham7b434a62015-03-11 16:07:24 +010022//config: default y
23//config: select PLATFORM_LINUX
24//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020025//config: All files and filesystems in Unix are arranged into one big directory
26//config: tree. The 'mount' utility is used to graft a filesystem onto a
27//config: particular part of the tree. A filesystem can either live on a block
28//config: device, or it can be accessible over the network, as is the case with
Denys Vlasenko68b653b2017-07-27 10:53:09 +020029//config: NFS filesystems.
Isaac Dunham7b434a62015-03-11 16:07:24 +010030//config:
31//config:config FEATURE_MOUNT_FAKE
Denys Vlasenko5b3cbe32017-07-27 14:45:25 +020032//config: bool "Support -f (fake mount)"
Isaac Dunham7b434a62015-03-11 16:07:24 +010033//config: default y
34//config: depends on MOUNT
35//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020036//config: Enable support for faking a file system mount.
Isaac Dunham7b434a62015-03-11 16:07:24 +010037//config:
38//config:config FEATURE_MOUNT_VERBOSE
Denys Vlasenko5b3cbe32017-07-27 14:45:25 +020039//config: bool "Support -v (verbose)"
Isaac Dunham7b434a62015-03-11 16:07:24 +010040//config: default y
41//config: depends on MOUNT
42//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020043//config: Enable multi-level -v[vv...] verbose messages. Useful if you
44//config: debug mount problems and want to see what is exactly passed
45//config: to the kernel.
Isaac Dunham7b434a62015-03-11 16:07:24 +010046//config:
47//config:config FEATURE_MOUNT_HELPERS
48//config: bool "Support mount helpers"
49//config: default n
50//config: depends on MOUNT
51//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020052//config: Enable mounting of virtual file systems via external helpers.
53//config: E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call
54//config: "obexfs -b00.11.22.33.44.55 /mnt"
55//config: Also "mount -t sometype [-o opts] fs /mnt" will try
56//config: "sometype [-o opts] fs /mnt" if simple mount syscall fails.
57//config: The idea is to use such virtual filesystems in /etc/fstab.
Isaac Dunham7b434a62015-03-11 16:07:24 +010058//config:
59//config:config FEATURE_MOUNT_LABEL
60//config: bool "Support specifying devices by label or UUID"
61//config: default y
62//config: depends on MOUNT
63//config: select VOLUMEID
64//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020065//config: This allows for specifying a device by label or uuid, rather than by
66//config: name. This feature utilizes the same functionality as blkid/findfs.
Isaac Dunham7b434a62015-03-11 16:07:24 +010067//config:
68//config:config FEATURE_MOUNT_NFS
69//config: bool "Support mounting NFS file systems on Linux < 2.6.23"
70//config: default n
71//config: depends on MOUNT
Isaac Dunham7b434a62015-03-11 16:07:24 +010072//config: select FEATURE_SYSLOG
73//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020074//config: Enable mounting of NFS file systems on Linux kernels prior
75//config: to version 2.6.23. Note that in this case mounting of NFS
76//config: over IPv6 will not be possible.
Isaac Dunham7b434a62015-03-11 16:07:24 +010077//config:
Denys Vlasenko72089cf2017-07-21 09:50:55 +020078//config: Note that this option links in RPC support from libc,
79//config: which is rather large (~10 kbytes on uclibc).
Isaac Dunham7b434a62015-03-11 16:07:24 +010080//config:
81//config:config FEATURE_MOUNT_CIFS
82//config: bool "Support mounting CIFS/SMB file systems"
83//config: default y
84//config: depends on MOUNT
85//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020086//config: Enable support for samba mounts.
Isaac Dunham7b434a62015-03-11 16:07:24 +010087//config:
88//config:config FEATURE_MOUNT_FLAGS
89//config: depends on MOUNT
Denys Vlasenkof5604222017-01-10 14:58:54 +010090//config: bool "Support lots of -o flags"
Isaac Dunham7b434a62015-03-11 16:07:24 +010091//config: default y
92//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020093//config: Without this, mount only supports ro/rw/remount. With this, it
94//config: supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime,
95//config: noatime, diratime, nodiratime, loud, bind, move, shared, slave,
96//config: private, unbindable, rshared, rslave, rprivate, and runbindable.
Isaac Dunham7b434a62015-03-11 16:07:24 +010097//config:
98//config:config FEATURE_MOUNT_FSTAB
99//config: depends on MOUNT
Denys Vlasenko5b3cbe32017-07-27 14:45:25 +0200100//config: bool "Support /etc/fstab and -a (mount all)"
Isaac Dunham7b434a62015-03-11 16:07:24 +0100101//config: default y
102//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200103//config: Support mount all and looking for files in /etc/fstab.
Isaac Dunham7b434a62015-03-11 16:07:24 +0100104//config:
105//config:config FEATURE_MOUNT_OTHERTAB
106//config: depends on FEATURE_MOUNT_FSTAB
107//config: bool "Support -T <alt_fstab>"
108//config: default y
109//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200110//config: Support mount -T (specifying an alternate fstab)
Isaac Dunham7b434a62015-03-11 16:07:24 +0100111
Denys Vlasenkodd898c92016-11-23 11:46:32 +0100112/* On full-blown systems, requires suid for user mounts.
113 * But it's not unthinkable to have it available in non-suid flavor on some systems,
114 * for viewing mount table.
115 * Therefore we use BB_SUID_MAYBE instead of BB_SUID_REQUIRE: */
116//applet:IF_MOUNT(APPLET(mount, BB_DIR_BIN, IF_DESKTOP(BB_SUID_MAYBE) IF_NOT_DESKTOP(BB_SUID_DROP)))
117
118//kbuild:lib-$(CONFIG_MOUNT) += mount.o
119
Pere Orga5bc8c002011-04-11 03:29:49 +0200120//usage:#define mount_trivial_usage
Isaac Dunham7b434a62015-03-11 16:07:24 +0100121//usage: "[OPTIONS] [-o OPT] DEVICE NODE"
Pere Orga5bc8c002011-04-11 03:29:49 +0200122//usage:#define mount_full_usage "\n\n"
123//usage: "Mount a filesystem. Filesystem autodetection requires /proc.\n"
Pere Orga5bc8c002011-04-11 03:29:49 +0200124//usage: "\n -a Mount all filesystems in fstab"
125//usage: IF_FEATURE_MOUNT_FAKE(
126//usage: IF_FEATURE_MTAB_SUPPORT(
127//usage: "\n -f Update /etc/mtab, but don't mount"
128//usage: )
129//usage: IF_NOT_FEATURE_MTAB_SUPPORT(
130//usage: "\n -f Dry run"
131//usage: )
132//usage: )
133//usage: IF_FEATURE_MOUNT_HELPERS(
134//usage: "\n -i Don't run mount helper"
135//usage: )
136//usage: IF_FEATURE_MTAB_SUPPORT(
137//usage: "\n -n Don't update /etc/mtab"
138//usage: )
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100139//usage: IF_FEATURE_MOUNT_VERBOSE(
140//usage: "\n -v Verbose"
141//usage: )
142////usage: "\n -s Sloppy (ignored)"
Pere Orga5bc8c002011-04-11 03:29:49 +0200143//usage: "\n -r Read-only mount"
Isaac Dunham7b434a62015-03-11 16:07:24 +0100144////usage: "\n -w Read-write mount (default)"
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +0100145//usage: "\n -t FSTYPE[,...] Filesystem type(s)"
Isaac Dunham7b434a62015-03-11 16:07:24 +0100146//usage: IF_FEATURE_MOUNT_OTHERTAB(
147//usage: "\n -T FILE Read FILE instead of /etc/fstab"
148//usage: )
Pere Orga5bc8c002011-04-11 03:29:49 +0200149//usage: "\n -O OPT Mount only filesystems with option OPT (-a only)"
150//usage: "\n-o OPT:"
151//usage: IF_FEATURE_MOUNT_LOOP(
152//usage: "\n loop Ignored (loop devices are autodetected)"
153//usage: )
154//usage: IF_FEATURE_MOUNT_FLAGS(
155//usage: "\n [a]sync Writes are [a]synchronous"
156//usage: "\n [no]atime Disable/enable updates to inode access times"
157//usage: "\n [no]diratime Disable/enable atime updates to directories"
158//usage: "\n [no]relatime Disable/enable atime updates relative to modification time"
159//usage: "\n [no]dev (Dis)allow use of special device files"
160//usage: "\n [no]exec (Dis)allow use of executable files"
161//usage: "\n [no]suid (Dis)allow set-user-id-root programs"
162//usage: "\n [r]shared Convert [recursively] to a shared subtree"
163//usage: "\n [r]slave Convert [recursively] to a slave subtree"
164//usage: "\n [r]private Convert [recursively] to a private subtree"
165//usage: "\n [un]bindable Make mount point [un]able to be bind mounted"
166//usage: "\n [r]bind Bind a file or directory [recursively] to another location"
167//usage: "\n move Relocate an existing mount point"
168//usage: )
169//usage: "\n remount Remount a mounted filesystem, changing flags"
Isaac Dunham7b434a62015-03-11 16:07:24 +0100170//usage: "\n ro Same as -r"
Pere Orga5bc8c002011-04-11 03:29:49 +0200171//usage: "\n"
172//usage: "\nThere are filesystem-specific -o flags."
173//usage:
174//usage:#define mount_example_usage
175//usage: "$ mount\n"
176//usage: "/dev/hda3 on / type minix (rw)\n"
177//usage: "proc on /proc type proc (rw)\n"
178//usage: "devpts on /dev/pts type devpts (rw)\n"
179//usage: "$ mount /dev/fd0 /mnt -t msdos -o ro\n"
180//usage: "$ mount /tmp/diskimage /opt -t ext2 -o loop\n"
181//usage: "$ mount cd_image.iso mydir\n"
182//usage:#define mount_notes_usage
183//usage: "Returns 0 for success, number of failed mounts for -a, or errno for one mount."
184
Eric Andersencc8ed391999-10-05 16:24:54 +0000185#include <mntent.h>
Bernhard Reutner-Fischerf4701962008-01-27 12:50:12 +0000186#include <syslog.h>
Denys Vlasenkoda49f582009-07-08 02:58:38 +0200187#include <sys/mount.h>
Denys Vlasenko102ff762009-11-21 17:14:08 +0100188// Grab more as needed from util-linux's mount/mount_constants.h
189#ifndef MS_DIRSYNC
190# define MS_DIRSYNC (1 << 7) // Directory modifications are synchronous
191#endif
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200192#ifndef MS_UNION
193# define MS_UNION (1 << 8)
194#endif
Denys Vlasenkoda49f582009-07-08 02:58:38 +0200195#ifndef MS_BIND
196# define MS_BIND (1 << 12)
197#endif
198#ifndef MS_MOVE
199# define MS_MOVE (1 << 13)
200#endif
201#ifndef MS_RECURSIVE
202# define MS_RECURSIVE (1 << 14)
203#endif
204#ifndef MS_SILENT
205# define MS_SILENT (1 << 15)
206#endif
Denys Vlasenko102ff762009-11-21 17:14:08 +0100207// The shared subtree stuff, which went in around 2.6.15
Denys Vlasenkoda49f582009-07-08 02:58:38 +0200208#ifndef MS_UNBINDABLE
209# define MS_UNBINDABLE (1 << 17)
210#endif
211#ifndef MS_PRIVATE
212# define MS_PRIVATE (1 << 18)
213#endif
214#ifndef MS_SLAVE
215# define MS_SLAVE (1 << 19)
216#endif
217#ifndef MS_SHARED
218# define MS_SHARED (1 << 20)
219#endif
220#ifndef MS_RELATIME
221# define MS_RELATIME (1 << 21)
222#endif
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200223#ifndef MS_STRICTATIME
224# define MS_STRICTATIME (1 << 24)
225#endif
226
227/* Any ~MS_FOO value has this bit set: */
228#define BB_MS_INVERTED_VALUE (1u << 31)
Eric Andersenbd22ed82000-07-08 18:55:24 +0000229
Denys Vlasenko102ff762009-11-21 17:14:08 +0100230#include "libbb.h"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200231#include "common_bufsiz.h"
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000232#if ENABLE_FEATURE_MOUNT_LABEL
Natanael Copa9aff2992009-09-20 04:28:22 +0200233# include "volume_id.h"
234#else
235# define resolve_mount_spec(fsname) ((void)0)
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000236#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000237
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000238// Needed for nfs support only
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000239#include <sys/utsname.h>
240#undef TRUE
241#undef FALSE
Denys Vlasenkocc428142009-12-16 02:06:56 +0100242#if ENABLE_FEATURE_MOUNT_NFS
243/* This is just a warning of a common mistake. Possibly this should be a
244 * uclibc faq entry rather than in busybox... */
245# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
Denys Vlasenkoce552842017-07-10 14:43:22 +0200246# warning "You probably need to build uClibc with UCLIBC_HAS_RPC for NFS support"
247 /* not #error, since user may be using e.g. libtirpc instead */
Denys Vlasenkocc428142009-12-16 02:06:56 +0100248# endif
249# include <rpc/rpc.h>
250# include <rpc/pmap_prot.h>
251# include <rpc/pmap_clnt.h>
252#endif
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000253
254
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000255#if defined(__dietlibc__)
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000256// 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
257// dietlibc-0.30 does not have implementation of getmntent_r()
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +0000258static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000259 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000260{
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000261 struct mntent* ment = getmntent(stream);
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +0000262 return memcpy(result, ment, sizeof(*ment));
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000263}
264#endif
265
266
Rob Landleydc0955b2006-03-14 18:16:25 +0000267// Not real flags, but we want to be able to check for this.
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000268enum {
Denys Vlasenko911d2652015-12-30 20:11:34 +0100269 MOUNT_USERS = (1 << 27) * ENABLE_DESKTOP,
270 MOUNT_NOFAIL = (1 << 28) * ENABLE_DESKTOP,
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000271 MOUNT_NOAUTO = (1 << 29),
272 MOUNT_SWAP = (1 << 30),
Denys Vlasenko911d2652015-12-30 20:11:34 +0100273 MOUNT_FAKEFLAGS = MOUNT_USERS | MOUNT_NOFAIL | MOUNT_NOAUTO | MOUNT_SWAP
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000274};
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000275
276
Denys Vlasenko237bedd2016-07-06 21:58:02 +0200277#define OPTION_STR "o:*t:rwanfvsiO:" IF_FEATURE_MOUNT_OTHERTAB("T:")
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000278enum {
279 OPT_o = (1 << 0),
280 OPT_t = (1 << 1),
281 OPT_r = (1 << 2),
282 OPT_w = (1 << 3),
283 OPT_a = (1 << 4),
284 OPT_n = (1 << 5),
285 OPT_f = (1 << 6),
286 OPT_v = (1 << 7),
287 OPT_s = (1 << 8),
288 OPT_i = (1 << 9),
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +0000289 OPT_O = (1 << 10),
Isaac Dunham7b434a62015-03-11 16:07:24 +0100290 OPT_T = (1 << 11),
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000291};
292
293#if ENABLE_FEATURE_MTAB_SUPPORT
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200294#define USE_MTAB (!(option_mask32 & OPT_n))
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000295#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200296#define USE_MTAB 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000297#endif
298
299#if ENABLE_FEATURE_MOUNT_FAKE
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200300#define FAKE_IT (option_mask32 & OPT_f)
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000301#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200302#define FAKE_IT 0
303#endif
304
305#if ENABLE_FEATURE_MOUNT_HELPERS
306#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
307#else
308#define HELPERS_ALLOWED 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000309#endif
310
311
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000312// TODO: more "user" flag compatibility.
313// "user" option (from mount manpage):
314// Only the user that mounted a filesystem can unmount it again.
315// If any user should be able to unmount, then use users instead of user
316// in the fstab line. The owner option is similar to the user option,
317// with the restriction that the user must be the owner of the special file.
318// This may be useful e.g. for /dev/fd if a login script makes
319// the console user owner of this device.
Rob Landley3ba7bd12006-08-09 19:51:13 +0000320
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000321// Standard mount options (from -o options or --options),
322// with corresponding flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000323static const int32_t mount_options[] = {
Rob Landleye3781b72006-08-08 01:39:49 +0000324 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
Rob Landleydc0955b2006-03-14 18:16:25 +0000325
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000326 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000327 /* "loop" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000328 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000329
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000330 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000331 /* "defaults" */ 0,
332 /* "quiet" 0 - do not filter out, vfat wants to see it */
333 /* "noauto" */ MOUNT_NOAUTO,
334 /* "sw" */ MOUNT_SWAP,
335 /* "swap" */ MOUNT_SWAP,
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000336 IF_DESKTOP(/* "user" */ MOUNT_USERS,)
337 IF_DESKTOP(/* "users" */ MOUNT_USERS,)
Denys Vlasenko911d2652015-12-30 20:11:34 +0100338 IF_DESKTOP(/* "nofail" */ MOUNT_NOFAIL,)
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000339 /* "_netdev" */ 0,
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100340 IF_DESKTOP(/* "comment=" */ 0,) /* systemd uses this in fstab */
Rob Landleye3781b72006-08-08 01:39:49 +0000341 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000342
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000343 IF_FEATURE_MOUNT_FLAGS(
Rob Landleye3781b72006-08-08 01:39:49 +0000344 // vfs flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000345 /* "nosuid" */ MS_NOSUID,
346 /* "suid" */ ~MS_NOSUID,
347 /* "dev" */ ~MS_NODEV,
348 /* "nodev" */ MS_NODEV,
349 /* "exec" */ ~MS_NOEXEC,
350 /* "noexec" */ MS_NOEXEC,
351 /* "sync" */ MS_SYNCHRONOUS,
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +0000352 /* "dirsync" */ MS_DIRSYNC,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000353 /* "async" */ ~MS_SYNCHRONOUS,
354 /* "atime" */ ~MS_NOATIME,
355 /* "noatime" */ MS_NOATIME,
356 /* "diratime" */ ~MS_NODIRATIME,
357 /* "nodiratime" */ MS_NODIRATIME,
Denis Vlasenko580ce2d2008-07-08 02:56:53 +0000358 /* "mand" */ MS_MANDLOCK,
359 /* "nomand" */ ~MS_MANDLOCK,
Bernhard Reutner-Fischerfb5902c2008-08-06 18:14:38 +0000360 /* "relatime" */ MS_RELATIME,
361 /* "norelatime" */ ~MS_RELATIME,
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200362 /* "strictatime" */ MS_STRICTATIME,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000363 /* "loud" */ ~MS_SILENT,
Roman Borisov19311bf2011-03-24 15:08:43 +0300364 /* "rbind" */ MS_BIND|MS_RECURSIVE,
Eric Andersen9601a1c2006-03-20 18:07:50 +0000365
Rob Landleye3781b72006-08-08 01:39:49 +0000366 // action flags
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200367 /* "union" */ MS_UNION,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000368 /* "bind" */ MS_BIND,
369 /* "move" */ MS_MOVE,
370 /* "shared" */ MS_SHARED,
371 /* "slave" */ MS_SLAVE,
372 /* "private" */ MS_PRIVATE,
373 /* "unbindable" */ MS_UNBINDABLE,
374 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
375 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
Roman Borisovd3679d22011-03-23 11:20:25 +0300376 /* "rprivate" */ MS_PRIVATE|MS_RECURSIVE,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000377 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
Rob Landleye3781b72006-08-08 01:39:49 +0000378 )
379
380 // Always understood.
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000381 /* "ro" */ MS_RDONLY, // vfs flag
382 /* "rw" */ ~MS_RDONLY, // vfs flag
383 /* "remount" */ MS_REMOUNT // action flag
Eric Andersencc8ed391999-10-05 16:24:54 +0000384};
385
Denys Vlasenko3e134eb2016-04-22 18:09:21 +0200386static const char mount_option_str[] ALIGN1 =
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000387 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000388 "loop\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000389 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000390 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000391 "defaults\0"
392 // "quiet\0" - do not filter out, vfat wants to see it
393 "noauto\0"
394 "sw\0"
395 "swap\0"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000396 IF_DESKTOP("user\0")
397 IF_DESKTOP("users\0")
Denys Vlasenko911d2652015-12-30 20:11:34 +0100398 IF_DESKTOP("nofail\0")
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000399 "_netdev\0"
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100400 IF_DESKTOP("comment=\0") /* systemd uses this in fstab */
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000401 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000402 IF_FEATURE_MOUNT_FLAGS(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000403 // vfs flags
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000404 "nosuid\0"
405 "suid\0"
406 "dev\0"
407 "nodev\0"
408 "exec\0"
409 "noexec\0"
410 "sync\0"
411 "dirsync\0"
412 "async\0"
413 "atime\0"
414 "noatime\0"
415 "diratime\0"
416 "nodiratime\0"
417 "mand\0"
418 "nomand\0"
419 "relatime\0"
420 "norelatime\0"
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200421 "strictatime\0"
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000422 "loud\0"
Roman Borisov19311bf2011-03-24 15:08:43 +0300423 "rbind\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000424
425 // action flags
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200426 "union\0"
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000427 "bind\0"
428 "move\0"
Roman Borisov945fd172011-02-25 14:50:39 +0300429 "make-shared\0"
430 "make-slave\0"
431 "make-private\0"
432 "make-unbindable\0"
433 "make-rshared\0"
434 "make-rslave\0"
435 "make-rprivate\0"
436 "make-runbindable\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000437 )
438
439 // Always understood.
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000440 "ro\0" // vfs flag
441 "rw\0" // vfs flag
442 "remount\0" // action flag
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000443;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000444
Denis Vlasenkof732e962008-02-18 12:07:49 +0000445
446struct globals {
447#if ENABLE_FEATURE_MOUNT_NFS
448 smalluint nfs_mount_version;
449#endif
450#if ENABLE_FEATURE_MOUNT_VERBOSE
451 unsigned verbose;
452#endif
453 llist_t *fslist;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000454 char getmntent_buf[1];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100455} FIX_ALIASING;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000456enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200457#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenkof732e962008-02-18 12:07:49 +0000458#define nfs_mount_version (G.nfs_mount_version)
Denis Vlasenkob4133682008-02-18 13:05:38 +0000459#if ENABLE_FEATURE_MOUNT_VERBOSE
Denis Vlasenkof732e962008-02-18 12:07:49 +0000460#define verbose (G.verbose )
Denis Vlasenkob4133682008-02-18 13:05:38 +0000461#else
462#define verbose 0
463#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000464#define fslist (G.fslist )
465#define getmntent_buf (G.getmntent_buf )
Denys Vlasenko47cfbf32016-04-21 18:18:48 +0200466#define INIT_G() do { setup_common_bufsiz(); } while (0)
Denis Vlasenkof732e962008-02-18 12:07:49 +0000467
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100468#if ENABLE_FEATURE_MTAB_SUPPORT
469/*
470 * update_mtab_entry_on_move() is used to update entry in case of mount --move.
471 * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
472 * input mntent and replace it by new one.
473 */
474static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
475{
476 struct mntent *entries, *m;
477 int i, count;
478 FILE *mountTable;
479
480 mountTable = setmntent(bb_path_mtab_file, "r");
481 if (!mountTable) {
482 bb_perror_msg(bb_path_mtab_file);
483 return;
484 }
485
486 entries = NULL;
487 count = 0;
488 while ((m = getmntent(mountTable)) != NULL) {
489 entries = xrealloc_vector(entries, 3, count);
490 entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
491 entries[count].mnt_dir = xstrdup(m->mnt_dir);
492 entries[count].mnt_type = xstrdup(m->mnt_type);
493 entries[count].mnt_opts = xstrdup(m->mnt_opts);
494 entries[count].mnt_freq = m->mnt_freq;
495 entries[count].mnt_passno = m->mnt_passno;
496 count++;
497 }
498 endmntent(mountTable);
499
500 mountTable = setmntent(bb_path_mtab_file, "w");
501 if (mountTable) {
502 for (i = 0; i < count; i++) {
503 if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
504 addmntent(mountTable, &entries[i]);
505 else
506 addmntent(mountTable, mp);
507 }
508 endmntent(mountTable);
509 } else if (errno != EROFS)
510 bb_perror_msg(bb_path_mtab_file);
511
512 if (ENABLE_FEATURE_CLEAN_UP) {
513 for (i = 0; i < count; i++) {
514 free(entries[i].mnt_fsname);
515 free(entries[i].mnt_dir);
516 free(entries[i].mnt_type);
517 free(entries[i].mnt_opts);
518 }
519 free(entries);
520 }
521}
522#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000523
524#if ENABLE_FEATURE_MOUNT_VERBOSE
525static int verbose_mount(const char *source, const char *target,
526 const char *filesystemtype,
527 unsigned long mountflags, const void *data)
528{
529 int rc;
530
531 errno = 0;
532 rc = mount(source, target, filesystemtype, mountflags, data);
533 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000534 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000535 source, target, filesystemtype,
536 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000537 return rc;
538}
539#else
540#define verbose_mount(...) mount(__VA_ARGS__)
541#endif
542
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000543// Append mount options to string
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000544static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000545{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000546 if (*oldopts && **oldopts) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000547 // Do not insert options which are already there
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000548 while (newopts[0]) {
549 char *p;
550 int len = strlen(newopts);
551 p = strchr(newopts, ',');
552 if (p) len = p - newopts;
553 p = *oldopts;
554 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000555 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000556 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000557 goto skip;
558 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000559 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000560 p++;
561 }
562 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
563 free(*oldopts);
564 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000565 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000566 newopts += len;
567 while (newopts[0] == ',') newopts++;
568 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000569 } else {
570 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000571 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000572 }
573}
574
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000575// Use the mount_options list to parse options into flags.
Alexander Shishkin77650952010-10-28 06:10:03 +0200576// Also update list of unrecognized options if unrecognized != NULL
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200577static unsigned long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000578{
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200579 unsigned long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000580
Rob Landley6a6798b2005-08-10 20:35:54 +0000581 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000582 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000583 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000584 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000585 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000586
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000587 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000588
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000589// FIXME: use hasmntopt()
Rob Landley6a6798b2005-08-10 20:35:54 +0000590 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000591 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200592 unsigned opt_len = strlen(option_str);
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100593
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200594 if (strncasecmp(option_str, options, opt_len) == 0
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100595 && (options[opt_len] == '\0'
596 /* or is it "comment=" thingy in fstab? */
597 IF_FEATURE_MOUNT_FSTAB(IF_DESKTOP( || option_str[opt_len-1] == '=' ))
598 )
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200599 ) {
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200600 unsigned long fl = mount_options[i];
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200601 if (fl & BB_MS_INVERTED_VALUE)
Alexander Shishkin77650952010-10-28 06:10:03 +0200602 flags &= fl;
603 else
604 flags |= fl;
605 goto found;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000606 }
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200607 option_str += opt_len + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000608 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200609 // We did not recognize this option.
610 // If "unrecognized" is not NULL, append option there.
611 // Note that we should not append *empty* option -
612 // in this case we want to pass NULL, not "", to "data"
613 // parameter of mount(2) syscall.
614 // This is crucial for filesystems that don't accept
615 // any arbitrary mount options, like cgroup fs:
616 // "mount -t cgroup none /mnt"
617 if (options[0] && unrecognized) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000618 // Add it to strflags, to pass on to kernel
Alexander Shishkin77650952010-10-28 06:10:03 +0200619 char *p = *unrecognized;
620 unsigned len = p ? strlen(p) : 0;
621 *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000622
Rob Landley6a6798b2005-08-10 20:35:54 +0000623 // Comma separated if it's not the first one
Alexander Shishkin77650952010-10-28 06:10:03 +0200624 if (len) p[len++] = ',';
625 strcpy(p + len, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000626 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200627 found:
Denis Vlasenko2535f122007-09-15 13:28:30 +0000628 if (!comma)
629 break;
630 // Advance to next option
631 *comma = ',';
632 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000633 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000634
Rob Landleydc0955b2006-03-14 18:16:25 +0000635 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000636}
637
Rob Landleydc0955b2006-03-14 18:16:25 +0000638// Return a list of all block device backed filesystems
Rob Landleydc0955b2006-03-14 18:16:25 +0000639static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000640{
Denis Vlasenko87468852007-04-13 23:22:00 +0000641 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000642 "/etc/filesystems",
643 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000644 };
645 char *fs, *buf;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200646 llist_t *list = NULL;
Rob Landleydc0955b2006-03-14 18:16:25 +0000647 int i;
648 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000649
Denis Vlasenko87468852007-04-13 23:22:00 +0000650 for (i = 0; i < 2; i++) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000651 f = fopen_for_read(filesystems[i]);
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000652 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000653
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000654 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denys Vlasenko8dff01d2015-03-12 17:48:34 +0100655 if (is_prefixed_with(buf, "nodev") && isspace(buf[5]))
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100656 goto next;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000657 fs = skip_whitespace(buf);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200658 if (*fs == '#' || *fs == '*' || !*fs)
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100659 goto next;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000660
Denis Vlasenko372686b2006-10-12 22:42:33 +0000661 llist_add_to_end(&list, xstrdup(fs));
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100662 next:
Denis Vlasenko372686b2006-10-12 22:42:33 +0000663 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000664 }
665 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
666 }
667
668 return list;
669}
670
Rob Landleydc0955b2006-03-14 18:16:25 +0000671#if ENABLE_FEATURE_CLEAN_UP
672static void delete_block_backed_filesystems(void)
673{
Rob Landleya6b5b602006-05-08 19:03:07 +0000674 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000675}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000676#else
677void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000678#endif
679
Rob Landleydc0955b2006-03-14 18:16:25 +0000680// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000681// NB: mp->xxx fields may be trashed on exit
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200682static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000683{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000684 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000685
Denys Vlasenko911d2652015-12-30 20:11:34 +0100686 vfsflags &= ~(unsigned long)MOUNT_FAKEFLAGS;
687
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200688 if (FAKE_IT) {
Denis Vlasenkob4133682008-02-18 13:05:38 +0000689 if (verbose >= 2)
690 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
691 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
692 vfsflags, filteropts);
693 goto mtab;
694 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000695
Rob Landleydc0955b2006-03-14 18:16:25 +0000696 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000697 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000698 errno = 0;
699 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000700 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000701
702 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000703 // helper program mount.<mnt_type>
Denys Vlasenkoba986032009-09-15 23:00:09 +0200704 if (HELPERS_ALLOWED && rc && mp->mnt_type) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200705 char *args[8];
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000706 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000707 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000708 rc = 1;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200709 if (FAKE_IT)
710 args[rc++] = (char *)"-f";
711 if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
712 args[rc++] = (char *)"-n";
Denis Vlasenko5c329932009-04-12 12:16:21 +0000713 args[rc++] = mp->mnt_fsname;
714 args[rc++] = mp->mnt_dir;
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000715 if (filteropts) {
716 args[rc++] = (char *)"-o";
717 args[rc++] = filteropts;
718 }
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000719 args[rc] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +0100720 rc = spawn_and_wait(args);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000721 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000722 if (!rc)
723 break;
724 errno = errno_save;
725 }
726
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000727 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000728 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000729 if (!(vfsflags & MS_SILENT))
730 bb_error_msg("%s is write-protected, mounting read-only",
731 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000732 vfsflags |= MS_RDONLY;
733 }
734
Rob Landleydc0955b2006-03-14 18:16:25 +0000735 // Abort entirely if permission denied.
736
737 if (rc && errno == EPERM)
738 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
739
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000740 // If the mount was successful, and we're maintaining an old-style
741 // mtab file by hand, add the new entry to it now.
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000742 mtab:
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200743 if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000744 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000745 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000746 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000747 int i;
748
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000749 if (!mountTable) {
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100750 bb_perror_msg(bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000751 goto ret;
752 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000753
754 // Add vfs string flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000755 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
756 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
757 append_mount_options(&(mp->mnt_opts), option_str);
758 option_str += strlen(option_str) + 1;
759 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000760
761 // Remove trailing / (if any) from directory we mounted on
Denis Vlasenko727ef942006-09-14 13:19:19 +0000762 i = strlen(mp->mnt_dir) - 1;
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100763 while (i > 0 && mp->mnt_dir[i] == '/')
Denys Vlasenkoc6450c92011-02-28 11:09:49 +0100764 mp->mnt_dir[i--] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000765
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000766 // Convert to canonical pathnames as needed
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000767 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100768 fsname = NULL;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000769 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000770 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000771 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000772 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000773 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000774
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100775 // Write and close
776#if ENABLE_FEATURE_MTAB_SUPPORT
777 if (vfsflags & MS_MOVE)
778 update_mtab_entry_on_move(mp);
779 else
780#endif
781 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000782 endmntent(mountTable);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100783
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000784 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000785 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000786 free(fsname);
787 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000788 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000789 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000790 return rc;
791}
792
Denis Vlasenko25098f72006-09-14 15:46:33 +0000793#if ENABLE_FEATURE_MOUNT_NFS
794
795/*
796 * Linux NFS mount
797 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
798 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +0200799 * Licensed under GPLv2, see file LICENSE in this source tree.
Denis Vlasenko25098f72006-09-14 15:46:33 +0000800 *
801 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
802 * numbers to be specified on the command line.
803 *
804 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
805 * Omit the call to connect() for Linux version 1.3.11 or later.
806 *
807 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
808 * Implemented the "bg", "fg" and "retry" mount options for NFS.
809 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000810 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000811 * - added Native Language Support
812 *
813 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
814 * plus NFSv3 stuff.
815 */
816
Denis Vlasenko25098f72006-09-14 15:46:33 +0000817#define MOUNTPORT 635
818#define MNTPATHLEN 1024
819#define MNTNAMLEN 255
820#define FHSIZE 32
821#define FHSIZE3 64
822
823typedef char fhandle[FHSIZE];
824
825typedef struct {
826 unsigned int fhandle3_len;
827 char *fhandle3_val;
828} fhandle3;
829
830enum mountstat3 {
831 MNT_OK = 0,
832 MNT3ERR_PERM = 1,
833 MNT3ERR_NOENT = 2,
834 MNT3ERR_IO = 5,
835 MNT3ERR_ACCES = 13,
836 MNT3ERR_NOTDIR = 20,
837 MNT3ERR_INVAL = 22,
838 MNT3ERR_NAMETOOLONG = 63,
839 MNT3ERR_NOTSUPP = 10004,
840 MNT3ERR_SERVERFAULT = 10006,
841};
842typedef enum mountstat3 mountstat3;
843
844struct fhstatus {
845 unsigned int fhs_status;
846 union {
847 fhandle fhs_fhandle;
848 } fhstatus_u;
849};
850typedef struct fhstatus fhstatus;
851
852struct mountres3_ok {
853 fhandle3 fhandle;
854 struct {
855 unsigned int auth_flavours_len;
856 char *auth_flavours_val;
857 } auth_flavours;
858};
859typedef struct mountres3_ok mountres3_ok;
860
861struct mountres3 {
862 mountstat3 fhs_status;
863 union {
864 mountres3_ok mountinfo;
865 } mountres3_u;
866};
867typedef struct mountres3 mountres3;
868
869typedef char *dirpath;
870
871typedef char *name;
872
873typedef struct mountbody *mountlist;
874
875struct mountbody {
876 name ml_hostname;
877 dirpath ml_directory;
878 mountlist ml_next;
879};
880typedef struct mountbody mountbody;
881
882typedef struct groupnode *groups;
883
884struct groupnode {
885 name gr_name;
886 groups gr_next;
887};
888typedef struct groupnode groupnode;
889
890typedef struct exportnode *exports;
891
892struct exportnode {
893 dirpath ex_dir;
894 groups ex_groups;
895 exports ex_next;
896};
897typedef struct exportnode exportnode;
898
899struct ppathcnf {
900 int pc_link_max;
901 short pc_max_canon;
902 short pc_max_input;
903 short pc_name_max;
904 short pc_path_max;
905 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000906 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000907 char pc_xxx;
908 short pc_mask[2];
909};
910typedef struct ppathcnf ppathcnf;
911
912#define MOUNTPROG 100005
913#define MOUNTVERS 1
914
915#define MOUNTPROC_NULL 0
916#define MOUNTPROC_MNT 1
917#define MOUNTPROC_DUMP 2
918#define MOUNTPROC_UMNT 3
919#define MOUNTPROC_UMNTALL 4
920#define MOUNTPROC_EXPORT 5
921#define MOUNTPROC_EXPORTALL 6
922
923#define MOUNTVERS_POSIX 2
924
925#define MOUNTPROC_PATHCONF 7
926
927#define MOUNT_V3 3
928
929#define MOUNTPROC3_NULL 0
930#define MOUNTPROC3_MNT 1
931#define MOUNTPROC3_DUMP 2
932#define MOUNTPROC3_UMNT 3
933#define MOUNTPROC3_UMNTALL 4
934#define MOUNTPROC3_EXPORT 5
935
936enum {
937#ifndef NFS_FHSIZE
938 NFS_FHSIZE = 32,
939#endif
940#ifndef NFS_PORT
941 NFS_PORT = 2049
942#endif
943};
944
Denis Vlasenko25098f72006-09-14 15:46:33 +0000945/*
946 * We want to be able to compile mount on old kernels in such a way
947 * that the binary will work well on more recent kernels.
948 * Thus, if necessary we teach nfsmount.c the structure of new fields
949 * that will come later.
950 *
951 * Moreover, the new kernel includes conflict with glibc includes
952 * so it is easiest to ignore the kernel altogether (at compile time).
953 */
954
955struct nfs2_fh {
Dave Lovefae473c2011-11-10 15:19:25 +0100956 char data[32];
Denis Vlasenko25098f72006-09-14 15:46:33 +0000957};
958struct nfs3_fh {
Dave Lovefae473c2011-11-10 15:19:25 +0100959 unsigned short size;
960 unsigned char data[64];
Denis Vlasenko25098f72006-09-14 15:46:33 +0000961};
962
963struct nfs_mount_data {
Dave Lovefae473c2011-11-10 15:19:25 +0100964 int version; /* 1 */
965 int fd; /* 1 */
966 struct nfs2_fh old_root; /* 1 */
967 int flags; /* 1 */
968 int rsize; /* 1 */
969 int wsize; /* 1 */
970 int timeo; /* 1 */
971 int retrans; /* 1 */
972 int acregmin; /* 1 */
973 int acregmax; /* 1 */
974 int acdirmin; /* 1 */
975 int acdirmax; /* 1 */
976 struct sockaddr_in addr; /* 1 */
977 char hostname[256]; /* 1 */
978 int namlen; /* 2 */
979 unsigned int bsize; /* 3 */
980 struct nfs3_fh root; /* 4 */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000981};
982
983/* bits in the flags field */
984enum {
985 NFS_MOUNT_SOFT = 0x0001, /* 1 */
986 NFS_MOUNT_INTR = 0x0002, /* 1 */
987 NFS_MOUNT_SECURE = 0x0004, /* 1 */
988 NFS_MOUNT_POSIX = 0x0008, /* 1 */
989 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
990 NFS_MOUNT_NOAC = 0x0020, /* 1 */
991 NFS_MOUNT_TCP = 0x0040, /* 2 */
992 NFS_MOUNT_VER3 = 0x0080, /* 3 */
993 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000994 NFS_MOUNT_NONLM = 0x0200, /* 3 */
Dave Lovefae473c2011-11-10 15:19:25 +0100995 NFS_MOUNT_NOACL = 0x0800, /* 4 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000996 NFS_MOUNT_NORDIRPLUS = 0x4000
Denis Vlasenko25098f72006-09-14 15:46:33 +0000997};
998
999
1000/*
1001 * We need to translate between nfs status return values and
1002 * the local errno values which may not be the same.
1003 *
1004 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
1005 * "after #include <errno.h> the symbol errno is reserved for any use,
1006 * it cannot even be used as a struct tag or field name".
1007 */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001008#ifndef EDQUOT
Denys Vlasenkocc428142009-12-16 02:06:56 +01001009# define EDQUOT ENOSPC
Denis Vlasenko25098f72006-09-14 15:46:33 +00001010#endif
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001011/* Convert each NFSERR_BLAH into EBLAH */
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02001012static const uint8_t nfs_err_stat[] ALIGN1 = {
Denys Vlasenkocc428142009-12-16 02:06:56 +01001013 1, 2, 5, 6, 13, 17,
1014 19, 20, 21, 22, 27, 28,
1015 30, 63, 66, 69, 70, 71
1016};
Denys Vlasenkodc1fd2e2010-05-19 17:01:29 +02001017#if ( \
1018 EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \
1019 ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \
1020 EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
1021typedef uint8_t nfs_err_type;
1022#else
1023typedef uint16_t nfs_err_type;
1024#endif
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02001025static const nfs_err_type nfs_err_errnum[] ALIGN2 = {
Denys Vlasenkocc428142009-12-16 02:06:56 +01001026 EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST,
1027 ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC,
1028 EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
Denis Vlasenko25098f72006-09-14 15:46:33 +00001029};
Denis Vlasenko25098f72006-09-14 15:46:33 +00001030static char *nfs_strerror(int status)
1031{
1032 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001033
Denys Vlasenkocc428142009-12-16 02:06:56 +01001034 for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
1035 if (nfs_err_stat[i] == status)
1036 return strerror(nfs_err_errnum[i]);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001037 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001038 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001039}
1040
1041static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
1042{
Alexander Shishkin54779a42010-10-22 13:35:47 +02001043 return xdr_opaque(xdrs, objp, FHSIZE);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001044}
1045
1046static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
1047{
1048 if (!xdr_u_int(xdrs, &objp->fhs_status))
Denys Vlasenko69675782013-01-14 01:34:48 +01001049 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +02001050 if (objp->fhs_status == 0)
1051 return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001052 return TRUE;
1053}
1054
1055static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
1056{
Alexander Shishkin54779a42010-10-22 13:35:47 +02001057 return xdr_string(xdrs, objp, MNTPATHLEN);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001058}
1059
1060static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
1061{
Alexander Shishkin54779a42010-10-22 13:35:47 +02001062 return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
Denys Vlasenko69675782013-01-14 01:34:48 +01001063 (unsigned int *) &objp->fhandle3_len,
1064 FHSIZE3);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001065}
1066
1067static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
1068{
1069 if (!xdr_fhandle3(xdrs, &objp->fhandle))
1070 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +02001071 return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
Denys Vlasenko69675782013-01-14 01:34:48 +01001072 &(objp->auth_flavours.auth_flavours_len),
1073 ~0,
1074 sizeof(int),
1075 (xdrproc_t) xdr_int);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001076}
1077
1078static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
1079{
Alexander Shishkin54779a42010-10-22 13:35:47 +02001080 return xdr_enum(xdrs, (enum_t *) objp);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001081}
1082
1083static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
1084{
1085 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
1086 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +02001087 if (objp->fhs_status == MNT_OK)
1088 return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001089 return TRUE;
1090}
1091
1092#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
1093
Denis Vlasenko25098f72006-09-14 15:46:33 +00001094/*
1095 * Unfortunately, the kernel prints annoying console messages
1096 * in case of an unexpected nfs mount version (instead of
1097 * just returning some error). Therefore we'll have to try
1098 * and figure out what version the kernel expects.
1099 *
1100 * Variables:
1101 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
1102 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
1103 * nfs_mount_version: version this source and running kernel can handle
1104 */
1105static void
1106find_kernel_nfs_mount_version(void)
1107{
Denis Vlasenkob9256052007-09-28 10:29:17 +00001108 int kernel_version;
1109
1110 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001111 return;
1112
1113 nfs_mount_version = 4; /* default */
1114
1115 kernel_version = get_linux_version_code();
1116 if (kernel_version) {
Denys Vlasenkocc428142009-12-16 02:06:56 +01001117 if (kernel_version < KERNEL_VERSION(2,2,18))
Denis Vlasenko25098f72006-09-14 15:46:33 +00001118 nfs_mount_version = 3;
1119 /* else v4 since 2.3.99pre4 */
1120 }
1121}
1122
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +00001123static void
Denis Vlasenkob9256052007-09-28 10:29:17 +00001124get_mountport(struct pmap *pm_mnt,
1125 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001126 long unsigned prog,
1127 long unsigned version,
1128 long unsigned proto,
1129 long unsigned port)
1130{
1131 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001132
1133 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001134/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
1135 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001136 pmap = pmap_getmaps(server_addr);
1137
1138 if (version > MAX_NFSPROT)
1139 version = MAX_NFSPROT;
1140 if (!prog)
1141 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001142 pm_mnt->pm_prog = prog;
1143 pm_mnt->pm_vers = version;
1144 pm_mnt->pm_prot = proto;
1145 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001146
Denis Vlasenko25098f72006-09-14 15:46:33 +00001147 while (pmap) {
1148 if (pmap->pml_map.pm_prog != prog)
1149 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001150 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001151 goto next;
1152 if (version > 2 && pmap->pml_map.pm_vers != version)
1153 goto next;
1154 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1155 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001156 if (pmap->pml_map.pm_vers > MAX_NFSPROT
1157 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1158 || (port && pmap->pml_map.pm_port != port)
1159 ) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001160 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001161 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001162 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1163 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001164 pmap = pmap->pml_next;
1165 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001166 if (!pm_mnt->pm_vers)
1167 pm_mnt->pm_vers = MOUNTVERS;
1168 if (!pm_mnt->pm_port)
1169 pm_mnt->pm_port = MOUNTPORT;
1170 if (!pm_mnt->pm_prot)
1171 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001172}
1173
Denis Vlasenkof0000652007-09-04 18:30:26 +00001174#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001175static int daemonize(void)
1176{
Denis Vlasenko25098f72006-09-14 15:46:33 +00001177 int pid = fork();
1178 if (pid < 0) /* error */
1179 return -errno;
1180 if (pid > 0) /* parent */
1181 return 0;
1182 /* child */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001183 close(0);
1184 xopen(bb_dev_null, O_RDWR);
1185 xdup2(0, 1);
1186 xdup2(0, 2);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001187 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +00001188 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001189 logmode = LOGMODE_SYSLOG;
1190 return 1;
1191}
Denis Vlasenkof0000652007-09-04 18:30:26 +00001192#else
1193static inline int daemonize(void) { return -ENOSYS; }
1194#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001195
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001196/* TODO */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001197static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001198{
1199 return 0;
1200}
1201
1202/* RPC strerror analogs are terminally idiotic:
1203 * *mandatory* prefix and \n at end.
1204 * This hopefully helps. Usage:
1205 * error_msg_rpc(clnt_*error*(" ")) */
1206static void error_msg_rpc(const char *msg)
1207{
Denis Vlasenko23514fe2006-09-19 14:07:52 +00001208 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001209 while (msg[0] == ' ' || msg[0] == ':') msg++;
1210 len = strlen(msg);
1211 while (len && msg[len-1] == '\n') len--;
1212 bb_error_msg("%.*s", len, msg);
1213}
1214
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001215/* NB: mp->xxx fields may be trashed on exit */
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001216static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001217{
1218 CLIENT *mclient;
1219 char *hostname;
1220 char *pathname;
1221 char *mounthost;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001222 /* prior to 2.6.23, kernel took NFS options in a form of this struct
1223 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1224 * then data pointer is interpreted as a string. */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001225 struct nfs_mount_data data;
1226 char *opt;
1227 struct hostent *hp;
1228 struct sockaddr_in server_addr;
1229 struct sockaddr_in mount_server_addr;
1230 int msock, fsock;
1231 union {
1232 struct fhstatus nfsv2;
1233 struct mountres3 nfsv3;
1234 } status;
1235 int daemonized;
1236 char *s;
1237 int port;
1238 int mountport;
1239 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001240#if BB_MMU
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001241 smallint bg = 0;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001242#else
1243 enum { bg = 0 };
1244#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001245 int retry;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001246 int mountprog;
1247 int mountvers;
1248 int nfsprog;
1249 int nfsvers;
1250 int retval;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001251 /* these all are one-bit really. gcc 4.3.1 likes this combination: */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001252 smallint tcp;
1253 smallint soft;
1254 int intr;
1255 int posix;
1256 int nocto;
1257 int noac;
1258 int nordirplus;
1259 int nolock;
Dave Lovefae473c2011-11-10 15:19:25 +01001260 int noacl;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001261
1262 find_kernel_nfs_mount_version();
1263
1264 daemonized = 0;
1265 mounthost = NULL;
1266 retval = ETIMEDOUT;
1267 msock = fsock = -1;
1268 mclient = NULL;
1269
1270 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1271
1272 filteropts = xstrdup(filteropts); /* going to trash it later... */
1273
1274 hostname = xstrdup(mp->mnt_fsname);
1275 /* mount_main() guarantees that ':' is there */
1276 s = strchr(hostname, ':');
1277 pathname = s + 1;
1278 *s = '\0';
1279 /* Ignore all but first hostname in replicated mounts
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001280 * until they can be fully supported. (mack@sgi.com) */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001281 s = strchr(hostname, ',');
1282 if (s) {
1283 *s = '\0';
1284 bb_error_msg("warning: multiple hostnames not supported");
1285 }
1286
1287 server_addr.sin_family = AF_INET;
1288 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1289 hp = gethostbyname(hostname);
1290 if (hp == NULL) {
1291 bb_herror_msg("%s", hostname);
1292 goto fail;
1293 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001294 if (hp->h_length != (int)sizeof(struct in_addr)) {
1295 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001296 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001297 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001298 }
1299
1300 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1301
1302 /* add IP address to mtab options for use when unmounting */
1303
1304 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1305 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1306 } else {
1307 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1308 mp->mnt_opts[0] ? "," : "",
1309 inet_ntoa(server_addr.sin_addr));
1310 free(mp->mnt_opts);
1311 mp->mnt_opts = tmp;
1312 }
1313
1314 /* Set default options.
1315 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1316 * let the kernel decide.
1317 * timeo is filled in after we know whether it'll be TCP or UDP. */
1318 memset(&data, 0, sizeof(data));
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001319 data.retrans = 3;
1320 data.acregmin = 3;
1321 data.acregmax = 60;
1322 data.acdirmin = 30;
1323 data.acdirmax = 60;
1324 data.namlen = NAME_MAX;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001325
Denis Vlasenko25098f72006-09-14 15:46:33 +00001326 soft = 0;
1327 intr = 0;
1328 posix = 0;
1329 nocto = 0;
1330 nolock = 0;
1331 noac = 0;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001332 nordirplus = 0;
Dave Lovefae473c2011-11-10 15:19:25 +01001333 noacl = 0;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001334 retry = 10000; /* 10000 minutes ~ 1 week */
Bernhard Reutner-Fischer88206292011-05-04 19:03:30 +02001335 tcp = 1; /* nfs-utils uses tcp per default */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001336
1337 mountprog = MOUNTPROG;
1338 mountvers = 0;
1339 port = 0;
1340 mountport = 0;
1341 nfsprog = 100003;
1342 nfsvers = 0;
1343
1344 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001345 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001346 char *opteq = strchr(opt, '=');
1347 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001348 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001349 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001350 /* 0 */ "rsize\0"
1351 /* 1 */ "wsize\0"
1352 /* 2 */ "timeo\0"
1353 /* 3 */ "retrans\0"
1354 /* 4 */ "acregmin\0"
1355 /* 5 */ "acregmax\0"
1356 /* 6 */ "acdirmin\0"
1357 /* 7 */ "acdirmax\0"
1358 /* 8 */ "actimeo\0"
1359 /* 9 */ "retry\0"
1360 /* 10 */ "port\0"
1361 /* 11 */ "mountport\0"
1362 /* 12 */ "mounthost\0"
1363 /* 13 */ "mountprog\0"
1364 /* 14 */ "mountvers\0"
1365 /* 15 */ "nfsprog\0"
1366 /* 16 */ "nfsvers\0"
1367 /* 17 */ "vers\0"
1368 /* 18 */ "proto\0"
1369 /* 19 */ "namlen\0"
1370 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001371
1372 *opteq++ = '\0';
1373 idx = index_in_strings(options, opt);
1374 switch (idx) {
1375 case 12: // "mounthost"
1376 mounthost = xstrndup(opteq,
1377 strcspn(opteq, " \t\n\r,"));
1378 continue;
1379 case 18: // "proto"
Denys Vlasenko8dff01d2015-03-12 17:48:34 +01001380 if (is_prefixed_with(opteq, "tcp"))
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001381 tcp = 1;
Denys Vlasenko8dff01d2015-03-12 17:48:34 +01001382 else if (is_prefixed_with(opteq, "udp"))
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001383 tcp = 0;
1384 else
1385 bb_error_msg("warning: unrecognized proto= option");
1386 continue;
1387 case 20: // "addr" - ignore
1388 continue;
Peter Korsgaard301fe502011-02-21 17:52:13 +01001389 case -1: // unknown
1390 if (vfsflags & MS_REMOUNT)
1391 continue;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001392 }
1393
Denys Vlasenko77832482010-08-12 14:14:45 +02001394 val = xatoi_positive(opteq);
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001395 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001396 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001397 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001398 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001399 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001400 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001401 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001402 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001403 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001404 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001405 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001406 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001407 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001408 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001409 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001410 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001411 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001412 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001413 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001414 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001415 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001416 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001417 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001418 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001419 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001420 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001421 data.acregmin = val;
1422 data.acregmax = val;
1423 data.acdirmin = val;
1424 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001425 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001426 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001427 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001428 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001429 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001430 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001431 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001432 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001433 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001434 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001435 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001436 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001437 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001438 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001439 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001440 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001441 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001442 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001443 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001444 case 16: // "nfsvers"
1445 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001446 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001447 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001448 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001449 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001450 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001451 //else
1452 // bb_error_msg("warning: option namlen is not supported\n");
1453 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001454 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001455 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1456 goto fail;
1457 }
1458 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001459 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001460 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001461 "bg\0"
1462 "fg\0"
1463 "soft\0"
1464 "hard\0"
1465 "intr\0"
1466 "posix\0"
1467 "cto\0"
1468 "ac\0"
1469 "tcp\0"
1470 "udp\0"
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001471 "lock\0"
Dave Lovefae473c2011-11-10 15:19:25 +01001472 "rdirplus\0"
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001473 "acl\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001474 int val = 1;
Denys Vlasenko8dff01d2015-03-12 17:48:34 +01001475 if (is_prefixed_with(opt, "no")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001476 val = 0;
1477 opt += 2;
1478 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001479 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001480 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001481#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001482 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001483#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001484 break;
1485 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001486#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001487 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001488#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001489 break;
1490 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001491 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001492 break;
1493 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001494 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001495 break;
1496 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001497 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001498 break;
1499 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001500 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001501 break;
1502 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001503 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001504 break;
1505 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001506 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001507 break;
1508 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001509 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001510 break;
1511 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001512 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001513 break;
1514 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001515 if (nfs_mount_version >= 3)
1516 nolock = !val;
1517 else
1518 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001519 break;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001520 case 11: //rdirplus
1521 nordirplus = !val;
1522 break;
Dave Lovefae473c2011-11-10 15:19:25 +01001523 case 12: // acl
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001524 noacl = !val;
Dave Lovefae473c2011-11-10 15:19:25 +01001525 break;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001526 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001527 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1528 goto fail;
1529 }
1530 }
1531 }
1532 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1533
1534 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1535 | (intr ? NFS_MOUNT_INTR : 0)
1536 | (posix ? NFS_MOUNT_POSIX : 0)
1537 | (nocto ? NFS_MOUNT_NOCTO : 0)
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001538 | (noac ? NFS_MOUNT_NOAC : 0)
Dave Lovefae473c2011-11-10 15:19:25 +01001539 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001540 | (noacl ? NFS_MOUNT_NOACL : 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001541 if (nfs_mount_version >= 2)
1542 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1543 if (nfs_mount_version >= 3)
1544 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1545 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1546 bb_error_msg("NFSv%d not supported", nfsvers);
1547 goto fail;
1548 }
1549 if (nfsvers && !mountvers)
1550 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1551 if (nfsvers && nfsvers < mountvers) {
1552 mountvers = nfsvers;
1553 }
1554
1555 /* Adjust options if none specified */
1556 if (!data.timeo)
1557 data.timeo = tcp ? 70 : 7;
1558
Denis Vlasenko25098f72006-09-14 15:46:33 +00001559 data.version = nfs_mount_version;
1560
1561 if (vfsflags & MS_REMOUNT)
1562 goto do_mount;
1563
1564 /*
1565 * If the previous mount operation on the same host was
1566 * backgrounded, and the "bg" for this mount is also set,
1567 * give up immediately, to avoid the initial timeout.
1568 */
1569 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001570 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001571 if (daemonized <= 0) { /* parent or error */
1572 retval = -daemonized;
1573 goto ret;
1574 }
1575 }
1576
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001577 /* Create mount daemon client */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001578 /* See if the nfs host = mount host. */
1579 if (mounthost) {
1580 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1581 mount_server_addr.sin_family = AF_INET;
1582 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1583 } else {
1584 hp = gethostbyname(mounthost);
1585 if (hp == NULL) {
1586 bb_herror_msg("%s", mounthost);
1587 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001588 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001589 if (hp->h_length != (int)sizeof(struct in_addr)) {
1590 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001591 }
1592 mount_server_addr.sin_family = AF_INET;
Denys Vlasenko99069332010-02-27 19:38:19 +01001593 memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001594 }
1595 }
1596
1597 /*
1598 * The following loop implements the mount retries. When the mount
1599 * times out, and the "bg" option is set, we background ourself
1600 * and continue trying.
1601 *
1602 * The case where the mount point is not present and the "bg"
1603 * option is set, is treated as a timeout. This is done to
1604 * support nested mounts.
1605 *
1606 * The "retry" count specified by the user is the number of
1607 * minutes to retry before giving up.
1608 */
1609 {
1610 struct timeval total_timeout;
1611 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001612 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001613 time_t t;
1614 time_t prevt;
1615 time_t timeout;
1616
1617 retry_timeout.tv_sec = 3;
1618 retry_timeout.tv_usec = 0;
1619 total_timeout.tv_sec = 20;
1620 total_timeout.tv_usec = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001621/* FIXME: use monotonic()? */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001622 timeout = time(NULL) + 60 * retry;
1623 prevt = 0;
1624 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001625 retry:
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001626 /* Be careful not to use too many CPU cycles */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001627 if (t - prevt < 30)
1628 sleep(30);
1629
Denis Vlasenkob9256052007-09-28 10:29:17 +00001630 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001631 mountprog,
1632 mountvers,
1633 proto,
1634 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001635 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001636
1637 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001638 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001639 msock = RPC_ANYSOCK;
1640
Denis Vlasenkob9256052007-09-28 10:29:17 +00001641 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001642 case IPPROTO_UDP:
1643 mclient = clntudp_create(&mount_server_addr,
Denys Vlasenko69675782013-01-14 01:34:48 +01001644 pm_mnt.pm_prog,
1645 pm_mnt.pm_vers,
1646 retry_timeout,
1647 &msock);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001648 if (mclient)
1649 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001650 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001651 msock = RPC_ANYSOCK;
1652 case IPPROTO_TCP:
1653 mclient = clnttcp_create(&mount_server_addr,
Denys Vlasenko69675782013-01-14 01:34:48 +01001654 pm_mnt.pm_prog,
1655 pm_mnt.pm_vers,
1656 &msock, 0, 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001657 break;
1658 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001659 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001660 }
1661 if (!mclient) {
1662 if (!daemonized && prevt == 0)
1663 error_msg_rpc(clnt_spcreateerror(" "));
1664 } else {
1665 enum clnt_stat clnt_stat;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001666
1667 /* Try to mount hostname:pathname */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001668 mclient->cl_auth = authunix_create_default();
1669
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001670 /* Make pointers in xdr_mountres3 NULL so
Denis Vlasenko25098f72006-09-14 15:46:33 +00001671 * that xdr_array allocates memory for us
1672 */
1673 memset(&status, 0, sizeof(status));
1674
Denis Vlasenkob9256052007-09-28 10:29:17 +00001675 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001676 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
Denys Vlasenko69675782013-01-14 01:34:48 +01001677 (xdrproc_t) xdr_dirpath,
1678 (caddr_t) &pathname,
1679 (xdrproc_t) xdr_mountres3,
1680 (caddr_t) &status,
1681 total_timeout);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001682 else
1683 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
Denys Vlasenko69675782013-01-14 01:34:48 +01001684 (xdrproc_t) xdr_dirpath,
1685 (caddr_t) &pathname,
1686 (xdrproc_t) xdr_fhstatus,
1687 (caddr_t) &status,
1688 total_timeout);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001689
1690 if (clnt_stat == RPC_SUCCESS)
1691 goto prepare_kernel_data; /* we're done */
1692 if (errno != ECONNREFUSED) {
1693 error_msg_rpc(clnt_sperror(mclient, " "));
1694 goto fail; /* don't retry */
1695 }
1696 /* Connection refused */
1697 if (!daemonized && prevt == 0) /* print just once */
1698 error_msg_rpc(clnt_sperror(mclient, " "));
1699 auth_destroy(mclient->cl_auth);
1700 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001701 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001702 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001703 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001704 }
1705
1706 /* Timeout. We are going to retry... maybe */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001707 if (!bg)
1708 goto fail;
1709 if (!daemonized) {
1710 daemonized = daemonize();
1711 if (daemonized <= 0) { /* parent or error */
1712 retval = -daemonized;
1713 goto ret;
1714 }
1715 }
1716 prevt = t;
1717 t = time(NULL);
1718 if (t >= timeout)
1719 /* TODO error message */
1720 goto fail;
1721
1722 goto retry;
1723 }
1724
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001725 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001726
1727 if (nfsvers == 2) {
1728 if (status.nfsv2.fhs_status != 0) {
1729 bb_error_msg("%s:%s failed, reason given by server: %s",
1730 hostname, pathname,
1731 nfs_strerror(status.nfsv2.fhs_status));
1732 goto fail;
1733 }
1734 memcpy(data.root.data,
1735 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1736 NFS_FHSIZE);
1737 data.root.size = NFS_FHSIZE;
1738 memcpy(data.old_root.data,
1739 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1740 NFS_FHSIZE);
1741 } else {
1742 fhandle3 *my_fhandle;
1743 if (status.nfsv3.fhs_status != 0) {
1744 bb_error_msg("%s:%s failed, reason given by server: %s",
1745 hostname, pathname,
1746 nfs_strerror(status.nfsv3.fhs_status));
1747 goto fail;
1748 }
1749 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1750 memset(data.old_root.data, 0, NFS_FHSIZE);
1751 memset(&data.root, 0, sizeof(data.root));
1752 data.root.size = my_fhandle->fhandle3_len;
1753 memcpy(data.root.data,
1754 (char *) my_fhandle->fhandle3_val,
1755 my_fhandle->fhandle3_len);
1756
1757 data.flags |= NFS_MOUNT_VER3;
1758 }
1759
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001760 /* Create nfs socket for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001761 if (tcp) {
1762 if (nfs_mount_version < 3) {
1763 bb_error_msg("NFS over TCP is not supported");
1764 goto fail;
1765 }
1766 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1767 } else
1768 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1769 if (fsock < 0) {
1770 bb_perror_msg("nfs socket");
1771 goto fail;
1772 }
1773 if (bindresvport(fsock, 0) < 0) {
1774 bb_perror_msg("nfs bindresvport");
1775 goto fail;
1776 }
1777 if (port == 0) {
1778 server_addr.sin_port = PMAPPORT;
1779 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1780 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1781 if (port == 0)
1782 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001783 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001784 server_addr.sin_port = htons(port);
1785
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001786 /* Prepare data structure for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001787 data.fd = fsock;
1788 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1789 strncpy(data.hostname, hostname, sizeof(data.hostname));
1790
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001791 /* Clean up */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001792 auth_destroy(mclient->cl_auth);
1793 clnt_destroy(mclient);
1794 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001795 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001796
1797 if (bg) {
1798 /* We must wait until mount directory is available */
1799 struct stat statbuf;
1800 int delay = 1;
1801 while (stat(mp->mnt_dir, &statbuf) == -1) {
1802 if (!daemonized) {
1803 daemonized = daemonize();
1804 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001805/* FIXME: parent doesn't close fsock - ??! */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001806 retval = -daemonized;
1807 goto ret;
1808 }
1809 }
1810 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1811 delay *= 2;
1812 if (delay > 30)
1813 delay = 30;
1814 }
1815 }
1816
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001817 /* Perform actual mount */
1818 do_mount:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001819 retval = mount_it_now(mp, vfsflags, (char*)&data);
1820 goto ret;
1821
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001822 /* Abort */
1823 fail:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001824 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001825 if (mclient) {
1826 auth_destroy(mclient->cl_auth);
1827 clnt_destroy(mclient);
1828 }
1829 close(msock);
1830 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001831 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001832 close(fsock);
1833
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001834 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001835 free(hostname);
1836 free(mounthost);
1837 free(filteropts);
1838 return retval;
1839}
1840
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001841#else // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001842
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001843/* Linux 2.6.23+ supports nfs mounts with options passed as a string.
1844 * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
1845 * (However, note that then you lose any chances that NFS over IPv6 would work).
1846 */
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001847static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001848{
1849 len_and_sockaddr *lsa;
1850 char *opts;
1851 char *end;
1852 char *dotted;
1853 int ret;
1854
1855# if ENABLE_FEATURE_IPV6
1856 end = strchr(mp->mnt_fsname, ']');
1857 if (end && end[1] == ':')
1858 end++;
1859 else
1860# endif
1861 /* mount_main() guarantees that ':' is there */
1862 end = strchr(mp->mnt_fsname, ':');
1863
1864 *end = '\0';
Denys Vlasenko39b23312011-11-10 17:01:39 +01001865 lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001866 *end = ':';
1867 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1868 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1869 opts = xasprintf("%s%saddr=%s",
1870 filteropts ? filteropts : "",
1871 filteropts ? "," : "",
1872 dotted
1873 );
1874 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1875 ret = mount_it_now(mp, vfsflags, opts);
1876 if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1877
1878 return ret;
1879}
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001880
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001881#endif // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001882
1883// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1884// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001885// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001886static int singlemount(struct mntent *mp, int ignore_busy)
1887{
Denys Vlasenkoab518ee2017-03-16 16:49:37 +01001888 int loopfd = -1;
Denis Vlasenkob4133682008-02-18 13:05:38 +00001889 int rc = -1;
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001890 unsigned long vfsflags;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001891 char *loopFile = NULL, *filteropts = NULL;
1892 llist_t *fl = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001893 struct stat st;
1894
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001895 errno = 0;
1896
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001897 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1898
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001899 // Treat fstype "auto" as unspecified
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001900 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1901 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001902
Denis Vlasenko2535f122007-09-15 13:28:30 +00001903 // Might this be a virtual filesystem?
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001904 if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1905 char *args[35];
1906 char *s;
1907 int n;
1908 // fsname: "cmd#arg1#arg2..."
1909 // WARNING: allows execution of arbitrary commands!
1910 // Try "mount 'sh#-c#sh' bogus_dir".
1911 // It is safe ONLY because non-root
1912 // cannot use two-argument mount command
1913 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1914 // "mount: can't find sh#-c#sh in /etc/fstab"
1915 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1916
1917 s = mp->mnt_fsname;
1918 n = 0;
1919 args[n++] = s;
1920 while (*s && n < 35 - 2) {
1921 if (*s++ == '#' && *s != '#') {
1922 s[-1] = '\0';
1923 args[n++] = s;
Denis Vlasenko2535f122007-09-15 13:28:30 +00001924 }
1925 }
Denis Vlasenko2535f122007-09-15 13:28:30 +00001926 args[n++] = mp->mnt_dir;
1927 args[n] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +01001928 rc = spawn_and_wait(args);
Denis Vlasenko2535f122007-09-15 13:28:30 +00001929 goto report_error;
1930 }
1931
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001932 // Might this be an CIFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001933 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001934 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1935 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1936 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001937 ) {
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001938 int len;
1939 char c;
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001940 char *hostname, *share;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001941 len_and_sockaddr *lsa;
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001942
1943 // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001944
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001945 hostname = mp->mnt_fsname + 2;
1946 len = strcspn(hostname, "/\\");
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001947 share = hostname + len + 1;
1948 if (len == 0 // 3rd char is a [back]slash (IOW: empty hostname)
1949 || share[-1] == '\0' // no [back]slash after hostname
1950 || share[0] == '\0' // empty share name
Martin Santesson406ea152013-01-16 00:47:19 +01001951 ) {
Denis Vlasenko5c329932009-04-12 12:16:21 +00001952 goto report_error;
Martin Santesson406ea152013-01-16 00:47:19 +01001953 }
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001954 c = share[-1];
1955 share[-1] = '\0';
1956 len = strcspn(share, "/\\");
Martin Santesson406ea152013-01-16 00:47:19 +01001957
1958 // "unc=\\hostname\share" option is mandatory
1959 // after CIFS option parsing was rewritten in Linux 3.4.
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001960 // Must use backslashes.
1961 // If /dir1/dir2 is present, also add "prefixpath=dir1/dir2"
Martin Santesson406ea152013-01-16 00:47:19 +01001962 {
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001963 char *unc = xasprintf(
1964 share[len] != '\0' /* "/dir1/dir2" exists? */
1965 ? "unc=\\\\%s\\%.*s,prefixpath=%s"
1966 : "unc=\\\\%s\\%.*s",
1967 hostname,
1968 len, share,
1969 share + len + 1 /* "dir1/dir2" */
1970 );
Denys Vlasenko9b7ebfe2013-01-22 11:00:45 +01001971 parse_mount_options(unc, &filteropts);
1972 if (ENABLE_FEATURE_CLEAN_UP) free(unc);
Martin Santesson406ea152013-01-16 00:47:19 +01001973 }
1974
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001975 lsa = host2sockaddr(hostname, 0);
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001976 share[-1] = c;
Denis Vlasenko5c329932009-04-12 12:16:21 +00001977 if (!lsa)
1978 goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001979
Denys Vlasenkob09ab442016-09-27 21:02:35 +02001980 // If there is no "ip=..." option yet
1981 if (!is_prefixed_with(filteropts, ",ip="+1)
1982 && !strstr(filteropts, ",ip=")
1983 ) {
1984 char *dotted, *ip;
1985 // Insert "ip=..." option into options
1986 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1987 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1988 ip = xasprintf("ip=%s", dotted);
1989 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
Denys Vlasenko5093c8c2016-09-26 20:36:30 +02001990// Note: IPv6 scoped addresses ("host%iface", see RFC 4007) should be
Denys Vlasenko3c18e302016-09-26 19:53:04 +02001991// handled by libc in getnameinfo() (inside xmalloc_sockaddr2dotted_noport()).
1992// Currently, glibc does not support that (has no NI_NUMERICSCOPE),
1993// musl apparently does. This results in "ip=numericIPv6%iface_name"
1994// (instead of _numeric_ iface_id) with glibc.
Denys Vlasenko5093c8c2016-09-26 20:36:30 +02001995// This probably should be fixed in glibc, not here.
Denys Vlasenkob09ab442016-09-27 21:02:35 +02001996// The workaround is to manually specify correct "ip=ADDR%n" option.
1997 parse_mount_options(ip, &filteropts);
1998 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
1999 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002000
Denis Vlasenko06c0a712007-01-29 22:51:44 +00002001 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002002 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002003
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002004 goto report_error;
2005 }
2006
2007 // Might this be an NFS filesystem?
Denys Vlasenko8dff01d2015-03-12 17:48:34 +01002008 if ((!mp->mnt_type || is_prefixed_with(mp->mnt_type, "nfs"))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002009 && strchr(mp->mnt_fsname, ':') != NULL
2010 ) {
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01002011 if (!mp->mnt_type)
2012 mp->mnt_type = (char*)"nfs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002013 rc = nfsmount(mp, vfsflags, filteropts);
2014 goto report_error;
2015 }
2016
2017 // Look at the file. (Not found isn't a failure for remount, or for
2018 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00002019 // (We use stat, not lstat, in order to allow
2020 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00002021 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002022 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
2023 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002024 // Do we need to allocate a loopback device for it?
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002025 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
2026 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002027 mp->mnt_fsname = NULL; // will receive malloced loop dev name
Denys Vlasenkoab518ee2017-03-16 16:49:37 +01002028
2029 // mount always creates AUTOCLEARed loopdevs, so that umounting
2030 // drops them without any code in the userspace.
2031 // This happens since circa linux-2.6.25:
2032 // commit 96c5865559cee0f9cbc5173f3c949f6ce3525581
2033 // Date: Wed Feb 6 01:36:27 2008 -0800
2034 // Subject: Allow auto-destruction of loop devices
2035 loopfd = set_loop(&mp->mnt_fsname,
2036 loopFile,
2037 0,
2038 ((vfsflags & MS_RDONLY) ? BB_LO_FLAGS_READ_ONLY : 0)
2039 | BB_LO_FLAGS_AUTOCLEAR
2040 );
2041 if (loopfd < 0) {
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00002042 if (errno == EPERM || errno == EACCES)
2043 bb_error_msg(bb_msg_perm_denied_are_you_root);
2044 else
Denys Vlasenko6331cf02009-11-13 09:08:27 +01002045 bb_perror_msg("can't setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00002046 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002047 }
2048
2049 // Autodetect bind mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002050 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
2051 vfsflags |= MS_BIND;
2052 }
2053
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002054 // If we know the fstype (or don't need to), jump straight
2055 // to the actual mount.
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02002056 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +01002057 char *next;
2058 for (;;) {
2059 next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
2060 if (next)
2061 *next = '\0';
2062 rc = mount_it_now(mp, vfsflags, filteropts);
2063 if (rc == 0 || !next)
2064 break;
2065 mp->mnt_type = next + 1;
2066 }
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02002067 } else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002068 // Loop through filesystem types until mount succeeds
2069 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002070
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01002071 // Initialize list of block backed filesystems.
2072 // This has to be done here so that during "mount -a",
2073 // mounts after /proc shows up can autodetect.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002074 if (!fslist) {
2075 fslist = get_block_backed_filesystems();
2076 if (ENABLE_FEATURE_CLEAN_UP && fslist)
2077 atexit(delete_block_backed_filesystems);
2078 }
2079
2080 for (fl = fslist; fl; fl = fl->link) {
2081 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00002082 rc = mount_it_now(mp, vfsflags, filteropts);
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +01002083 if (rc == 0)
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02002084 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002085 }
2086 }
2087
2088 // If mount failed, clean up loop file (if any).
Denys Vlasenkoab518ee2017-03-16 16:49:37 +01002089 // (Newer kernels which support LO_FLAGS_AUTOCLEAR should not need this,
2090 // merely "close(loopfd)" should do it?)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002091 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
2092 del_loop(mp->mnt_fsname);
2093 if (ENABLE_FEATURE_CLEAN_UP) {
2094 free(loopFile);
Denys Vlasenkoecf25cb2016-06-20 11:04:04 +02002095 /* No, "rc != 0" needs it: free(mp->mnt_fsname); */
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002096 }
2097 }
2098
Denis Vlasenko5870ad92007-02-04 02:39:55 +00002099 report_error:
2100 if (ENABLE_FEATURE_CLEAN_UP)
2101 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002102
Denys Vlasenkoab518ee2017-03-16 16:49:37 +01002103 if (loopfd >= 0)
2104 close(loopfd);
2105
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00002106 if (errno == EBUSY && ignore_busy)
2107 return 0;
Denys Vlasenko911d2652015-12-30 20:11:34 +01002108 if (errno == ENOENT && (vfsflags & MOUNT_NOFAIL))
2109 return 0;
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02002110 if (rc != 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00002111 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002112 return rc;
2113}
2114
Michael Abbott6b5accb2009-12-04 03:33:07 +01002115// -O support
2116// -O interprets a list of filter options which select whether a mount
2117// point will be mounted: only mounts with options matching *all* filtering
2118// options will be selected.
2119// By default each -O filter option must be present in the list of mount
2120// options, but if it is prefixed by "no" then it must be absent.
2121// For example,
2122// -O a,nob,c matches -o a,c but fails to match -o a,b,c
2123// (and also fails to match -o a because -o c is absent).
2124//
2125// It is different from -t in that each option is matched exactly; a leading
2126// "no" at the beginning of one option does not negate the rest.
2127static int match_opt(const char *fs_opt_in, const char *O_opt)
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002128{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002129 if (!O_opt)
Michael Abbott6b5accb2009-12-04 03:33:07 +01002130 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002131
Michael Abbott6b5accb2009-12-04 03:33:07 +01002132 while (*O_opt) {
2133 const char *fs_opt = fs_opt_in;
2134 int O_len;
2135 int match;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002136
Michael Abbott6b5accb2009-12-04 03:33:07 +01002137 // If option begins with "no" then treat as an inverted match:
2138 // matching is a failure
2139 match = 0;
2140 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
2141 match = 1;
2142 O_opt += 2;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002143 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01002144 // Isolate the current O option
2145 O_len = strchrnul(O_opt, ',') - O_opt;
2146 // Check for a match against existing options
2147 while (1) {
2148 if (strncmp(fs_opt, O_opt, O_len) == 0
2149 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
2150 ) {
2151 if (match)
2152 return 0; // "no" prefix, but option found
2153 match = 1; // current O option found, go check next one
2154 break;
2155 }
2156 fs_opt = strchr(fs_opt, ',');
2157 if (!fs_opt)
2158 break;
2159 fs_opt++;
2160 }
2161 if (match == 0)
2162 return 0; // match wanted but not found
2163 if (O_opt[O_len] == '\0') // end?
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002164 break;
Michael Abbott6b5accb2009-12-04 03:33:07 +01002165 // Step to the next O option
2166 O_opt += O_len + 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002167 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01002168 // If we get here then everything matched
2169 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002170}
2171
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002172// Parse options, if necessary parse fstab/mtab, and call singlemount for
2173// each directory to be mounted.
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00002174int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002175int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002176{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002177 char *cmdopts = xzalloc(1);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002178 char *fstype = NULL;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002179 char *O_optmatch = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002180 char *storage_path;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00002181 llist_t *lst_o = NULL;
Isaac Dunham7b434a62015-03-11 16:07:24 +01002182 const char *fstabname = "/etc/fstab";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002183 FILE *fstab;
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002184 int i, j;
2185 int rc = EXIT_SUCCESS;
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002186 unsigned long cmdopt_flags;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00002187 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002188 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002189 IF_NOT_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002190
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002191 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00002192
Denys Vlasenko16714242011-09-21 01:59:15 +02002193 INIT_G();
2194
Denis Vlasenkof732e962008-02-18 12:07:49 +00002195 // Parse long options, like --bind and --move. Note that -o option
2196 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002197 for (i = j = 1; argv[i]; i++) {
2198 if (argv[i][0] == '-' && argv[i][1] == '-')
2199 append_mount_options(&cmdopts, argv[i] + 2);
2200 else
2201 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002202 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002203 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002204
2205 // Parse remaining options
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002206 // Max 2 params; -o is a list, -v is a counter
Denys Vlasenko22542ec2017-08-08 21:55:02 +02002207 opt = getopt32(argv, "^"
2208 OPTION_STR
2209 "\0" "?2"IF_FEATURE_MOUNT_VERBOSE("vv"),
2210 &lst_o, &fstype, &O_optmatch
2211 IF_FEATURE_MOUNT_OTHERTAB(, &fstabname)
2212 IF_FEATURE_MOUNT_VERBOSE(, &verbose)
2213 );
2214
Denis Vlasenkof9dde912008-10-18 19:15:57 +00002215 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00002216 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2217 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00002218 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002219
2220 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002221 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00002222 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002223 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2224
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002225 if (!mountTable)
2226 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002227
Denis Vlasenko2535f122007-09-15 13:28:30 +00002228 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002229 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002230 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002231 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00002232 // util-linux 2.12a happily shows rootfs...
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002233 //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002234
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002235 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002236 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2237 mtpair->mnt_dir, mtpair->mnt_type,
2238 mtpair->mnt_opts);
2239 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002240 if (ENABLE_FEATURE_CLEAN_UP)
2241 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002242 return EXIT_SUCCESS;
2243 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002244 storage_path = NULL;
2245 } else {
2246 // When we have two arguments, the second is the directory and we can
2247 // skip looking at fstab entirely. We can always abspath() the directory
2248 // argument when we get it.
2249 if (argv[1]) {
2250 if (nonroot)
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002251 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002252 mtpair->mnt_fsname = argv[0];
2253 mtpair->mnt_dir = argv[1];
2254 mtpair->mnt_type = fstype;
2255 mtpair->mnt_opts = cmdopts;
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002256 resolve_mount_spec(&mtpair->mnt_fsname);
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002257 rc = singlemount(mtpair, /*ignore_busy:*/ 0);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002258 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00002259 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002260 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002261 }
2262
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002263 // Past this point, we are handling either "mount -a [opts]"
2264 // or "mount [opts] single_param"
2265
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002266 cmdopt_flags = parse_mount_options(cmdopts, NULL);
2267 if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002268 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00002269
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002270 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002271 if (ENABLE_FEATURE_MOUNT_FLAGS
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002272 && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002273 ) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002274 // verbose_mount(source, target, type, flags, data)
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002275 rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002276 if (rc)
2277 bb_simple_perror_msg_and_die(argv[0]);
2278 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002279 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00002280
Isaac Dunham7b434a62015-03-11 16:07:24 +01002281 // A malicious user could overmount /usr without this.
2282 if (ENABLE_FEATURE_MOUNT_OTHERTAB && nonroot)
2283 fstabname = "/etc/fstab";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002284 // Open either fstab or mtab
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002285 if (cmdopt_flags & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002286 // WARNING. I am not sure this matches util-linux's
2287 // behavior. It's possible util-linux does not
2288 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002289 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002290 }
2291 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00002292 if (!fstab)
Denys Vlasenko651a2692010-03-23 16:25:17 +01002293 bb_perror_msg_and_die("can't read '%s'", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002294
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002295 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00002296 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002297 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002298 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002299
2300 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00002301 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002302 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002303 GETMNTENT_BUFSIZE/2)
2304 ) { // End of fstab/mtab is reached
2305 mtcur = mtother; // the thing we found last time
2306 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002307 }
2308
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002309 // If we're trying to mount something specific and this isn't it,
2310 // skip it. Note we must match the exact text in fstab (ala
2311 // "proc") or a full path from root
2312 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002313
2314 // Is this what we're looking for?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002315 if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2316 && strcmp(storage_path, mtcur->mnt_fsname) != 0
2317 && strcmp(argv[0], mtcur->mnt_dir) != 0
2318 && strcmp(storage_path, mtcur->mnt_dir) != 0
2319 ) {
2320 continue; // no
2321 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002322
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002323 // Remember this entry. Something later may have
2324 // overmounted it, and we want the _last_ match.
2325 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002326
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002327 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002328 } else {
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002329 struct mntent *mp;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002330 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002331 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002332 if (nonroot)
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002333 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002334
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002335 // Does type match? (NULL matches always)
Denys Vlasenko35b54a32017-01-30 00:45:05 +01002336 if (!fstype_matches(mtcur->mnt_type, fstype))
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002337 continue;
2338
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002339 // Skip noauto and swap anyway
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002340 if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2341 // swap is bogus "fstype", parse_mount_options can't check fstypes
2342 || strcasecmp(mtcur->mnt_type, "swap") == 0
2343 ) {
2344 continue;
2345 }
2346
2347 // Does (at least one) option match?
2348 // (NULL matches always)
2349 if (!match_opt(mtcur->mnt_opts, O_optmatch))
2350 continue;
2351
2352 resolve_mount_spec(&mtcur->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002353
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002354 // NFS mounts want this to be xrealloc-able
2355 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002356
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002357 // If nothing is mounted on this directory...
2358 // (otherwise repeated "mount -a" mounts everything again)
2359 mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2360 // We do not check fsname match of found mount point -
2361 // "/" may have fsname of "/dev/root" while fstab
2362 // says "/dev/something_else".
2363 if (mp) {
Denys Vlasenko86566762009-12-10 21:32:28 +01002364 if (verbose) {
2365 bb_error_msg("according to %s, "
2366 "%s is already mounted on %s",
2367 bb_path_mtab_file,
2368 mp->mnt_fsname, mp->mnt_dir);
2369 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002370 } else {
2371 // ...mount this thing
2372 if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2373 // Count number of failed mounts
2374 rc++;
2375 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002376 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002377 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002378 }
2379 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002380
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002381 // End of fstab/mtab is reached.
2382 // Were we looking for something specific?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002383 if (argv[0]) { // yes
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002384 unsigned long l;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002385
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002386 // If we didn't find anything, complain
2387 if (!mtcur->mnt_fsname)
2388 bb_error_msg_and_die("can't find %s in %s",
2389 argv[0], fstabname);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002390
2391 // What happens when we try to "mount swap_partition"?
2392 // (fstab containts "swap_partition swap swap defaults 0 0")
2393 // util-linux-ng 2.13.1 does this:
2394 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2395 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2396 // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory)
2397 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2398 // exit_group(32) = ?
2399#if 0
2400 // In case we want to simply skip swap partitions:
2401 l = parse_mount_options(mtcur->mnt_opts, NULL);
2402 if ((l & MOUNT_SWAP)
2403 // swap is bogus "fstype", parse_mount_options can't check fstypes
2404 || strcasecmp(mtcur->mnt_type, "swap") == 0
2405 ) {
2406 goto ret;
2407 }
2408#endif
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002409 if (nonroot) {
2410 // fstab must have "users" or "user"
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002411 l = parse_mount_options(mtcur->mnt_opts, NULL);
2412 if (!(l & MOUNT_USERS))
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002413 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002414 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002415
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002416 //util-linux-2.12 does not do this check.
2417 //// If nothing is mounted on this directory...
2418 //// (otherwise repeated "mount FOO" mounts FOO again)
2419 //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2420 //if (mp) {
2421 // bb_error_msg("according to %s, "
2422 // "%s is already mounted on %s",
2423 // bb_path_mtab_file,
2424 // mp->mnt_fsname, mp->mnt_dir);
2425 //} else {
2426 // ...mount the last thing we found
2427 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2428 append_mount_options(&(mtcur->mnt_opts), cmdopts);
2429 resolve_mount_spec(&mtpair->mnt_fsname);
2430 rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2431 if (ENABLE_FEATURE_CLEAN_UP)
2432 free(mtcur->mnt_opts);
2433 //}
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002434 }
2435
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002436 //ret:
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002437 if (ENABLE_FEATURE_CLEAN_UP)
2438 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002439 if (ENABLE_FEATURE_CLEAN_UP) {
2440 free(storage_path);
2441 free(cmdopts);
2442 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002443
2444//TODO: exitcode should be ORed mask of (from "man mount"):
2445// 0 success
2446// 1 incorrect invocation or permissions
2447// 2 system error (out of memory, cannot fork, no more loop devices)
2448// 4 internal mount bug or missing nfs support in mount
2449// 8 user interrupt
2450//16 problems writing or locking /etc/mtab
2451//32 mount failure
2452//64 some mount succeeded
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002453 return rc;
2454}