blob: 831dab9e21c6fdffc38074407739c6d254c43bc9 [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 Vlasenkob097a842018-12-28 03:20:17 +010021//config: bool "mount (23 kb)"
Isaac Dunham7b434a62015-03-11 16:07:24 +010022//config: default y
Isaac Dunham7b434a62015-03-11 16:07:24 +010023//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020024//config: All files and filesystems in Unix are arranged into one big directory
25//config: tree. The 'mount' utility is used to graft a filesystem onto a
26//config: particular part of the tree. A filesystem can either live on a block
27//config: device, or it can be accessible over the network, as is the case with
Denys Vlasenko68b653b2017-07-27 10:53:09 +020028//config: NFS filesystems.
Isaac Dunham7b434a62015-03-11 16:07:24 +010029//config:
30//config:config FEATURE_MOUNT_FAKE
Denys Vlasenko5b3cbe32017-07-27 14:45:25 +020031//config: bool "Support -f (fake mount)"
Isaac Dunham7b434a62015-03-11 16:07:24 +010032//config: default y
33//config: depends on MOUNT
34//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020035//config: Enable support for faking a file system mount.
Isaac Dunham7b434a62015-03-11 16:07:24 +010036//config:
37//config:config FEATURE_MOUNT_VERBOSE
Denys Vlasenko5b3cbe32017-07-27 14:45:25 +020038//config: bool "Support -v (verbose)"
Isaac Dunham7b434a62015-03-11 16:07:24 +010039//config: default y
40//config: depends on MOUNT
41//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020042//config: Enable multi-level -v[vv...] verbose messages. Useful if you
43//config: debug mount problems and want to see what is exactly passed
44//config: to the kernel.
Isaac Dunham7b434a62015-03-11 16:07:24 +010045//config:
46//config:config FEATURE_MOUNT_HELPERS
47//config: bool "Support mount helpers"
48//config: default n
49//config: depends on MOUNT
50//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020051//config: Enable mounting of virtual file systems via external helpers.
52//config: E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call
53//config: "obexfs -b00.11.22.33.44.55 /mnt"
54//config: Also "mount -t sometype [-o opts] fs /mnt" will try
55//config: "sometype [-o opts] fs /mnt" if simple mount syscall fails.
56//config: The idea is to use such virtual filesystems in /etc/fstab.
Isaac Dunham7b434a62015-03-11 16:07:24 +010057//config:
58//config:config FEATURE_MOUNT_LABEL
59//config: bool "Support specifying devices by label or UUID"
60//config: default y
61//config: depends on MOUNT
62//config: select VOLUMEID
63//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020064//config: This allows for specifying a device by label or uuid, rather than by
65//config: name. This feature utilizes the same functionality as blkid/findfs.
Isaac Dunham7b434a62015-03-11 16:07:24 +010066//config:
67//config:config FEATURE_MOUNT_NFS
68//config: bool "Support mounting NFS file systems on Linux < 2.6.23"
69//config: default n
70//config: depends on MOUNT
Isaac Dunham7b434a62015-03-11 16:07:24 +010071//config: select FEATURE_SYSLOG
72//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020073//config: Enable mounting of NFS file systems on Linux kernels prior
74//config: to version 2.6.23. Note that in this case mounting of NFS
75//config: over IPv6 will not be possible.
Isaac Dunham7b434a62015-03-11 16:07:24 +010076//config:
Denys Vlasenko72089cf2017-07-21 09:50:55 +020077//config: Note that this option links in RPC support from libc,
78//config: which is rather large (~10 kbytes on uclibc).
Isaac Dunham7b434a62015-03-11 16:07:24 +010079//config:
80//config:config FEATURE_MOUNT_CIFS
81//config: bool "Support mounting CIFS/SMB file systems"
82//config: default y
83//config: depends on MOUNT
84//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020085//config: Enable support for samba mounts.
Isaac Dunham7b434a62015-03-11 16:07:24 +010086//config:
87//config:config FEATURE_MOUNT_FLAGS
88//config: depends on MOUNT
Denys Vlasenkof5604222017-01-10 14:58:54 +010089//config: bool "Support lots of -o flags"
Isaac Dunham7b434a62015-03-11 16:07:24 +010090//config: default y
91//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020092//config: Without this, mount only supports ro/rw/remount. With this, it
93//config: supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime,
94//config: noatime, diratime, nodiratime, loud, bind, move, shared, slave,
95//config: private, unbindable, rshared, rslave, rprivate, and runbindable.
Isaac Dunham7b434a62015-03-11 16:07:24 +010096//config:
97//config:config FEATURE_MOUNT_FSTAB
98//config: depends on MOUNT
Denys Vlasenko5b3cbe32017-07-27 14:45:25 +020099//config: bool "Support /etc/fstab and -a (mount all)"
Isaac Dunham7b434a62015-03-11 16:07:24 +0100100//config: default y
101//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200102//config: Support mount all and looking for files in /etc/fstab.
Isaac Dunham7b434a62015-03-11 16:07:24 +0100103//config:
104//config:config FEATURE_MOUNT_OTHERTAB
105//config: depends on FEATURE_MOUNT_FSTAB
106//config: bool "Support -T <alt_fstab>"
107//config: default y
108//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200109//config: Support mount -T (specifying an alternate fstab)
Isaac Dunham7b434a62015-03-11 16:07:24 +0100110
Denys Vlasenkodd898c92016-11-23 11:46:32 +0100111/* On full-blown systems, requires suid for user mounts.
112 * But it's not unthinkable to have it available in non-suid flavor on some systems,
113 * for viewing mount table.
114 * Therefore we use BB_SUID_MAYBE instead of BB_SUID_REQUIRE: */
115//applet:IF_MOUNT(APPLET(mount, BB_DIR_BIN, IF_DESKTOP(BB_SUID_MAYBE) IF_NOT_DESKTOP(BB_SUID_DROP)))
116
117//kbuild:lib-$(CONFIG_MOUNT) += mount.o
118
Pere Orga5bc8c002011-04-11 03:29:49 +0200119//usage:#define mount_trivial_usage
Isaac Dunham7b434a62015-03-11 16:07:24 +0100120//usage: "[OPTIONS] [-o OPT] DEVICE NODE"
Pere Orga5bc8c002011-04-11 03:29:49 +0200121//usage:#define mount_full_usage "\n\n"
122//usage: "Mount a filesystem. Filesystem autodetection requires /proc.\n"
Pere Orga5bc8c002011-04-11 03:29:49 +0200123//usage: "\n -a Mount all filesystems in fstab"
124//usage: IF_FEATURE_MOUNT_FAKE(
125//usage: IF_FEATURE_MTAB_SUPPORT(
126//usage: "\n -f Update /etc/mtab, but don't mount"
127//usage: )
128//usage: IF_NOT_FEATURE_MTAB_SUPPORT(
129//usage: "\n -f Dry run"
130//usage: )
131//usage: )
132//usage: IF_FEATURE_MOUNT_HELPERS(
133//usage: "\n -i Don't run mount helper"
134//usage: )
135//usage: IF_FEATURE_MTAB_SUPPORT(
136//usage: "\n -n Don't update /etc/mtab"
137//usage: )
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100138//usage: IF_FEATURE_MOUNT_VERBOSE(
139//usage: "\n -v Verbose"
140//usage: )
141////usage: "\n -s Sloppy (ignored)"
Pere Orga5bc8c002011-04-11 03:29:49 +0200142//usage: "\n -r Read-only mount"
Isaac Dunham7b434a62015-03-11 16:07:24 +0100143////usage: "\n -w Read-write mount (default)"
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +0100144//usage: "\n -t FSTYPE[,...] Filesystem type(s)"
Isaac Dunham7b434a62015-03-11 16:07:24 +0100145//usage: IF_FEATURE_MOUNT_OTHERTAB(
146//usage: "\n -T FILE Read FILE instead of /etc/fstab"
147//usage: )
Pere Orga5bc8c002011-04-11 03:29:49 +0200148//usage: "\n -O OPT Mount only filesystems with option OPT (-a only)"
149//usage: "\n-o OPT:"
150//usage: IF_FEATURE_MOUNT_LOOP(
151//usage: "\n loop Ignored (loop devices are autodetected)"
152//usage: )
153//usage: IF_FEATURE_MOUNT_FLAGS(
154//usage: "\n [a]sync Writes are [a]synchronous"
155//usage: "\n [no]atime Disable/enable updates to inode access times"
156//usage: "\n [no]diratime Disable/enable atime updates to directories"
157//usage: "\n [no]relatime Disable/enable atime updates relative to modification time"
158//usage: "\n [no]dev (Dis)allow use of special device files"
159//usage: "\n [no]exec (Dis)allow use of executable files"
160//usage: "\n [no]suid (Dis)allow set-user-id-root programs"
161//usage: "\n [r]shared Convert [recursively] to a shared subtree"
162//usage: "\n [r]slave Convert [recursively] to a slave subtree"
163//usage: "\n [r]private Convert [recursively] to a private subtree"
164//usage: "\n [un]bindable Make mount point [un]able to be bind mounted"
165//usage: "\n [r]bind Bind a file or directory [recursively] to another location"
166//usage: "\n move Relocate an existing mount point"
167//usage: )
168//usage: "\n remount Remount a mounted filesystem, changing flags"
Isaac Dunham7b434a62015-03-11 16:07:24 +0100169//usage: "\n ro Same as -r"
Pere Orga5bc8c002011-04-11 03:29:49 +0200170//usage: "\n"
171//usage: "\nThere are filesystem-specific -o flags."
172//usage:
173//usage:#define mount_example_usage
174//usage: "$ mount\n"
175//usage: "/dev/hda3 on / type minix (rw)\n"
176//usage: "proc on /proc type proc (rw)\n"
177//usage: "devpts on /dev/pts type devpts (rw)\n"
178//usage: "$ mount /dev/fd0 /mnt -t msdos -o ro\n"
179//usage: "$ mount /tmp/diskimage /opt -t ext2 -o loop\n"
180//usage: "$ mount cd_image.iso mydir\n"
181//usage:#define mount_notes_usage
182//usage: "Returns 0 for success, number of failed mounts for -a, or errno for one mount."
183
Eric Andersencc8ed391999-10-05 16:24:54 +0000184#include <mntent.h>
Lauri Kasanen6cfec7d2020-12-21 18:55:59 +0200185#if ENABLE_FEATURE_SYSLOG
Bernhard Reutner-Fischerf4701962008-01-27 12:50:12 +0000186#include <syslog.h>
Lauri Kasanen6cfec7d2020-12-21 18:55:59 +0200187#endif
Denys Vlasenkoda49f582009-07-08 02:58:38 +0200188#include <sys/mount.h>
Denys Vlasenko102ff762009-11-21 17:14:08 +0100189// Grab more as needed from util-linux's mount/mount_constants.h
190#ifndef MS_DIRSYNC
191# define MS_DIRSYNC (1 << 7) // Directory modifications are synchronous
192#endif
Denys Vlasenko34c51152020-12-06 21:47:24 +0100193#ifndef MS_NOSYMFOLLOW
194# define MS_NOSYMFOLLOW (1 << 8)
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200195#endif
Denys Vlasenkoda49f582009-07-08 02:58:38 +0200196#ifndef MS_BIND
197# define MS_BIND (1 << 12)
198#endif
199#ifndef MS_MOVE
200# define MS_MOVE (1 << 13)
201#endif
202#ifndef MS_RECURSIVE
203# define MS_RECURSIVE (1 << 14)
204#endif
205#ifndef MS_SILENT
206# define MS_SILENT (1 << 15)
207#endif
Denys Vlasenko102ff762009-11-21 17:14:08 +0100208// The shared subtree stuff, which went in around 2.6.15
Denys Vlasenkoda49f582009-07-08 02:58:38 +0200209#ifndef MS_UNBINDABLE
210# define MS_UNBINDABLE (1 << 17)
211#endif
212#ifndef MS_PRIVATE
213# define MS_PRIVATE (1 << 18)
214#endif
215#ifndef MS_SLAVE
216# define MS_SLAVE (1 << 19)
217#endif
218#ifndef MS_SHARED
219# define MS_SHARED (1 << 20)
220#endif
Denys Vlasenko696c38d2020-12-06 20:59:41 +0100221
Denys Vlasenkoda49f582009-07-08 02:58:38 +0200222#ifndef MS_RELATIME
223# define MS_RELATIME (1 << 21)
224#endif
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200225#ifndef MS_STRICTATIME
226# define MS_STRICTATIME (1 << 24)
227#endif
Denys Vlasenko696c38d2020-12-06 20:59:41 +0100228#ifndef MS_LAZYTIME
229# define MS_LAZYTIME (1 << 25)
230#endif
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200231
232/* Any ~MS_FOO value has this bit set: */
233#define BB_MS_INVERTED_VALUE (1u << 31)
Eric Andersenbd22ed82000-07-08 18:55:24 +0000234
Denys Vlasenko102ff762009-11-21 17:14:08 +0100235#include "libbb.h"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200236#include "common_bufsiz.h"
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000237#if ENABLE_FEATURE_MOUNT_LABEL
Natanael Copa9aff2992009-09-20 04:28:22 +0200238# include "volume_id.h"
239#else
240# define resolve_mount_spec(fsname) ((void)0)
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000241#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000242
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000243// Needed for nfs support only
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000244#include <sys/utsname.h>
245#undef TRUE
246#undef FALSE
Denys Vlasenkocc428142009-12-16 02:06:56 +0100247#if ENABLE_FEATURE_MOUNT_NFS
248/* This is just a warning of a common mistake. Possibly this should be a
249 * uclibc faq entry rather than in busybox... */
250# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
Denys Vlasenkoce552842017-07-10 14:43:22 +0200251# warning "You probably need to build uClibc with UCLIBC_HAS_RPC for NFS support"
Denys Vlasenko4892f3a2018-02-13 18:20:28 +0100252 /* not #error, since user may be using e.g. libtirpc instead.
253 * This might work:
254 * CONFIG_EXTRA_CFLAGS="-I/usr/include/tirpc"
255 * CONFIG_EXTRA_LDLIBS="tirpc"
256 */
Denys Vlasenkocc428142009-12-16 02:06:56 +0100257# endif
258# include <rpc/rpc.h>
259# include <rpc/pmap_prot.h>
260# include <rpc/pmap_clnt.h>
261#endif
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000262
263
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000264#if defined(__dietlibc__)
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000265// 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
266// dietlibc-0.30 does not have implementation of getmntent_r()
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +0000267static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000268 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000269{
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000270 struct mntent* ment = getmntent(stream);
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +0000271 return memcpy(result, ment, sizeof(*ment));
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000272}
273#endif
274
275
Rob Landleydc0955b2006-03-14 18:16:25 +0000276// Not real flags, but we want to be able to check for this.
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000277enum {
Denys Vlasenko911d2652015-12-30 20:11:34 +0100278 MOUNT_USERS = (1 << 27) * ENABLE_DESKTOP,
279 MOUNT_NOFAIL = (1 << 28) * ENABLE_DESKTOP,
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000280 MOUNT_NOAUTO = (1 << 29),
281 MOUNT_SWAP = (1 << 30),
Denys Vlasenko911d2652015-12-30 20:11:34 +0100282 MOUNT_FAKEFLAGS = MOUNT_USERS | MOUNT_NOFAIL | MOUNT_NOAUTO | MOUNT_SWAP
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000283};
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000284
285
Denys Vlasenko237bedd2016-07-06 21:58:02 +0200286#define OPTION_STR "o:*t:rwanfvsiO:" IF_FEATURE_MOUNT_OTHERTAB("T:")
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000287enum {
288 OPT_o = (1 << 0),
289 OPT_t = (1 << 1),
290 OPT_r = (1 << 2),
291 OPT_w = (1 << 3),
292 OPT_a = (1 << 4),
293 OPT_n = (1 << 5),
294 OPT_f = (1 << 6),
295 OPT_v = (1 << 7),
296 OPT_s = (1 << 8),
297 OPT_i = (1 << 9),
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +0000298 OPT_O = (1 << 10),
Isaac Dunham7b434a62015-03-11 16:07:24 +0100299 OPT_T = (1 << 11),
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000300};
301
302#if ENABLE_FEATURE_MTAB_SUPPORT
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200303#define USE_MTAB (!(option_mask32 & OPT_n))
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000304#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200305#define USE_MTAB 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000306#endif
307
308#if ENABLE_FEATURE_MOUNT_FAKE
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200309#define FAKE_IT (option_mask32 & OPT_f)
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000310#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200311#define FAKE_IT 0
312#endif
313
314#if ENABLE_FEATURE_MOUNT_HELPERS
315#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
316#else
317#define HELPERS_ALLOWED 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000318#endif
319
320
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000321// TODO: more "user" flag compatibility.
322// "user" option (from mount manpage):
323// Only the user that mounted a filesystem can unmount it again.
324// If any user should be able to unmount, then use users instead of user
325// in the fstab line. The owner option is similar to the user option,
326// with the restriction that the user must be the owner of the special file.
327// This may be useful e.g. for /dev/fd if a login script makes
328// the console user owner of this device.
Rob Landley3ba7bd12006-08-09 19:51:13 +0000329
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000330// Standard mount options (from -o options or --options),
331// with corresponding flags
Denys Vlasenko965b7952020-11-30 13:03:03 +0100332static const int32_t mount_options[] ALIGN4 = {
Rob Landleye3781b72006-08-08 01:39:49 +0000333 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
Rob Landleydc0955b2006-03-14 18:16:25 +0000334
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000335 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000336 /* "loop" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000337 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000338
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000339 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000340 /* "defaults" */ 0,
341 /* "quiet" 0 - do not filter out, vfat wants to see it */
342 /* "noauto" */ MOUNT_NOAUTO,
343 /* "sw" */ MOUNT_SWAP,
344 /* "swap" */ MOUNT_SWAP,
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000345 IF_DESKTOP(/* "user" */ MOUNT_USERS,)
346 IF_DESKTOP(/* "users" */ MOUNT_USERS,)
Denys Vlasenko911d2652015-12-30 20:11:34 +0100347 IF_DESKTOP(/* "nofail" */ MOUNT_NOFAIL,)
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000348 /* "_netdev" */ 0,
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100349 IF_DESKTOP(/* "comment=" */ 0,) /* systemd uses this in fstab */
Rob Landleye3781b72006-08-08 01:39:49 +0000350 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000351
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000352 IF_FEATURE_MOUNT_FLAGS(
Rob Landleye3781b72006-08-08 01:39:49 +0000353 // vfs flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000354 /* "nosuid" */ MS_NOSUID,
355 /* "suid" */ ~MS_NOSUID,
356 /* "dev" */ ~MS_NODEV,
357 /* "nodev" */ MS_NODEV,
358 /* "exec" */ ~MS_NOEXEC,
359 /* "noexec" */ MS_NOEXEC,
360 /* "sync" */ MS_SYNCHRONOUS,
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +0000361 /* "dirsync" */ MS_DIRSYNC,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000362 /* "async" */ ~MS_SYNCHRONOUS,
363 /* "atime" */ ~MS_NOATIME,
364 /* "noatime" */ MS_NOATIME,
365 /* "diratime" */ ~MS_NODIRATIME,
366 /* "nodiratime" */ MS_NODIRATIME,
Bernhard Reutner-Fischerfb5902c2008-08-06 18:14:38 +0000367 /* "relatime" */ MS_RELATIME,
368 /* "norelatime" */ ~MS_RELATIME,
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200369 /* "strictatime" */ MS_STRICTATIME,
Denys Vlasenko696c38d2020-12-06 20:59:41 +0100370 /* "nostrictatime"*/ ~MS_STRICTATIME,
371 /* "lazytime" */ MS_LAZYTIME,
372 /* "nolazytime" */ ~MS_LAZYTIME,
Denys Vlasenko34c51152020-12-06 21:47:24 +0100373 /* "nosymfollow" */ MS_NOSYMFOLLOW,
Denys Vlasenko696c38d2020-12-06 20:59:41 +0100374 /* "mand" */ MS_MANDLOCK,
375 /* "nomand" */ ~MS_MANDLOCK,
376 /* "loud" */ ~MS_SILENT,
Eric Andersen9601a1c2006-03-20 18:07:50 +0000377
Rob Landleye3781b72006-08-08 01:39:49 +0000378 // action flags
Denys Vlasenko696c38d2020-12-06 20:59:41 +0100379 /* "rbind" */ MS_BIND|MS_RECURSIVE,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000380 /* "bind" */ MS_BIND,
381 /* "move" */ MS_MOVE,
382 /* "shared" */ MS_SHARED,
383 /* "slave" */ MS_SLAVE,
384 /* "private" */ MS_PRIVATE,
385 /* "unbindable" */ MS_UNBINDABLE,
386 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
387 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
Roman Borisovd3679d22011-03-23 11:20:25 +0300388 /* "rprivate" */ MS_PRIVATE|MS_RECURSIVE,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000389 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
Rob Landleye3781b72006-08-08 01:39:49 +0000390 )
391
392 // Always understood.
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000393 /* "ro" */ MS_RDONLY, // vfs flag
394 /* "rw" */ ~MS_RDONLY, // vfs flag
395 /* "remount" */ MS_REMOUNT // action flag
Eric Andersencc8ed391999-10-05 16:24:54 +0000396};
397
Denys Vlasenko3e134eb2016-04-22 18:09:21 +0200398static const char mount_option_str[] ALIGN1 =
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000399 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000400 "loop\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000401 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000402 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000403 "defaults\0"
404 // "quiet\0" - do not filter out, vfat wants to see it
405 "noauto\0"
406 "sw\0"
407 "swap\0"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000408 IF_DESKTOP("user\0")
409 IF_DESKTOP("users\0")
Denys Vlasenko911d2652015-12-30 20:11:34 +0100410 IF_DESKTOP("nofail\0")
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000411 "_netdev\0"
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100412 IF_DESKTOP("comment=\0") /* systemd uses this in fstab */
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000413 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000414 IF_FEATURE_MOUNT_FLAGS(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000415 // vfs flags
Denys Vlasenko696c38d2020-12-06 20:59:41 +0100416 "nosuid" "\0"
417 "suid" "\0"
418 "dev" "\0"
419 "nodev" "\0"
420 "exec" "\0"
421 "noexec" "\0"
422 "sync" "\0"
423 "dirsync" "\0"
424 "async" "\0"
425 "atime" "\0"
426 "noatime" "\0"
427 "diratime" "\0"
428 "nodiratime" "\0"
429 "relatime" "\0"
430 "norelatime" "\0"
431 "strictatime" "\0"
432 "nostrictatime""\0"
433 "lazytime" "\0"
434 "nolazytime" "\0"
Denys Vlasenko34c51152020-12-06 21:47:24 +0100435 "nosymfollow" "\0"
Denys Vlasenko696c38d2020-12-06 20:59:41 +0100436 "mand" "\0"
437 "nomand" "\0"
438 "loud" "\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000439
440 // action flags
Denys Vlasenko696c38d2020-12-06 20:59:41 +0100441 "rbind\0"
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000442 "bind\0"
443 "move\0"
Roman Borisov945fd172011-02-25 14:50:39 +0300444 "make-shared\0"
445 "make-slave\0"
446 "make-private\0"
447 "make-unbindable\0"
448 "make-rshared\0"
449 "make-rslave\0"
450 "make-rprivate\0"
451 "make-runbindable\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000452 )
453
454 // Always understood.
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000455 "ro\0" // vfs flag
456 "rw\0" // vfs flag
457 "remount\0" // action flag
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000458;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000459
Denis Vlasenkof732e962008-02-18 12:07:49 +0000460
461struct globals {
462#if ENABLE_FEATURE_MOUNT_NFS
463 smalluint nfs_mount_version;
464#endif
465#if ENABLE_FEATURE_MOUNT_VERBOSE
466 unsigned verbose;
467#endif
468 llist_t *fslist;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000469 char getmntent_buf[1];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100470} FIX_ALIASING;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000471enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200472#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenkof732e962008-02-18 12:07:49 +0000473#define nfs_mount_version (G.nfs_mount_version)
Denis Vlasenkob4133682008-02-18 13:05:38 +0000474#if ENABLE_FEATURE_MOUNT_VERBOSE
Denis Vlasenkof732e962008-02-18 12:07:49 +0000475#define verbose (G.verbose )
Denis Vlasenkob4133682008-02-18 13:05:38 +0000476#else
477#define verbose 0
478#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000479#define fslist (G.fslist )
480#define getmntent_buf (G.getmntent_buf )
Denys Vlasenko47cfbf32016-04-21 18:18:48 +0200481#define INIT_G() do { setup_common_bufsiz(); } while (0)
Denis Vlasenkof732e962008-02-18 12:07:49 +0000482
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100483#if ENABLE_FEATURE_MTAB_SUPPORT
484/*
485 * update_mtab_entry_on_move() is used to update entry in case of mount --move.
486 * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
487 * input mntent and replace it by new one.
488 */
489static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
490{
491 struct mntent *entries, *m;
492 int i, count;
493 FILE *mountTable;
494
495 mountTable = setmntent(bb_path_mtab_file, "r");
496 if (!mountTable) {
James Byrne69374872019-07-02 11:35:03 +0200497 bb_simple_perror_msg(bb_path_mtab_file);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100498 return;
499 }
500
501 entries = NULL;
502 count = 0;
503 while ((m = getmntent(mountTable)) != NULL) {
504 entries = xrealloc_vector(entries, 3, count);
505 entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
506 entries[count].mnt_dir = xstrdup(m->mnt_dir);
507 entries[count].mnt_type = xstrdup(m->mnt_type);
508 entries[count].mnt_opts = xstrdup(m->mnt_opts);
509 entries[count].mnt_freq = m->mnt_freq;
510 entries[count].mnt_passno = m->mnt_passno;
511 count++;
512 }
513 endmntent(mountTable);
514
515 mountTable = setmntent(bb_path_mtab_file, "w");
516 if (mountTable) {
517 for (i = 0; i < count; i++) {
518 if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
519 addmntent(mountTable, &entries[i]);
520 else
521 addmntent(mountTable, mp);
522 }
523 endmntent(mountTable);
524 } else if (errno != EROFS)
James Byrne69374872019-07-02 11:35:03 +0200525 bb_simple_perror_msg(bb_path_mtab_file);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100526
527 if (ENABLE_FEATURE_CLEAN_UP) {
528 for (i = 0; i < count; i++) {
529 free(entries[i].mnt_fsname);
530 free(entries[i].mnt_dir);
531 free(entries[i].mnt_type);
532 free(entries[i].mnt_opts);
533 }
534 free(entries);
535 }
536}
537#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000538
539#if ENABLE_FEATURE_MOUNT_VERBOSE
540static int verbose_mount(const char *source, const char *target,
541 const char *filesystemtype,
542 unsigned long mountflags, const void *data)
543{
544 int rc;
545
546 errno = 0;
547 rc = mount(source, target, filesystemtype, mountflags, data);
548 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000549 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000550 source, target, filesystemtype,
551 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000552 return rc;
553}
554#else
555#define verbose_mount(...) mount(__VA_ARGS__)
556#endif
557
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000558// Append mount options to string
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000559static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000560{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000561 if (*oldopts && **oldopts) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000562 // Do not insert options which are already there
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000563 while (newopts[0]) {
564 char *p;
565 int len = strlen(newopts);
566 p = strchr(newopts, ',');
567 if (p) len = p - newopts;
568 p = *oldopts;
569 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000570 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000571 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000572 goto skip;
573 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000574 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000575 p++;
576 }
577 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
578 free(*oldopts);
579 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000580 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000581 newopts += len;
582 while (newopts[0] == ',') newopts++;
583 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000584 } else {
585 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000586 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000587 }
588}
589
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000590// Use the mount_options list to parse options into flags.
Alexander Shishkin77650952010-10-28 06:10:03 +0200591// Also update list of unrecognized options if unrecognized != NULL
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200592static unsigned long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000593{
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200594 unsigned long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000595
Rob Landley6a6798b2005-08-10 20:35:54 +0000596 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000597 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000598 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000599 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000600 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000601
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000602 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000603
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000604// FIXME: use hasmntopt()
Rob Landley6a6798b2005-08-10 20:35:54 +0000605 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000606 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200607 unsigned opt_len = strlen(option_str);
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100608
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200609 if (strncasecmp(option_str, options, opt_len) == 0
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100610 && (options[opt_len] == '\0'
611 /* or is it "comment=" thingy in fstab? */
612 IF_FEATURE_MOUNT_FSTAB(IF_DESKTOP( || option_str[opt_len-1] == '=' ))
613 )
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200614 ) {
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200615 unsigned long fl = mount_options[i];
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200616 if (fl & BB_MS_INVERTED_VALUE)
Alexander Shishkin77650952010-10-28 06:10:03 +0200617 flags &= fl;
618 else
619 flags |= fl;
620 goto found;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000621 }
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200622 option_str += opt_len + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000623 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200624 // We did not recognize this option.
625 // If "unrecognized" is not NULL, append option there.
626 // Note that we should not append *empty* option -
627 // in this case we want to pass NULL, not "", to "data"
628 // parameter of mount(2) syscall.
629 // This is crucial for filesystems that don't accept
630 // any arbitrary mount options, like cgroup fs:
631 // "mount -t cgroup none /mnt"
632 if (options[0] && unrecognized) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000633 // Add it to strflags, to pass on to kernel
Alexander Shishkin77650952010-10-28 06:10:03 +0200634 char *p = *unrecognized;
635 unsigned len = p ? strlen(p) : 0;
636 *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000637
Rob Landley6a6798b2005-08-10 20:35:54 +0000638 // Comma separated if it's not the first one
Alexander Shishkin77650952010-10-28 06:10:03 +0200639 if (len) p[len++] = ',';
640 strcpy(p + len, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000641 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200642 found:
Denis Vlasenko2535f122007-09-15 13:28:30 +0000643 if (!comma)
644 break;
645 // Advance to next option
646 *comma = ',';
647 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000648 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000649
Rob Landleydc0955b2006-03-14 18:16:25 +0000650 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000651}
652
Rob Landleydc0955b2006-03-14 18:16:25 +0000653// Return a list of all block device backed filesystems
Rob Landleydc0955b2006-03-14 18:16:25 +0000654static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000655{
Denys Vlasenko6599e382020-11-30 15:10:43 +0100656 static const char filesystems[2][sizeof("/proc/filesystems")] ALIGN1 = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000657 "/etc/filesystems",
658 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000659 };
660 char *fs, *buf;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200661 llist_t *list = NULL;
Rob Landleydc0955b2006-03-14 18:16:25 +0000662 int i;
663 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000664
Denis Vlasenko87468852007-04-13 23:22:00 +0000665 for (i = 0; i < 2; i++) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000666 f = fopen_for_read(filesystems[i]);
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000667 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000668
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000669 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denys Vlasenko8dff01d2015-03-12 17:48:34 +0100670 if (is_prefixed_with(buf, "nodev") && isspace(buf[5]))
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100671 goto next;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000672 fs = skip_whitespace(buf);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200673 if (*fs == '#' || *fs == '*' || !*fs)
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100674 goto next;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000675
Denis Vlasenko372686b2006-10-12 22:42:33 +0000676 llist_add_to_end(&list, xstrdup(fs));
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100677 next:
Denis Vlasenko372686b2006-10-12 22:42:33 +0000678 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000679 }
680 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
681 }
682
683 return list;
684}
685
Rob Landleydc0955b2006-03-14 18:16:25 +0000686#if ENABLE_FEATURE_CLEAN_UP
687static void delete_block_backed_filesystems(void)
688{
Rob Landleya6b5b602006-05-08 19:03:07 +0000689 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000690}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000691#else
692void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000693#endif
694
Rob Landleydc0955b2006-03-14 18:16:25 +0000695// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000696// NB: mp->xxx fields may be trashed on exit
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200697static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000698{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000699 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000700
Denys Vlasenko911d2652015-12-30 20:11:34 +0100701 vfsflags &= ~(unsigned long)MOUNT_FAKEFLAGS;
702
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200703 if (FAKE_IT) {
Denis Vlasenkob4133682008-02-18 13:05:38 +0000704 if (verbose >= 2)
705 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
706 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
707 vfsflags, filteropts);
708 goto mtab;
709 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000710
Rob Landleydc0955b2006-03-14 18:16:25 +0000711 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000712 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000713 errno = 0;
714 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000715 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000716
717 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000718 // helper program mount.<mnt_type>
Denys Vlasenkoba986032009-09-15 23:00:09 +0200719 if (HELPERS_ALLOWED && rc && mp->mnt_type) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200720 char *args[8];
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000721 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000722 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000723 rc = 1;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200724 if (FAKE_IT)
725 args[rc++] = (char *)"-f";
726 if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
727 args[rc++] = (char *)"-n";
Denis Vlasenko5c329932009-04-12 12:16:21 +0000728 args[rc++] = mp->mnt_fsname;
729 args[rc++] = mp->mnt_dir;
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000730 if (filteropts) {
731 args[rc++] = (char *)"-o";
732 args[rc++] = filteropts;
733 }
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000734 args[rc] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +0100735 rc = spawn_and_wait(args);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000736 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000737 if (!rc)
738 break;
739 errno = errno_save;
740 }
741
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000742 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000743 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000744 if (!(vfsflags & MS_SILENT))
745 bb_error_msg("%s is write-protected, mounting read-only",
746 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000747 vfsflags |= MS_RDONLY;
748 }
749
Rob Landleydc0955b2006-03-14 18:16:25 +0000750 // Abort entirely if permission denied.
751
752 if (rc && errno == EPERM)
James Byrne69374872019-07-02 11:35:03 +0200753 bb_simple_error_msg_and_die(bb_msg_perm_denied_are_you_root);
Rob Landleydc0955b2006-03-14 18:16:25 +0000754
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000755 // If the mount was successful, and we're maintaining an old-style
756 // mtab file by hand, add the new entry to it now.
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000757 mtab:
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200758 if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000759 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000760 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000761 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000762 int i;
763
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000764 if (!mountTable) {
James Byrne69374872019-07-02 11:35:03 +0200765 bb_simple_perror_msg(bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000766 goto ret;
767 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000768
769 // Add vfs string flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000770 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
771 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
772 append_mount_options(&(mp->mnt_opts), option_str);
773 option_str += strlen(option_str) + 1;
774 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000775
776 // Remove trailing / (if any) from directory we mounted on
Denis Vlasenko727ef942006-09-14 13:19:19 +0000777 i = strlen(mp->mnt_dir) - 1;
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100778 while (i > 0 && mp->mnt_dir[i] == '/')
Denys Vlasenkoc6450c92011-02-28 11:09:49 +0100779 mp->mnt_dir[i--] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000780
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000781 // Convert to canonical pathnames as needed
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000782 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100783 fsname = NULL;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000784 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000785 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000786 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000787 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000788 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000789
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100790 // Write and close
791#if ENABLE_FEATURE_MTAB_SUPPORT
792 if (vfsflags & MS_MOVE)
793 update_mtab_entry_on_move(mp);
794 else
795#endif
796 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000797 endmntent(mountTable);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100798
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000799 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000800 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000801 free(fsname);
802 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000803 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000804 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000805 return rc;
806}
807
Denis Vlasenko25098f72006-09-14 15:46:33 +0000808#if ENABLE_FEATURE_MOUNT_NFS
809
810/*
811 * Linux NFS mount
812 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
813 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +0200814 * Licensed under GPLv2, see file LICENSE in this source tree.
Denis Vlasenko25098f72006-09-14 15:46:33 +0000815 *
816 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
817 * numbers to be specified on the command line.
818 *
819 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
820 * Omit the call to connect() for Linux version 1.3.11 or later.
821 *
822 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
823 * Implemented the "bg", "fg" and "retry" mount options for NFS.
824 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000825 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000826 * - added Native Language Support
827 *
828 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
829 * plus NFSv3 stuff.
830 */
831
Denis Vlasenko25098f72006-09-14 15:46:33 +0000832#define MOUNTPORT 635
833#define MNTPATHLEN 1024
834#define MNTNAMLEN 255
835#define FHSIZE 32
836#define FHSIZE3 64
837
838typedef char fhandle[FHSIZE];
839
840typedef struct {
841 unsigned int fhandle3_len;
842 char *fhandle3_val;
843} fhandle3;
844
845enum mountstat3 {
846 MNT_OK = 0,
847 MNT3ERR_PERM = 1,
848 MNT3ERR_NOENT = 2,
849 MNT3ERR_IO = 5,
850 MNT3ERR_ACCES = 13,
851 MNT3ERR_NOTDIR = 20,
852 MNT3ERR_INVAL = 22,
853 MNT3ERR_NAMETOOLONG = 63,
854 MNT3ERR_NOTSUPP = 10004,
855 MNT3ERR_SERVERFAULT = 10006,
856};
857typedef enum mountstat3 mountstat3;
858
859struct fhstatus {
860 unsigned int fhs_status;
861 union {
862 fhandle fhs_fhandle;
863 } fhstatus_u;
864};
865typedef struct fhstatus fhstatus;
866
867struct mountres3_ok {
868 fhandle3 fhandle;
869 struct {
870 unsigned int auth_flavours_len;
871 char *auth_flavours_val;
872 } auth_flavours;
873};
874typedef struct mountres3_ok mountres3_ok;
875
876struct mountres3 {
877 mountstat3 fhs_status;
878 union {
879 mountres3_ok mountinfo;
880 } mountres3_u;
881};
882typedef struct mountres3 mountres3;
883
884typedef char *dirpath;
885
886typedef char *name;
887
888typedef struct mountbody *mountlist;
889
890struct mountbody {
891 name ml_hostname;
892 dirpath ml_directory;
893 mountlist ml_next;
894};
895typedef struct mountbody mountbody;
896
897typedef struct groupnode *groups;
898
899struct groupnode {
900 name gr_name;
901 groups gr_next;
902};
903typedef struct groupnode groupnode;
904
905typedef struct exportnode *exports;
906
907struct exportnode {
908 dirpath ex_dir;
909 groups ex_groups;
910 exports ex_next;
911};
912typedef struct exportnode exportnode;
913
914struct ppathcnf {
915 int pc_link_max;
916 short pc_max_canon;
917 short pc_max_input;
918 short pc_name_max;
919 short pc_path_max;
920 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000921 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000922 char pc_xxx;
923 short pc_mask[2];
924};
925typedef struct ppathcnf ppathcnf;
926
927#define MOUNTPROG 100005
928#define MOUNTVERS 1
929
930#define MOUNTPROC_NULL 0
931#define MOUNTPROC_MNT 1
932#define MOUNTPROC_DUMP 2
933#define MOUNTPROC_UMNT 3
934#define MOUNTPROC_UMNTALL 4
935#define MOUNTPROC_EXPORT 5
936#define MOUNTPROC_EXPORTALL 6
937
938#define MOUNTVERS_POSIX 2
939
940#define MOUNTPROC_PATHCONF 7
941
942#define MOUNT_V3 3
943
944#define MOUNTPROC3_NULL 0
945#define MOUNTPROC3_MNT 1
946#define MOUNTPROC3_DUMP 2
947#define MOUNTPROC3_UMNT 3
948#define MOUNTPROC3_UMNTALL 4
949#define MOUNTPROC3_EXPORT 5
950
951enum {
952#ifndef NFS_FHSIZE
953 NFS_FHSIZE = 32,
954#endif
955#ifndef NFS_PORT
956 NFS_PORT = 2049
957#endif
958};
959
Denis Vlasenko25098f72006-09-14 15:46:33 +0000960/*
961 * We want to be able to compile mount on old kernels in such a way
962 * that the binary will work well on more recent kernels.
963 * Thus, if necessary we teach nfsmount.c the structure of new fields
964 * that will come later.
965 *
966 * Moreover, the new kernel includes conflict with glibc includes
967 * so it is easiest to ignore the kernel altogether (at compile time).
968 */
969
970struct nfs2_fh {
Dave Lovefae473c2011-11-10 15:19:25 +0100971 char data[32];
Denis Vlasenko25098f72006-09-14 15:46:33 +0000972};
973struct nfs3_fh {
Dave Lovefae473c2011-11-10 15:19:25 +0100974 unsigned short size;
975 unsigned char data[64];
Denis Vlasenko25098f72006-09-14 15:46:33 +0000976};
977
978struct nfs_mount_data {
Dave Lovefae473c2011-11-10 15:19:25 +0100979 int version; /* 1 */
980 int fd; /* 1 */
981 struct nfs2_fh old_root; /* 1 */
982 int flags; /* 1 */
983 int rsize; /* 1 */
984 int wsize; /* 1 */
985 int timeo; /* 1 */
986 int retrans; /* 1 */
987 int acregmin; /* 1 */
988 int acregmax; /* 1 */
989 int acdirmin; /* 1 */
990 int acdirmax; /* 1 */
991 struct sockaddr_in addr; /* 1 */
992 char hostname[256]; /* 1 */
993 int namlen; /* 2 */
994 unsigned int bsize; /* 3 */
995 struct nfs3_fh root; /* 4 */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000996};
997
998/* bits in the flags field */
999enum {
1000 NFS_MOUNT_SOFT = 0x0001, /* 1 */
1001 NFS_MOUNT_INTR = 0x0002, /* 1 */
1002 NFS_MOUNT_SECURE = 0x0004, /* 1 */
1003 NFS_MOUNT_POSIX = 0x0008, /* 1 */
1004 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
1005 NFS_MOUNT_NOAC = 0x0020, /* 1 */
1006 NFS_MOUNT_TCP = 0x0040, /* 2 */
1007 NFS_MOUNT_VER3 = 0x0080, /* 3 */
1008 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001009 NFS_MOUNT_NONLM = 0x0200, /* 3 */
Dave Lovefae473c2011-11-10 15:19:25 +01001010 NFS_MOUNT_NOACL = 0x0800, /* 4 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001011 NFS_MOUNT_NORDIRPLUS = 0x4000
Denis Vlasenko25098f72006-09-14 15:46:33 +00001012};
1013
1014
1015/*
1016 * We need to translate between nfs status return values and
1017 * the local errno values which may not be the same.
1018 *
1019 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
1020 * "after #include <errno.h> the symbol errno is reserved for any use,
1021 * it cannot even be used as a struct tag or field name".
1022 */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001023#ifndef EDQUOT
Denys Vlasenkocc428142009-12-16 02:06:56 +01001024# define EDQUOT ENOSPC
Denis Vlasenko25098f72006-09-14 15:46:33 +00001025#endif
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001026/* Convert each NFSERR_BLAH into EBLAH */
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02001027static const uint8_t nfs_err_stat[] ALIGN1 = {
Denys Vlasenkocc428142009-12-16 02:06:56 +01001028 1, 2, 5, 6, 13, 17,
1029 19, 20, 21, 22, 27, 28,
1030 30, 63, 66, 69, 70, 71
1031};
Denys Vlasenkodc1fd2e2010-05-19 17:01:29 +02001032#if ( \
1033 EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \
1034 ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \
1035 EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
1036typedef uint8_t nfs_err_type;
1037#else
1038typedef uint16_t nfs_err_type;
1039#endif
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02001040static const nfs_err_type nfs_err_errnum[] ALIGN2 = {
Denys Vlasenkocc428142009-12-16 02:06:56 +01001041 EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST,
1042 ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC,
1043 EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
Denis Vlasenko25098f72006-09-14 15:46:33 +00001044};
Denis Vlasenko25098f72006-09-14 15:46:33 +00001045static char *nfs_strerror(int status)
1046{
1047 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001048
Denys Vlasenkocc428142009-12-16 02:06:56 +01001049 for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
1050 if (nfs_err_stat[i] == status)
1051 return strerror(nfs_err_errnum[i]);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001052 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001053 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001054}
1055
1056static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
1057{
Alexander Shishkin54779a42010-10-22 13:35:47 +02001058 return xdr_opaque(xdrs, objp, FHSIZE);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001059}
1060
1061static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
1062{
1063 if (!xdr_u_int(xdrs, &objp->fhs_status))
Denys Vlasenko69675782013-01-14 01:34:48 +01001064 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +02001065 if (objp->fhs_status == 0)
1066 return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001067 return TRUE;
1068}
1069
1070static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
1071{
Alexander Shishkin54779a42010-10-22 13:35:47 +02001072 return xdr_string(xdrs, objp, MNTPATHLEN);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001073}
1074
1075static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
1076{
Alexander Shishkin54779a42010-10-22 13:35:47 +02001077 return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
Denys Vlasenko69675782013-01-14 01:34:48 +01001078 (unsigned int *) &objp->fhandle3_len,
1079 FHSIZE3);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001080}
1081
1082static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
1083{
1084 if (!xdr_fhandle3(xdrs, &objp->fhandle))
1085 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +02001086 return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
Denys Vlasenko69675782013-01-14 01:34:48 +01001087 &(objp->auth_flavours.auth_flavours_len),
1088 ~0,
1089 sizeof(int),
1090 (xdrproc_t) xdr_int);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001091}
1092
1093static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
1094{
Alexander Shishkin54779a42010-10-22 13:35:47 +02001095 return xdr_enum(xdrs, (enum_t *) objp);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001096}
1097
1098static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
1099{
1100 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
1101 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +02001102 if (objp->fhs_status == MNT_OK)
1103 return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001104 return TRUE;
1105}
1106
1107#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
1108
Denis Vlasenko25098f72006-09-14 15:46:33 +00001109/*
1110 * Unfortunately, the kernel prints annoying console messages
1111 * in case of an unexpected nfs mount version (instead of
1112 * just returning some error). Therefore we'll have to try
1113 * and figure out what version the kernel expects.
1114 *
1115 * Variables:
1116 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
1117 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
1118 * nfs_mount_version: version this source and running kernel can handle
1119 */
1120static void
1121find_kernel_nfs_mount_version(void)
1122{
Denis Vlasenkob9256052007-09-28 10:29:17 +00001123 int kernel_version;
1124
1125 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001126 return;
1127
1128 nfs_mount_version = 4; /* default */
1129
1130 kernel_version = get_linux_version_code();
1131 if (kernel_version) {
Denys Vlasenkocc428142009-12-16 02:06:56 +01001132 if (kernel_version < KERNEL_VERSION(2,2,18))
Denis Vlasenko25098f72006-09-14 15:46:33 +00001133 nfs_mount_version = 3;
1134 /* else v4 since 2.3.99pre4 */
1135 }
1136}
1137
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +00001138static void
Denis Vlasenkob9256052007-09-28 10:29:17 +00001139get_mountport(struct pmap *pm_mnt,
1140 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001141 long unsigned prog,
1142 long unsigned version,
1143 long unsigned proto,
1144 long unsigned port)
1145{
1146 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001147
1148 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001149/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
1150 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001151 pmap = pmap_getmaps(server_addr);
1152
1153 if (version > MAX_NFSPROT)
1154 version = MAX_NFSPROT;
1155 if (!prog)
1156 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001157 pm_mnt->pm_prog = prog;
1158 pm_mnt->pm_vers = version;
1159 pm_mnt->pm_prot = proto;
1160 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001161
Denis Vlasenko25098f72006-09-14 15:46:33 +00001162 while (pmap) {
1163 if (pmap->pml_map.pm_prog != prog)
1164 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001165 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001166 goto next;
1167 if (version > 2 && pmap->pml_map.pm_vers != version)
1168 goto next;
1169 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1170 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001171 if (pmap->pml_map.pm_vers > MAX_NFSPROT
1172 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1173 || (port && pmap->pml_map.pm_port != port)
1174 ) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001175 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001176 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001177 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1178 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001179 pmap = pmap->pml_next;
1180 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001181 if (!pm_mnt->pm_vers)
1182 pm_mnt->pm_vers = MOUNTVERS;
1183 if (!pm_mnt->pm_port)
1184 pm_mnt->pm_port = MOUNTPORT;
1185 if (!pm_mnt->pm_prot)
1186 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001187}
1188
Denis Vlasenkof0000652007-09-04 18:30:26 +00001189#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001190static int daemonize(void)
1191{
Denis Vlasenko25098f72006-09-14 15:46:33 +00001192 int pid = fork();
1193 if (pid < 0) /* error */
1194 return -errno;
1195 if (pid > 0) /* parent */
1196 return 0;
1197 /* child */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001198 close(0);
1199 xopen(bb_dev_null, O_RDWR);
1200 xdup2(0, 1);
1201 xdup2(0, 2);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001202 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +00001203 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001204 logmode = LOGMODE_SYSLOG;
1205 return 1;
1206}
Denis Vlasenkof0000652007-09-04 18:30:26 +00001207#else
Denys Vlasenko8c317f02019-05-14 17:26:47 +02001208static inline int daemonize(void)
1209{
1210 return -ENOSYS;
1211}
Denis Vlasenkof0000652007-09-04 18:30:26 +00001212#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001213
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001214/* TODO */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001215static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001216{
1217 return 0;
1218}
1219
1220/* RPC strerror analogs are terminally idiotic:
1221 * *mandatory* prefix and \n at end.
1222 * This hopefully helps. Usage:
1223 * error_msg_rpc(clnt_*error*(" ")) */
1224static void error_msg_rpc(const char *msg)
1225{
Denis Vlasenko23514fe2006-09-19 14:07:52 +00001226 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001227 while (msg[0] == ' ' || msg[0] == ':') msg++;
1228 len = strlen(msg);
1229 while (len && msg[len-1] == '\n') len--;
1230 bb_error_msg("%.*s", len, msg);
1231}
1232
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001233/* NB: mp->xxx fields may be trashed on exit */
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001234static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001235{
1236 CLIENT *mclient;
1237 char *hostname;
1238 char *pathname;
1239 char *mounthost;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001240 /* prior to 2.6.23, kernel took NFS options in a form of this struct
1241 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1242 * then data pointer is interpreted as a string. */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001243 struct nfs_mount_data data;
1244 char *opt;
Denys Vlasenko24966162020-10-06 02:36:47 +02001245 char *tokstate;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001246 struct hostent *hp;
1247 struct sockaddr_in server_addr;
1248 struct sockaddr_in mount_server_addr;
1249 int msock, fsock;
1250 union {
1251 struct fhstatus nfsv2;
1252 struct mountres3 nfsv3;
1253 } status;
1254 int daemonized;
1255 char *s;
1256 int port;
1257 int mountport;
1258 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001259#if BB_MMU
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001260 smallint bg = 0;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001261#else
1262 enum { bg = 0 };
1263#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001264 int retry;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001265 int mountprog;
1266 int mountvers;
1267 int nfsprog;
1268 int nfsvers;
1269 int retval;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001270 /* these all are one-bit really. gcc 4.3.1 likes this combination: */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001271 smallint tcp;
1272 smallint soft;
1273 int intr;
1274 int posix;
1275 int nocto;
1276 int noac;
1277 int nordirplus;
1278 int nolock;
Dave Lovefae473c2011-11-10 15:19:25 +01001279 int noacl;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001280
1281 find_kernel_nfs_mount_version();
1282
1283 daemonized = 0;
1284 mounthost = NULL;
1285 retval = ETIMEDOUT;
1286 msock = fsock = -1;
1287 mclient = NULL;
1288
1289 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1290
1291 filteropts = xstrdup(filteropts); /* going to trash it later... */
1292
1293 hostname = xstrdup(mp->mnt_fsname);
1294 /* mount_main() guarantees that ':' is there */
1295 s = strchr(hostname, ':');
1296 pathname = s + 1;
1297 *s = '\0';
1298 /* Ignore all but first hostname in replicated mounts
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001299 * until they can be fully supported. (mack@sgi.com) */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001300 s = strchr(hostname, ',');
1301 if (s) {
1302 *s = '\0';
James Byrne69374872019-07-02 11:35:03 +02001303 bb_simple_error_msg("warning: multiple hostnames not supported");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001304 }
1305
1306 server_addr.sin_family = AF_INET;
1307 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1308 hp = gethostbyname(hostname);
1309 if (hp == NULL) {
James Byrne69374872019-07-02 11:35:03 +02001310 bb_simple_herror_msg(hostname);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001311 goto fail;
1312 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001313 if (hp->h_length != (int)sizeof(struct in_addr)) {
James Byrne69374872019-07-02 11:35:03 +02001314 bb_simple_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001315 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001316 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001317 }
1318
1319 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1320
1321 /* add IP address to mtab options for use when unmounting */
1322
1323 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1324 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1325 } else {
1326 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1327 mp->mnt_opts[0] ? "," : "",
1328 inet_ntoa(server_addr.sin_addr));
1329 free(mp->mnt_opts);
1330 mp->mnt_opts = tmp;
1331 }
1332
1333 /* Set default options.
1334 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1335 * let the kernel decide.
1336 * timeo is filled in after we know whether it'll be TCP or UDP. */
1337 memset(&data, 0, sizeof(data));
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001338 data.retrans = 3;
1339 data.acregmin = 3;
1340 data.acregmax = 60;
1341 data.acdirmin = 30;
1342 data.acdirmax = 60;
1343 data.namlen = NAME_MAX;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001344
Denis Vlasenko25098f72006-09-14 15:46:33 +00001345 soft = 0;
1346 intr = 0;
1347 posix = 0;
1348 nocto = 0;
1349 nolock = 0;
1350 noac = 0;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001351 nordirplus = 0;
Dave Lovefae473c2011-11-10 15:19:25 +01001352 noacl = 0;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001353 retry = 10000; /* 10000 minutes ~ 1 week */
Bernhard Reutner-Fischer88206292011-05-04 19:03:30 +02001354 tcp = 1; /* nfs-utils uses tcp per default */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001355
1356 mountprog = MOUNTPROG;
1357 mountvers = 0;
1358 port = 0;
1359 mountport = 0;
1360 nfsprog = 100003;
1361 nfsvers = 0;
1362
1363 /* parse options */
Denys Vlasenko24966162020-10-06 02:36:47 +02001364 if (filteropts) for (opt = strtok_r(filteropts, ",", &tokstate); opt; opt = strtok_r(NULL, ",", &tokstate)) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001365 char *opteq = strchr(opt, '=');
1366 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001367 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001368 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001369 /* 0 */ "rsize\0"
1370 /* 1 */ "wsize\0"
1371 /* 2 */ "timeo\0"
1372 /* 3 */ "retrans\0"
1373 /* 4 */ "acregmin\0"
1374 /* 5 */ "acregmax\0"
1375 /* 6 */ "acdirmin\0"
1376 /* 7 */ "acdirmax\0"
1377 /* 8 */ "actimeo\0"
1378 /* 9 */ "retry\0"
1379 /* 10 */ "port\0"
1380 /* 11 */ "mountport\0"
1381 /* 12 */ "mounthost\0"
1382 /* 13 */ "mountprog\0"
1383 /* 14 */ "mountvers\0"
1384 /* 15 */ "nfsprog\0"
1385 /* 16 */ "nfsvers\0"
1386 /* 17 */ "vers\0"
1387 /* 18 */ "proto\0"
1388 /* 19 */ "namlen\0"
1389 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001390
1391 *opteq++ = '\0';
1392 idx = index_in_strings(options, opt);
1393 switch (idx) {
1394 case 12: // "mounthost"
1395 mounthost = xstrndup(opteq,
1396 strcspn(opteq, " \t\n\r,"));
1397 continue;
1398 case 18: // "proto"
Denys Vlasenko8dff01d2015-03-12 17:48:34 +01001399 if (is_prefixed_with(opteq, "tcp"))
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001400 tcp = 1;
Denys Vlasenko8dff01d2015-03-12 17:48:34 +01001401 else if (is_prefixed_with(opteq, "udp"))
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001402 tcp = 0;
1403 else
James Byrne69374872019-07-02 11:35:03 +02001404 bb_simple_error_msg("warning: unrecognized proto= option");
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001405 continue;
1406 case 20: // "addr" - ignore
1407 continue;
Peter Korsgaard301fe502011-02-21 17:52:13 +01001408 case -1: // unknown
1409 if (vfsflags & MS_REMOUNT)
1410 continue;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001411 }
1412
Denys Vlasenko77832482010-08-12 14:14:45 +02001413 val = xatoi_positive(opteq);
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001414 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001415 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001416 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001417 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001418 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001419 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001420 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001421 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001422 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001423 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001424 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001425 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001426 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001427 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001428 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001429 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001430 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001431 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001432 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001433 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001434 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001435 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001436 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001437 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001438 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001439 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001440 data.acregmin = val;
1441 data.acregmax = val;
1442 data.acdirmin = val;
1443 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001444 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001445 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001446 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001447 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001448 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001449 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001450 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001451 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001452 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001453 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001454 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001455 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001456 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001457 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001458 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001459 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001460 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001461 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001462 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001463 case 16: // "nfsvers"
1464 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001465 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001466 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001467 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001468 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001469 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001470 //else
1471 // bb_error_msg("warning: option namlen is not supported\n");
1472 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001473 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001474 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1475 goto fail;
1476 }
1477 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001478 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001479 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001480 "bg\0"
1481 "fg\0"
1482 "soft\0"
1483 "hard\0"
1484 "intr\0"
1485 "posix\0"
1486 "cto\0"
1487 "ac\0"
1488 "tcp\0"
1489 "udp\0"
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001490 "lock\0"
Dave Lovefae473c2011-11-10 15:19:25 +01001491 "rdirplus\0"
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001492 "acl\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001493 int val = 1;
Denys Vlasenko8dff01d2015-03-12 17:48:34 +01001494 if (is_prefixed_with(opt, "no")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001495 val = 0;
1496 opt += 2;
1497 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001498 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001499 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001500#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001501 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001502#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001503 break;
1504 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001505#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001506 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001507#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001508 break;
1509 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001510 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001511 break;
1512 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001513 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001514 break;
1515 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001516 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001517 break;
1518 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001519 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001520 break;
1521 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001522 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001523 break;
1524 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001525 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001526 break;
1527 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001528 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001529 break;
1530 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001531 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001532 break;
1533 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001534 if (nfs_mount_version >= 3)
1535 nolock = !val;
1536 else
James Byrne69374872019-07-02 11:35:03 +02001537 bb_simple_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001538 break;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001539 case 11: //rdirplus
1540 nordirplus = !val;
1541 break;
Dave Lovefae473c2011-11-10 15:19:25 +01001542 case 12: // acl
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001543 noacl = !val;
Dave Lovefae473c2011-11-10 15:19:25 +01001544 break;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001545 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001546 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1547 goto fail;
1548 }
1549 }
1550 }
1551 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1552
1553 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1554 | (intr ? NFS_MOUNT_INTR : 0)
1555 | (posix ? NFS_MOUNT_POSIX : 0)
1556 | (nocto ? NFS_MOUNT_NOCTO : 0)
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001557 | (noac ? NFS_MOUNT_NOAC : 0)
Dave Lovefae473c2011-11-10 15:19:25 +01001558 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001559 | (noacl ? NFS_MOUNT_NOACL : 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001560 if (nfs_mount_version >= 2)
1561 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1562 if (nfs_mount_version >= 3)
1563 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1564 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1565 bb_error_msg("NFSv%d not supported", nfsvers);
1566 goto fail;
1567 }
1568 if (nfsvers && !mountvers)
1569 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1570 if (nfsvers && nfsvers < mountvers) {
1571 mountvers = nfsvers;
1572 }
1573
1574 /* Adjust options if none specified */
1575 if (!data.timeo)
1576 data.timeo = tcp ? 70 : 7;
1577
Denis Vlasenko25098f72006-09-14 15:46:33 +00001578 data.version = nfs_mount_version;
1579
1580 if (vfsflags & MS_REMOUNT)
1581 goto do_mount;
1582
1583 /*
1584 * If the previous mount operation on the same host was
1585 * backgrounded, and the "bg" for this mount is also set,
1586 * give up immediately, to avoid the initial timeout.
1587 */
1588 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001589 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001590 if (daemonized <= 0) { /* parent or error */
1591 retval = -daemonized;
1592 goto ret;
1593 }
1594 }
1595
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001596 /* Create mount daemon client */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001597 /* See if the nfs host = mount host. */
1598 if (mounthost) {
1599 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1600 mount_server_addr.sin_family = AF_INET;
1601 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1602 } else {
1603 hp = gethostbyname(mounthost);
1604 if (hp == NULL) {
James Byrne69374872019-07-02 11:35:03 +02001605 bb_simple_herror_msg(mounthost);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001606 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001607 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001608 if (hp->h_length != (int)sizeof(struct in_addr)) {
James Byrne69374872019-07-02 11:35:03 +02001609 bb_simple_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001610 }
1611 mount_server_addr.sin_family = AF_INET;
Denys Vlasenko99069332010-02-27 19:38:19 +01001612 memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001613 }
1614 }
1615
1616 /*
1617 * The following loop implements the mount retries. When the mount
1618 * times out, and the "bg" option is set, we background ourself
1619 * and continue trying.
1620 *
1621 * The case where the mount point is not present and the "bg"
1622 * option is set, is treated as a timeout. This is done to
1623 * support nested mounts.
1624 *
1625 * The "retry" count specified by the user is the number of
1626 * minutes to retry before giving up.
1627 */
1628 {
1629 struct timeval total_timeout;
1630 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001631 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001632 time_t t;
1633 time_t prevt;
1634 time_t timeout;
1635
1636 retry_timeout.tv_sec = 3;
1637 retry_timeout.tv_usec = 0;
1638 total_timeout.tv_sec = 20;
1639 total_timeout.tv_usec = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001640/* FIXME: use monotonic()? */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001641 timeout = time(NULL) + 60 * retry;
1642 prevt = 0;
1643 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001644 retry:
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001645 /* Be careful not to use too many CPU cycles */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001646 if (t - prevt < 30)
1647 sleep(30);
1648
Denis Vlasenkob9256052007-09-28 10:29:17 +00001649 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001650 mountprog,
1651 mountvers,
1652 proto,
1653 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001654 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001655
1656 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001657 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001658 msock = RPC_ANYSOCK;
1659
Denis Vlasenkob9256052007-09-28 10:29:17 +00001660 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001661 case IPPROTO_UDP:
1662 mclient = clntudp_create(&mount_server_addr,
Denys Vlasenko69675782013-01-14 01:34:48 +01001663 pm_mnt.pm_prog,
1664 pm_mnt.pm_vers,
1665 retry_timeout,
1666 &msock);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001667 if (mclient)
1668 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001669 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001670 msock = RPC_ANYSOCK;
1671 case IPPROTO_TCP:
1672 mclient = clnttcp_create(&mount_server_addr,
Denys Vlasenko69675782013-01-14 01:34:48 +01001673 pm_mnt.pm_prog,
1674 pm_mnt.pm_vers,
1675 &msock, 0, 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001676 break;
1677 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001678 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001679 }
1680 if (!mclient) {
1681 if (!daemonized && prevt == 0)
1682 error_msg_rpc(clnt_spcreateerror(" "));
1683 } else {
1684 enum clnt_stat clnt_stat;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001685
1686 /* Try to mount hostname:pathname */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001687 mclient->cl_auth = authunix_create_default();
1688
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001689 /* Make pointers in xdr_mountres3 NULL so
Denis Vlasenko25098f72006-09-14 15:46:33 +00001690 * that xdr_array allocates memory for us
1691 */
1692 memset(&status, 0, sizeof(status));
1693
Denis Vlasenkob9256052007-09-28 10:29:17 +00001694 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001695 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
Denys Vlasenko69675782013-01-14 01:34:48 +01001696 (xdrproc_t) xdr_dirpath,
1697 (caddr_t) &pathname,
1698 (xdrproc_t) xdr_mountres3,
1699 (caddr_t) &status,
1700 total_timeout);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001701 else
1702 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
Denys Vlasenko69675782013-01-14 01:34:48 +01001703 (xdrproc_t) xdr_dirpath,
1704 (caddr_t) &pathname,
1705 (xdrproc_t) xdr_fhstatus,
1706 (caddr_t) &status,
1707 total_timeout);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001708
1709 if (clnt_stat == RPC_SUCCESS)
1710 goto prepare_kernel_data; /* we're done */
1711 if (errno != ECONNREFUSED) {
1712 error_msg_rpc(clnt_sperror(mclient, " "));
1713 goto fail; /* don't retry */
1714 }
1715 /* Connection refused */
1716 if (!daemonized && prevt == 0) /* print just once */
1717 error_msg_rpc(clnt_sperror(mclient, " "));
1718 auth_destroy(mclient->cl_auth);
1719 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001720 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001721 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001722 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001723 }
1724
1725 /* Timeout. We are going to retry... maybe */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001726 if (!bg)
1727 goto fail;
1728 if (!daemonized) {
1729 daemonized = daemonize();
1730 if (daemonized <= 0) { /* parent or error */
1731 retval = -daemonized;
1732 goto ret;
1733 }
1734 }
1735 prevt = t;
1736 t = time(NULL);
1737 if (t >= timeout)
1738 /* TODO error message */
1739 goto fail;
1740
1741 goto retry;
1742 }
1743
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001744 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001745
1746 if (nfsvers == 2) {
1747 if (status.nfsv2.fhs_status != 0) {
1748 bb_error_msg("%s:%s failed, reason given by server: %s",
1749 hostname, pathname,
1750 nfs_strerror(status.nfsv2.fhs_status));
1751 goto fail;
1752 }
1753 memcpy(data.root.data,
1754 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1755 NFS_FHSIZE);
1756 data.root.size = NFS_FHSIZE;
1757 memcpy(data.old_root.data,
1758 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1759 NFS_FHSIZE);
1760 } else {
1761 fhandle3 *my_fhandle;
1762 if (status.nfsv3.fhs_status != 0) {
1763 bb_error_msg("%s:%s failed, reason given by server: %s",
1764 hostname, pathname,
1765 nfs_strerror(status.nfsv3.fhs_status));
1766 goto fail;
1767 }
1768 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1769 memset(data.old_root.data, 0, NFS_FHSIZE);
1770 memset(&data.root, 0, sizeof(data.root));
1771 data.root.size = my_fhandle->fhandle3_len;
1772 memcpy(data.root.data,
1773 (char *) my_fhandle->fhandle3_val,
1774 my_fhandle->fhandle3_len);
1775
1776 data.flags |= NFS_MOUNT_VER3;
1777 }
1778
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001779 /* Create nfs socket for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001780 if (tcp) {
1781 if (nfs_mount_version < 3) {
James Byrne69374872019-07-02 11:35:03 +02001782 bb_simple_error_msg("NFS over TCP is not supported");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001783 goto fail;
1784 }
1785 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1786 } else
1787 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1788 if (fsock < 0) {
James Byrne69374872019-07-02 11:35:03 +02001789 bb_simple_perror_msg("nfs socket");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001790 goto fail;
1791 }
1792 if (bindresvport(fsock, 0) < 0) {
James Byrne69374872019-07-02 11:35:03 +02001793 bb_simple_perror_msg("nfs bindresvport");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001794 goto fail;
1795 }
1796 if (port == 0) {
1797 server_addr.sin_port = PMAPPORT;
1798 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1799 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1800 if (port == 0)
1801 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001802 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001803 server_addr.sin_port = htons(port);
1804
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001805 /* Prepare data structure for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001806 data.fd = fsock;
1807 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1808 strncpy(data.hostname, hostname, sizeof(data.hostname));
1809
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001810 /* Clean up */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001811 auth_destroy(mclient->cl_auth);
1812 clnt_destroy(mclient);
1813 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001814 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001815
1816 if (bg) {
1817 /* We must wait until mount directory is available */
1818 struct stat statbuf;
1819 int delay = 1;
1820 while (stat(mp->mnt_dir, &statbuf) == -1) {
1821 if (!daemonized) {
1822 daemonized = daemonize();
1823 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001824/* FIXME: parent doesn't close fsock - ??! */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001825 retval = -daemonized;
1826 goto ret;
1827 }
1828 }
1829 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1830 delay *= 2;
1831 if (delay > 30)
1832 delay = 30;
1833 }
1834 }
1835
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001836 /* Perform actual mount */
1837 do_mount:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001838 retval = mount_it_now(mp, vfsflags, (char*)&data);
1839 goto ret;
1840
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001841 /* Abort */
1842 fail:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001843 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001844 if (mclient) {
1845 auth_destroy(mclient->cl_auth);
1846 clnt_destroy(mclient);
1847 }
1848 close(msock);
1849 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001850 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001851 close(fsock);
1852
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001853 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001854 free(hostname);
1855 free(mounthost);
1856 free(filteropts);
1857 return retval;
1858}
1859
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001860#else // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001861
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001862/* Linux 2.6.23+ supports nfs mounts with options passed as a string.
1863 * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
1864 * (However, note that then you lose any chances that NFS over IPv6 would work).
1865 */
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001866static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001867{
1868 len_and_sockaddr *lsa;
1869 char *opts;
1870 char *end;
1871 char *dotted;
1872 int ret;
1873
1874# if ENABLE_FEATURE_IPV6
1875 end = strchr(mp->mnt_fsname, ']');
1876 if (end && end[1] == ':')
1877 end++;
1878 else
1879# endif
1880 /* mount_main() guarantees that ':' is there */
1881 end = strchr(mp->mnt_fsname, ':');
1882
1883 *end = '\0';
Denys Vlasenko39b23312011-11-10 17:01:39 +01001884 lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001885 *end = ':';
1886 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1887 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1888 opts = xasprintf("%s%saddr=%s",
1889 filteropts ? filteropts : "",
1890 filteropts ? "," : "",
1891 dotted
1892 );
1893 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1894 ret = mount_it_now(mp, vfsflags, opts);
1895 if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1896
1897 return ret;
1898}
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001899
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001900#endif // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001901
Steffen Trumtrar6561e072020-07-29 10:43:53 +02001902// Find "...,NAME=NUM,..." in the option string, remove "NAME=NUM" option
1903// and return NUM.
1904// Return 0 if not found.
1905// All instances must be parsed and removed (for example, since kernel 5.4
1906// squashfs: Unknown parameter 'sizelimit'
1907// will result if loopback mount option "sizelimit=NNN" is not removed
1908// and squashfs sees it in option string).
1909static unsigned long long cut_out_ull_opt(char *opts, const char *name_eq)
1910{
1911 unsigned long long ret = 0;
1912
1913 if (!opts) // allow NULL opts (simplifies callers' work)
1914 return ret;
1915
1916 for (;;) {
1917 char *end;
1918 char *opt;
1919
1920 // Find comma-delimited "NAME="
1921 for (;;) {
1922 opt = strstr(opts, name_eq);
1923 if (!opt)
1924 return ret;
1925 if (opt == opts)
1926 break; // found it (it's first opt)
1927 if (opt[-1] == ',') {
1928 opts = opt - 1;
1929 break; // found it (it's not a first opt)
1930 }
1931 // False positive like "VNAME=", we are at "N".
1932 // - skip it, loop back to searching
1933 opts = opt + 1;
1934 }
1935
1936 ret = bb_strtoull(opt + strlen(name_eq), &end, 0);
1937 if (errno && errno != EINVAL) {
1938 err:
1939 bb_error_msg_and_die("bad option '%s'", opt);
1940 }
1941 if (*end == '\0') {
1942 // It is "[,]NAME=NUM\0" - truncate it and return
1943 *opts = '\0';
1944 return ret;
1945 }
1946 if (*end != ',')
1947 goto err;
1948 // We are at trailing comma
1949 // Remove "NAME=NUM," and loop back to check for duplicate opts
1950 overlapping_strcpy(opt, end + 1);
1951 }
1952}
1953
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001954// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1955// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001956// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001957static int singlemount(struct mntent *mp, int ignore_busy)
1958{
Denys Vlasenkoab518ee2017-03-16 16:49:37 +01001959 int loopfd = -1;
Denis Vlasenkob4133682008-02-18 13:05:38 +00001960 int rc = -1;
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001961 unsigned long vfsflags;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001962 char *loopFile = NULL, *filteropts = NULL;
1963 llist_t *fl = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001964 struct stat st;
1965
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001966 errno = 0;
1967
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001968 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1969
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001970 // Treat fstype "auto" as unspecified
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001971 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1972 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001973
Denis Vlasenko2535f122007-09-15 13:28:30 +00001974 // Might this be a virtual filesystem?
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001975 if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1976 char *args[35];
1977 char *s;
1978 int n;
1979 // fsname: "cmd#arg1#arg2..."
1980 // WARNING: allows execution of arbitrary commands!
1981 // Try "mount 'sh#-c#sh' bogus_dir".
1982 // It is safe ONLY because non-root
1983 // cannot use two-argument mount command
1984 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1985 // "mount: can't find sh#-c#sh in /etc/fstab"
1986 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1987
1988 s = mp->mnt_fsname;
1989 n = 0;
1990 args[n++] = s;
1991 while (*s && n < 35 - 2) {
1992 if (*s++ == '#' && *s != '#') {
1993 s[-1] = '\0';
1994 args[n++] = s;
Denis Vlasenko2535f122007-09-15 13:28:30 +00001995 }
1996 }
Denis Vlasenko2535f122007-09-15 13:28:30 +00001997 args[n++] = mp->mnt_dir;
1998 args[n] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +01001999 rc = spawn_and_wait(args);
Denis Vlasenko2535f122007-09-15 13:28:30 +00002000 goto report_error;
2001 }
2002
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002003 // Might this be an CIFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002004 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002005 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
2006 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
2007 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002008 ) {
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002009 int len;
2010 char c;
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01002011 char *hostname, *share;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00002012 len_and_sockaddr *lsa;
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01002013
2014 // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002015
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01002016 hostname = mp->mnt_fsname + 2;
2017 len = strcspn(hostname, "/\\");
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01002018 share = hostname + len + 1;
2019 if (len == 0 // 3rd char is a [back]slash (IOW: empty hostname)
2020 || share[-1] == '\0' // no [back]slash after hostname
2021 || share[0] == '\0' // empty share name
Martin Santesson406ea152013-01-16 00:47:19 +01002022 ) {
Denis Vlasenko5c329932009-04-12 12:16:21 +00002023 goto report_error;
Martin Santesson406ea152013-01-16 00:47:19 +01002024 }
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01002025 c = share[-1];
2026 share[-1] = '\0';
2027 len = strcspn(share, "/\\");
Martin Santesson406ea152013-01-16 00:47:19 +01002028
2029 // "unc=\\hostname\share" option is mandatory
2030 // after CIFS option parsing was rewritten in Linux 3.4.
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01002031 // Must use backslashes.
2032 // If /dir1/dir2 is present, also add "prefixpath=dir1/dir2"
Martin Santesson406ea152013-01-16 00:47:19 +01002033 {
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01002034 char *unc = xasprintf(
2035 share[len] != '\0' /* "/dir1/dir2" exists? */
2036 ? "unc=\\\\%s\\%.*s,prefixpath=%s"
2037 : "unc=\\\\%s\\%.*s",
2038 hostname,
2039 len, share,
2040 share + len + 1 /* "dir1/dir2" */
2041 );
Denys Vlasenko9b7ebfe2013-01-22 11:00:45 +01002042 parse_mount_options(unc, &filteropts);
2043 if (ENABLE_FEATURE_CLEAN_UP) free(unc);
Martin Santesson406ea152013-01-16 00:47:19 +01002044 }
2045
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01002046 lsa = host2sockaddr(hostname, 0);
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01002047 share[-1] = c;
Denis Vlasenko5c329932009-04-12 12:16:21 +00002048 if (!lsa)
2049 goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002050
Denys Vlasenkob09ab442016-09-27 21:02:35 +02002051 // If there is no "ip=..." option yet
2052 if (!is_prefixed_with(filteropts, ",ip="+1)
2053 && !strstr(filteropts, ",ip=")
2054 ) {
2055 char *dotted, *ip;
2056 // Insert "ip=..." option into options
2057 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
2058 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
2059 ip = xasprintf("ip=%s", dotted);
2060 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
Denys Vlasenko5093c8c2016-09-26 20:36:30 +02002061// Note: IPv6 scoped addresses ("host%iface", see RFC 4007) should be
Denys Vlasenko3c18e302016-09-26 19:53:04 +02002062// handled by libc in getnameinfo() (inside xmalloc_sockaddr2dotted_noport()).
2063// Currently, glibc does not support that (has no NI_NUMERICSCOPE),
2064// musl apparently does. This results in "ip=numericIPv6%iface_name"
2065// (instead of _numeric_ iface_id) with glibc.
Denys Vlasenko5093c8c2016-09-26 20:36:30 +02002066// This probably should be fixed in glibc, not here.
Denys Vlasenkob09ab442016-09-27 21:02:35 +02002067// The workaround is to manually specify correct "ip=ADDR%n" option.
2068 parse_mount_options(ip, &filteropts);
2069 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
2070 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002071
Denis Vlasenko06c0a712007-01-29 22:51:44 +00002072 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002073 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002074
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002075 goto report_error;
2076 }
2077
2078 // Might this be an NFS filesystem?
Denys Vlasenkob1eedfc2020-12-05 14:14:11 +01002079 if (!(vfsflags & (MS_BIND | MS_MOVE))
2080 && (!mp->mnt_type || is_prefixed_with(mp->mnt_type, "nfs"))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002081 ) {
Denys Vlasenkob1eedfc2020-12-05 14:14:11 +01002082 char *colon = strchr(mp->mnt_fsname, ':');
Denys Vlasenko56ee5762020-12-09 20:56:43 +01002083 if (colon // looks like "hostname:..."
2084 && strchrnul(mp->mnt_fsname, '/') > colon // "hostname:" has no slashes
Denys Vlasenkob1eedfc2020-12-05 14:14:11 +01002085 ) {
2086 if (!mp->mnt_type)
2087 mp->mnt_type = (char*)"nfs";
2088 rc = nfsmount(mp, vfsflags, filteropts);
2089 goto report_error;
2090 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002091 }
2092
2093 // Look at the file. (Not found isn't a failure for remount, or for
2094 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00002095 // (We use stat, not lstat, in order to allow
2096 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00002097 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002098 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
2099 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002100 // Do we need to allocate a loopback device for it?
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002101 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
Steffen Trumtrar6561e072020-07-29 10:43:53 +02002102 unsigned long long offset;
2103 unsigned long long sizelimit;
2104
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002105 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002106 mp->mnt_fsname = NULL; // will receive malloced loop dev name
Denys Vlasenkoab518ee2017-03-16 16:49:37 +01002107
Steffen Trumtrar6561e072020-07-29 10:43:53 +02002108 // Parse and remove loopback options
2109 offset = cut_out_ull_opt(filteropts, "offset=");
2110 sizelimit = cut_out_ull_opt(filteropts, "sizelimit=");
2111
Denys Vlasenkoab518ee2017-03-16 16:49:37 +01002112 // mount always creates AUTOCLEARed loopdevs, so that umounting
2113 // drops them without any code in the userspace.
2114 // This happens since circa linux-2.6.25:
2115 // commit 96c5865559cee0f9cbc5173f3c949f6ce3525581
2116 // Date: Wed Feb 6 01:36:27 2008 -0800
2117 // Subject: Allow auto-destruction of loop devices
2118 loopfd = set_loop(&mp->mnt_fsname,
2119 loopFile,
Steffen Trumtrar6561e072020-07-29 10:43:53 +02002120 offset,
2121 sizelimit,
Denys Vlasenkoab518ee2017-03-16 16:49:37 +01002122 ((vfsflags & MS_RDONLY) ? BB_LO_FLAGS_READ_ONLY : 0)
2123 | BB_LO_FLAGS_AUTOCLEAR
2124 );
2125 if (loopfd < 0) {
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00002126 if (errno == EPERM || errno == EACCES)
James Byrne69374872019-07-02 11:35:03 +02002127 bb_simple_error_msg(bb_msg_perm_denied_are_you_root);
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00002128 else
James Byrne69374872019-07-02 11:35:03 +02002129 bb_simple_perror_msg("can't setup loop device");
Denys Vlasenko56ee5762020-12-09 20:56:43 +01002130 return loopfd; // was "return errno", but it can be 0 here
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002131 }
2132
2133 // Autodetect bind mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002134 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
2135 vfsflags |= MS_BIND;
2136 }
2137
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002138 // If we know the fstype (or don't need to), jump straight
2139 // to the actual mount.
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02002140 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +01002141 char *next;
2142 for (;;) {
2143 next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
2144 if (next)
2145 *next = '\0';
2146 rc = mount_it_now(mp, vfsflags, filteropts);
2147 if (rc == 0 || !next)
2148 break;
2149 mp->mnt_type = next + 1;
2150 }
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02002151 } else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002152 // Loop through filesystem types until mount succeeds
2153 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002154
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01002155 // Initialize list of block backed filesystems.
2156 // This has to be done here so that during "mount -a",
2157 // mounts after /proc shows up can autodetect.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002158 if (!fslist) {
2159 fslist = get_block_backed_filesystems();
2160 if (ENABLE_FEATURE_CLEAN_UP && fslist)
2161 atexit(delete_block_backed_filesystems);
2162 }
2163
2164 for (fl = fslist; fl; fl = fl->link) {
2165 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00002166 rc = mount_it_now(mp, vfsflags, filteropts);
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +01002167 if (rc == 0)
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02002168 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002169 }
2170 }
2171
2172 // If mount failed, clean up loop file (if any).
Denys Vlasenkoab518ee2017-03-16 16:49:37 +01002173 // (Newer kernels which support LO_FLAGS_AUTOCLEAR should not need this,
2174 // merely "close(loopfd)" should do it?)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002175 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
2176 del_loop(mp->mnt_fsname);
2177 if (ENABLE_FEATURE_CLEAN_UP) {
2178 free(loopFile);
Denys Vlasenkoecf25cb2016-06-20 11:04:04 +02002179 /* No, "rc != 0" needs it: free(mp->mnt_fsname); */
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002180 }
2181 }
2182
Denis Vlasenko5870ad92007-02-04 02:39:55 +00002183 report_error:
2184 if (ENABLE_FEATURE_CLEAN_UP)
2185 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002186
Denys Vlasenkoab518ee2017-03-16 16:49:37 +01002187 if (loopfd >= 0)
2188 close(loopfd);
2189
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00002190 if (errno == EBUSY && ignore_busy)
2191 return 0;
Denys Vlasenko911d2652015-12-30 20:11:34 +01002192 if (errno == ENOENT && (vfsflags & MOUNT_NOFAIL))
2193 return 0;
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02002194 if (rc != 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00002195 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002196 return rc;
2197}
2198
Michael Abbott6b5accb2009-12-04 03:33:07 +01002199// -O support
2200// -O interprets a list of filter options which select whether a mount
2201// point will be mounted: only mounts with options matching *all* filtering
2202// options will be selected.
2203// By default each -O filter option must be present in the list of mount
2204// options, but if it is prefixed by "no" then it must be absent.
2205// For example,
2206// -O a,nob,c matches -o a,c but fails to match -o a,b,c
2207// (and also fails to match -o a because -o c is absent).
2208//
2209// It is different from -t in that each option is matched exactly; a leading
2210// "no" at the beginning of one option does not negate the rest.
2211static int match_opt(const char *fs_opt_in, const char *O_opt)
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002212{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002213 if (!O_opt)
Michael Abbott6b5accb2009-12-04 03:33:07 +01002214 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002215
Michael Abbott6b5accb2009-12-04 03:33:07 +01002216 while (*O_opt) {
2217 const char *fs_opt = fs_opt_in;
2218 int O_len;
2219 int match;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002220
Michael Abbott6b5accb2009-12-04 03:33:07 +01002221 // If option begins with "no" then treat as an inverted match:
2222 // matching is a failure
2223 match = 0;
2224 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
2225 match = 1;
2226 O_opt += 2;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002227 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01002228 // Isolate the current O option
2229 O_len = strchrnul(O_opt, ',') - O_opt;
2230 // Check for a match against existing options
2231 while (1) {
2232 if (strncmp(fs_opt, O_opt, O_len) == 0
2233 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
2234 ) {
2235 if (match)
2236 return 0; // "no" prefix, but option found
2237 match = 1; // current O option found, go check next one
2238 break;
2239 }
2240 fs_opt = strchr(fs_opt, ',');
2241 if (!fs_opt)
2242 break;
2243 fs_opt++;
2244 }
2245 if (match == 0)
2246 return 0; // match wanted but not found
2247 if (O_opt[O_len] == '\0') // end?
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002248 break;
Michael Abbott6b5accb2009-12-04 03:33:07 +01002249 // Step to the next O option
2250 O_opt += O_len + 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002251 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01002252 // If we get here then everything matched
2253 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002254}
2255
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002256// Parse options, if necessary parse fstab/mtab, and call singlemount for
2257// each directory to be mounted.
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00002258int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002259int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002260{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002261 char *cmdopts = xzalloc(1);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002262 char *fstype = NULL;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002263 char *O_optmatch = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002264 char *storage_path;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00002265 llist_t *lst_o = NULL;
Isaac Dunham7b434a62015-03-11 16:07:24 +01002266 const char *fstabname = "/etc/fstab";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002267 FILE *fstab;
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002268 int i, j;
2269 int rc = EXIT_SUCCESS;
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002270 unsigned long cmdopt_flags;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00002271 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002272 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002273 IF_NOT_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002274
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002275 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00002276
Denys Vlasenko16714242011-09-21 01:59:15 +02002277 INIT_G();
2278
Denis Vlasenkof732e962008-02-18 12:07:49 +00002279 // Parse long options, like --bind and --move. Note that -o option
2280 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002281 for (i = j = 1; argv[i]; i++) {
2282 if (argv[i][0] == '-' && argv[i][1] == '-')
2283 append_mount_options(&cmdopts, argv[i] + 2);
2284 else
2285 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002286 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002287 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002288
2289 // Parse remaining options
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002290 // Max 2 params; -o is a list, -v is a counter
Denys Vlasenko22542ec2017-08-08 21:55:02 +02002291 opt = getopt32(argv, "^"
2292 OPTION_STR
2293 "\0" "?2"IF_FEATURE_MOUNT_VERBOSE("vv"),
2294 &lst_o, &fstype, &O_optmatch
2295 IF_FEATURE_MOUNT_OTHERTAB(, &fstabname)
2296 IF_FEATURE_MOUNT_VERBOSE(, &verbose)
2297 );
2298
Denis Vlasenkof9dde912008-10-18 19:15:57 +00002299 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00002300 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2301 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00002302 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002303
2304 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002305 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00002306 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002307 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2308
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002309 if (!mountTable)
2310 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002311
Denis Vlasenko2535f122007-09-15 13:28:30 +00002312 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002313 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002314 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002315 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00002316 // util-linux 2.12a happily shows rootfs...
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002317 //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002318
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002319 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002320 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2321 mtpair->mnt_dir, mtpair->mnt_type,
2322 mtpair->mnt_opts);
2323 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002324 if (ENABLE_FEATURE_CLEAN_UP)
2325 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002326 return EXIT_SUCCESS;
2327 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002328 storage_path = NULL;
2329 } else {
2330 // When we have two arguments, the second is the directory and we can
2331 // skip looking at fstab entirely. We can always abspath() the directory
2332 // argument when we get it.
2333 if (argv[1]) {
2334 if (nonroot)
James Byrne69374872019-07-02 11:35:03 +02002335 bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002336 mtpair->mnt_fsname = argv[0];
2337 mtpair->mnt_dir = argv[1];
2338 mtpair->mnt_type = fstype;
2339 mtpair->mnt_opts = cmdopts;
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002340 resolve_mount_spec(&mtpair->mnt_fsname);
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002341 rc = singlemount(mtpair, /*ignore_busy:*/ 0);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002342 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00002343 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002344 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002345 }
2346
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002347 // Past this point, we are handling either "mount -a [opts]"
2348 // or "mount [opts] single_param"
2349
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002350 cmdopt_flags = parse_mount_options(cmdopts, NULL);
2351 if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
James Byrne69374872019-07-02 11:35:03 +02002352 bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00002353
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002354 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002355 if (ENABLE_FEATURE_MOUNT_FLAGS
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002356 && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002357 ) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002358 // verbose_mount(source, target, type, flags, data)
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002359 rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002360 if (rc)
2361 bb_simple_perror_msg_and_die(argv[0]);
2362 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002363 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00002364
Isaac Dunham7b434a62015-03-11 16:07:24 +01002365 // A malicious user could overmount /usr without this.
2366 if (ENABLE_FEATURE_MOUNT_OTHERTAB && nonroot)
2367 fstabname = "/etc/fstab";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002368 // Open either fstab or mtab
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002369 if (cmdopt_flags & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002370 // WARNING. I am not sure this matches util-linux's
2371 // behavior. It's possible util-linux does not
2372 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002373 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002374 }
2375 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00002376 if (!fstab)
Denys Vlasenko651a2692010-03-23 16:25:17 +01002377 bb_perror_msg_and_die("can't read '%s'", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002378
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002379 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00002380 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002381 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002382 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002383
2384 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00002385 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002386 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002387 GETMNTENT_BUFSIZE/2)
2388 ) { // End of fstab/mtab is reached
2389 mtcur = mtother; // the thing we found last time
2390 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002391 }
2392
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002393 // If we're trying to mount something specific and this isn't it,
2394 // skip it. Note we must match the exact text in fstab (ala
2395 // "proc") or a full path from root
2396 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002397
2398 // Is this what we're looking for?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002399 if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2400 && strcmp(storage_path, mtcur->mnt_fsname) != 0
2401 && strcmp(argv[0], mtcur->mnt_dir) != 0
2402 && strcmp(storage_path, mtcur->mnt_dir) != 0
2403 ) {
2404 continue; // no
2405 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002406
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002407 // Remember this entry. Something later may have
2408 // overmounted it, and we want the _last_ match.
2409 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002410
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002411 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002412 } else {
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002413 struct mntent *mp;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002414 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002415 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002416 if (nonroot)
James Byrne69374872019-07-02 11:35:03 +02002417 bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002418
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002419 // Does type match? (NULL matches always)
Denys Vlasenko35b54a32017-01-30 00:45:05 +01002420 if (!fstype_matches(mtcur->mnt_type, fstype))
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002421 continue;
2422
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002423 // Skip noauto and swap anyway
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002424 if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2425 // swap is bogus "fstype", parse_mount_options can't check fstypes
2426 || strcasecmp(mtcur->mnt_type, "swap") == 0
2427 ) {
2428 continue;
2429 }
2430
2431 // Does (at least one) option match?
2432 // (NULL matches always)
2433 if (!match_opt(mtcur->mnt_opts, O_optmatch))
2434 continue;
2435
2436 resolve_mount_spec(&mtcur->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002437
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002438 // NFS mounts want this to be xrealloc-able
2439 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002440
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002441 // If nothing is mounted on this directory...
2442 // (otherwise repeated "mount -a" mounts everything again)
2443 mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2444 // We do not check fsname match of found mount point -
2445 // "/" may have fsname of "/dev/root" while fstab
2446 // says "/dev/something_else".
2447 if (mp) {
Denys Vlasenko86566762009-12-10 21:32:28 +01002448 if (verbose) {
2449 bb_error_msg("according to %s, "
2450 "%s is already mounted on %s",
2451 bb_path_mtab_file,
2452 mp->mnt_fsname, mp->mnt_dir);
2453 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002454 } else {
2455 // ...mount this thing
2456 if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2457 // Count number of failed mounts
2458 rc++;
2459 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002460 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002461 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002462 }
2463 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002464
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002465 // End of fstab/mtab is reached.
2466 // Were we looking for something specific?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002467 if (argv[0]) { // yes
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002468 unsigned long l;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002469
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002470 // If we didn't find anything, complain
2471 if (!mtcur->mnt_fsname)
2472 bb_error_msg_and_die("can't find %s in %s",
2473 argv[0], fstabname);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002474
2475 // What happens when we try to "mount swap_partition"?
2476 // (fstab containts "swap_partition swap swap defaults 0 0")
2477 // util-linux-ng 2.13.1 does this:
2478 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2479 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2480 // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory)
2481 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2482 // exit_group(32) = ?
2483#if 0
2484 // In case we want to simply skip swap partitions:
2485 l = parse_mount_options(mtcur->mnt_opts, NULL);
2486 if ((l & MOUNT_SWAP)
2487 // swap is bogus "fstype", parse_mount_options can't check fstypes
2488 || strcasecmp(mtcur->mnt_type, "swap") == 0
2489 ) {
2490 goto ret;
2491 }
2492#endif
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002493 if (nonroot) {
2494 // fstab must have "users" or "user"
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002495 l = parse_mount_options(mtcur->mnt_opts, NULL);
2496 if (!(l & MOUNT_USERS))
James Byrne69374872019-07-02 11:35:03 +02002497 bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002498 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002499
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002500 //util-linux-2.12 does not do this check.
2501 //// If nothing is mounted on this directory...
2502 //// (otherwise repeated "mount FOO" mounts FOO again)
2503 //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2504 //if (mp) {
2505 // bb_error_msg("according to %s, "
2506 // "%s is already mounted on %s",
2507 // bb_path_mtab_file,
2508 // mp->mnt_fsname, mp->mnt_dir);
2509 //} else {
2510 // ...mount the last thing we found
2511 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2512 append_mount_options(&(mtcur->mnt_opts), cmdopts);
2513 resolve_mount_spec(&mtpair->mnt_fsname);
2514 rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2515 if (ENABLE_FEATURE_CLEAN_UP)
2516 free(mtcur->mnt_opts);
2517 //}
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002518 }
2519
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002520 //ret:
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002521 if (ENABLE_FEATURE_CLEAN_UP)
2522 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002523 if (ENABLE_FEATURE_CLEAN_UP) {
2524 free(storage_path);
2525 free(cmdopts);
2526 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002527
2528//TODO: exitcode should be ORed mask of (from "man mount"):
2529// 0 success
2530// 1 incorrect invocation or permissions
2531// 2 system error (out of memory, cannot fork, no more loop devices)
2532// 4 internal mount bug or missing nfs support in mount
2533// 8 user interrupt
2534//16 problems writing or locking /etc/mtab
2535//32 mount failure
2536//64 some mount succeeded
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002537 return rc;
2538}