blob: 4e65b6b46c3ce21db2abc00473b68254a670fe2d [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;
Denys Vlasenko1b661122021-08-23 02:31:26 +0200565 int len;
566
567 len = strchrnul(newopts, ',') - newopts;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000568 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;
Denys Vlasenko1b661122021-08-23 02:31:26 +0200582 while (*newopts == ',') newopts++;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000583 }
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 Vlasenkocf4dc4a2021-10-08 02:16:41 +0200592static unsigned long parse_mount_options(char *options, char **unrecognized, uint32_t *opt)
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;
Denys Vlasenkocf4dc4a2021-10-08 02:16:41 +0200620 /* If we see "-o rw" on command line, it's the same as -w:
621 * "do not try to fall back to RO mounts"
622 */
623 if (fl == ~MS_RDONLY && opt)
624 (*opt) |= OPT_w;
Alexander Shishkin77650952010-10-28 06:10:03 +0200625 goto found;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000626 }
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200627 option_str += opt_len + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000628 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200629 // We did not recognize this option.
630 // If "unrecognized" is not NULL, append option there.
631 // Note that we should not append *empty* option -
632 // in this case we want to pass NULL, not "", to "data"
633 // parameter of mount(2) syscall.
634 // This is crucial for filesystems that don't accept
635 // any arbitrary mount options, like cgroup fs:
636 // "mount -t cgroup none /mnt"
637 if (options[0] && unrecognized) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000638 // Add it to strflags, to pass on to kernel
Alexander Shishkin77650952010-10-28 06:10:03 +0200639 char *p = *unrecognized;
640 unsigned len = p ? strlen(p) : 0;
641 *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000642
Rob Landley6a6798b2005-08-10 20:35:54 +0000643 // Comma separated if it's not the first one
Alexander Shishkin77650952010-10-28 06:10:03 +0200644 if (len) p[len++] = ',';
645 strcpy(p + len, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000646 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200647 found:
Denis Vlasenko2535f122007-09-15 13:28:30 +0000648 if (!comma)
649 break;
650 // Advance to next option
651 *comma = ',';
652 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000653 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000654
Rob Landleydc0955b2006-03-14 18:16:25 +0000655 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000656}
657
Rob Landleydc0955b2006-03-14 18:16:25 +0000658// Return a list of all block device backed filesystems
Rob Landleydc0955b2006-03-14 18:16:25 +0000659static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000660{
Denys Vlasenko6599e382020-11-30 15:10:43 +0100661 static const char filesystems[2][sizeof("/proc/filesystems")] ALIGN1 = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000662 "/etc/filesystems",
663 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000664 };
665 char *fs, *buf;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200666 llist_t *list = NULL;
Rob Landleydc0955b2006-03-14 18:16:25 +0000667 int i;
668 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000669
Denis Vlasenko87468852007-04-13 23:22:00 +0000670 for (i = 0; i < 2; i++) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000671 f = fopen_for_read(filesystems[i]);
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000672 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000673
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000674 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denys Vlasenko8dff01d2015-03-12 17:48:34 +0100675 if (is_prefixed_with(buf, "nodev") && isspace(buf[5]))
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100676 goto next;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000677 fs = skip_whitespace(buf);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200678 if (*fs == '#' || *fs == '*' || !*fs)
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100679 goto next;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000680
Denis Vlasenko372686b2006-10-12 22:42:33 +0000681 llist_add_to_end(&list, xstrdup(fs));
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100682 next:
Denis Vlasenko372686b2006-10-12 22:42:33 +0000683 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000684 }
685 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
686 }
687
688 return list;
689}
690
Rob Landleydc0955b2006-03-14 18:16:25 +0000691#if ENABLE_FEATURE_CLEAN_UP
692static void delete_block_backed_filesystems(void)
693{
Rob Landleya6b5b602006-05-08 19:03:07 +0000694 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000695}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000696#else
697void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000698#endif
699
Rob Landleydc0955b2006-03-14 18:16:25 +0000700// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000701// NB: mp->xxx fields may be trashed on exit
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200702static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000703{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000704 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000705
Denys Vlasenko911d2652015-12-30 20:11:34 +0100706 vfsflags &= ~(unsigned long)MOUNT_FAKEFLAGS;
707
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200708 if (FAKE_IT) {
Denis Vlasenkob4133682008-02-18 13:05:38 +0000709 if (verbose >= 2)
710 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
711 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
712 vfsflags, filteropts);
713 goto mtab;
714 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000715
Rob Landleydc0955b2006-03-14 18:16:25 +0000716 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000717 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000718 errno = 0;
719 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000720 vfsflags, filteropts);
Denys Vlasenkodbdf9e02021-08-23 02:30:13 +0200721 if (rc == 0)
722 goto mtab; // success
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000723
Denys Vlasenkodbdf9e02021-08-23 02:30:13 +0200724 // mount failed, try helper program
725 // mount.<mnt_type>
726 if (HELPERS_ALLOWED && mp->mnt_type) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200727 char *args[8];
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000728 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000729 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000730 rc = 1;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200731 if (FAKE_IT)
732 args[rc++] = (char *)"-f";
733 if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
734 args[rc++] = (char *)"-n";
Denis Vlasenko5c329932009-04-12 12:16:21 +0000735 args[rc++] = mp->mnt_fsname;
736 args[rc++] = mp->mnt_dir;
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000737 if (filteropts) {
738 args[rc++] = (char *)"-o";
739 args[rc++] = filteropts;
740 }
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000741 args[rc] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +0100742 rc = spawn_and_wait(args);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000743 free(args[0]);
Denys Vlasenkodbdf9e02021-08-23 02:30:13 +0200744 if (rc == 0)
745 goto mtab; // success
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000746 errno = errno_save;
747 }
748
Denys Vlasenkodbdf9e02021-08-23 02:30:13 +0200749 // Should we retry read-only mount?
750 if (vfsflags & MS_RDONLY)
751 break; // no, already was tried
752 if (option_mask32 & OPT_w)
753 break; // no, "mount -w" never falls back to RO
754 if (errno != EACCES && errno != EROFS)
755 break; // no, error isn't hinting that RO may work
756
Denis Vlasenko2535f122007-09-15 13:28:30 +0000757 if (!(vfsflags & MS_SILENT))
758 bb_error_msg("%s is write-protected, mounting read-only",
759 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000760 vfsflags |= MS_RDONLY;
761 }
762
Rob Landleydc0955b2006-03-14 18:16:25 +0000763 // Abort entirely if permission denied.
764
765 if (rc && errno == EPERM)
James Byrne69374872019-07-02 11:35:03 +0200766 bb_simple_error_msg_and_die(bb_msg_perm_denied_are_you_root);
Rob Landleydc0955b2006-03-14 18:16:25 +0000767
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000768 // If the mount was successful, and we're maintaining an old-style
769 // mtab file by hand, add the new entry to it now.
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000770 mtab:
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200771 if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000772 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000773 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000774 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000775 int i;
776
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000777 if (!mountTable) {
James Byrne69374872019-07-02 11:35:03 +0200778 bb_simple_perror_msg(bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000779 goto ret;
780 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000781
782 // Add vfs string flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000783 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
784 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
785 append_mount_options(&(mp->mnt_opts), option_str);
786 option_str += strlen(option_str) + 1;
787 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000788
789 // Remove trailing / (if any) from directory we mounted on
Denis Vlasenko727ef942006-09-14 13:19:19 +0000790 i = strlen(mp->mnt_dir) - 1;
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100791 while (i > 0 && mp->mnt_dir[i] == '/')
Denys Vlasenkoc6450c92011-02-28 11:09:49 +0100792 mp->mnt_dir[i--] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000793
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000794 // Convert to canonical pathnames as needed
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000795 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100796 fsname = NULL;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000797 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000798 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000799 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000800 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000801 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000802
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100803 // Write and close
804#if ENABLE_FEATURE_MTAB_SUPPORT
805 if (vfsflags & MS_MOVE)
806 update_mtab_entry_on_move(mp);
807 else
808#endif
809 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000810 endmntent(mountTable);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100811
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000812 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000813 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000814 free(fsname);
815 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000816 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000817 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000818 return rc;
819}
820
Denis Vlasenko25098f72006-09-14 15:46:33 +0000821#if ENABLE_FEATURE_MOUNT_NFS
822
823/*
824 * Linux NFS mount
825 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
826 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +0200827 * Licensed under GPLv2, see file LICENSE in this source tree.
Denis Vlasenko25098f72006-09-14 15:46:33 +0000828 *
829 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
830 * numbers to be specified on the command line.
831 *
832 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
833 * Omit the call to connect() for Linux version 1.3.11 or later.
834 *
835 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
836 * Implemented the "bg", "fg" and "retry" mount options for NFS.
837 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000838 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000839 * - added Native Language Support
840 *
841 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
842 * plus NFSv3 stuff.
843 */
844
Denis Vlasenko25098f72006-09-14 15:46:33 +0000845#define MOUNTPORT 635
846#define MNTPATHLEN 1024
847#define MNTNAMLEN 255
848#define FHSIZE 32
849#define FHSIZE3 64
850
851typedef char fhandle[FHSIZE];
852
853typedef struct {
854 unsigned int fhandle3_len;
855 char *fhandle3_val;
856} fhandle3;
857
858enum mountstat3 {
859 MNT_OK = 0,
860 MNT3ERR_PERM = 1,
861 MNT3ERR_NOENT = 2,
862 MNT3ERR_IO = 5,
863 MNT3ERR_ACCES = 13,
864 MNT3ERR_NOTDIR = 20,
865 MNT3ERR_INVAL = 22,
866 MNT3ERR_NAMETOOLONG = 63,
867 MNT3ERR_NOTSUPP = 10004,
868 MNT3ERR_SERVERFAULT = 10006,
869};
870typedef enum mountstat3 mountstat3;
871
872struct fhstatus {
873 unsigned int fhs_status;
874 union {
875 fhandle fhs_fhandle;
876 } fhstatus_u;
877};
878typedef struct fhstatus fhstatus;
879
880struct mountres3_ok {
881 fhandle3 fhandle;
882 struct {
883 unsigned int auth_flavours_len;
884 char *auth_flavours_val;
885 } auth_flavours;
886};
887typedef struct mountres3_ok mountres3_ok;
888
889struct mountres3 {
890 mountstat3 fhs_status;
891 union {
892 mountres3_ok mountinfo;
893 } mountres3_u;
894};
895typedef struct mountres3 mountres3;
896
897typedef char *dirpath;
898
899typedef char *name;
900
901typedef struct mountbody *mountlist;
902
903struct mountbody {
904 name ml_hostname;
905 dirpath ml_directory;
906 mountlist ml_next;
907};
908typedef struct mountbody mountbody;
909
910typedef struct groupnode *groups;
911
912struct groupnode {
913 name gr_name;
914 groups gr_next;
915};
916typedef struct groupnode groupnode;
917
918typedef struct exportnode *exports;
919
920struct exportnode {
921 dirpath ex_dir;
922 groups ex_groups;
923 exports ex_next;
924};
925typedef struct exportnode exportnode;
926
927struct ppathcnf {
928 int pc_link_max;
929 short pc_max_canon;
930 short pc_max_input;
931 short pc_name_max;
932 short pc_path_max;
933 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000934 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000935 char pc_xxx;
936 short pc_mask[2];
937};
938typedef struct ppathcnf ppathcnf;
939
940#define MOUNTPROG 100005
941#define MOUNTVERS 1
942
943#define MOUNTPROC_NULL 0
944#define MOUNTPROC_MNT 1
945#define MOUNTPROC_DUMP 2
946#define MOUNTPROC_UMNT 3
947#define MOUNTPROC_UMNTALL 4
948#define MOUNTPROC_EXPORT 5
949#define MOUNTPROC_EXPORTALL 6
950
951#define MOUNTVERS_POSIX 2
952
953#define MOUNTPROC_PATHCONF 7
954
955#define MOUNT_V3 3
956
957#define MOUNTPROC3_NULL 0
958#define MOUNTPROC3_MNT 1
959#define MOUNTPROC3_DUMP 2
960#define MOUNTPROC3_UMNT 3
961#define MOUNTPROC3_UMNTALL 4
962#define MOUNTPROC3_EXPORT 5
963
964enum {
965#ifndef NFS_FHSIZE
966 NFS_FHSIZE = 32,
967#endif
968#ifndef NFS_PORT
969 NFS_PORT = 2049
970#endif
971};
972
Denis Vlasenko25098f72006-09-14 15:46:33 +0000973/*
974 * We want to be able to compile mount on old kernels in such a way
975 * that the binary will work well on more recent kernels.
976 * Thus, if necessary we teach nfsmount.c the structure of new fields
977 * that will come later.
978 *
979 * Moreover, the new kernel includes conflict with glibc includes
980 * so it is easiest to ignore the kernel altogether (at compile time).
981 */
982
983struct nfs2_fh {
Dave Lovefae473c2011-11-10 15:19:25 +0100984 char data[32];
Denis Vlasenko25098f72006-09-14 15:46:33 +0000985};
986struct nfs3_fh {
Dave Lovefae473c2011-11-10 15:19:25 +0100987 unsigned short size;
988 unsigned char data[64];
Denis Vlasenko25098f72006-09-14 15:46:33 +0000989};
990
991struct nfs_mount_data {
Dave Lovefae473c2011-11-10 15:19:25 +0100992 int version; /* 1 */
993 int fd; /* 1 */
994 struct nfs2_fh old_root; /* 1 */
995 int flags; /* 1 */
996 int rsize; /* 1 */
997 int wsize; /* 1 */
998 int timeo; /* 1 */
999 int retrans; /* 1 */
1000 int acregmin; /* 1 */
1001 int acregmax; /* 1 */
1002 int acdirmin; /* 1 */
1003 int acdirmax; /* 1 */
1004 struct sockaddr_in addr; /* 1 */
1005 char hostname[256]; /* 1 */
1006 int namlen; /* 2 */
1007 unsigned int bsize; /* 3 */
1008 struct nfs3_fh root; /* 4 */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001009};
1010
1011/* bits in the flags field */
1012enum {
1013 NFS_MOUNT_SOFT = 0x0001, /* 1 */
1014 NFS_MOUNT_INTR = 0x0002, /* 1 */
1015 NFS_MOUNT_SECURE = 0x0004, /* 1 */
1016 NFS_MOUNT_POSIX = 0x0008, /* 1 */
1017 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
1018 NFS_MOUNT_NOAC = 0x0020, /* 1 */
1019 NFS_MOUNT_TCP = 0x0040, /* 2 */
1020 NFS_MOUNT_VER3 = 0x0080, /* 3 */
1021 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001022 NFS_MOUNT_NONLM = 0x0200, /* 3 */
Dave Lovefae473c2011-11-10 15:19:25 +01001023 NFS_MOUNT_NOACL = 0x0800, /* 4 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001024 NFS_MOUNT_NORDIRPLUS = 0x4000
Denis Vlasenko25098f72006-09-14 15:46:33 +00001025};
1026
1027
1028/*
1029 * We need to translate between nfs status return values and
1030 * the local errno values which may not be the same.
1031 *
1032 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
1033 * "after #include <errno.h> the symbol errno is reserved for any use,
1034 * it cannot even be used as a struct tag or field name".
1035 */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001036#ifndef EDQUOT
Denys Vlasenkocc428142009-12-16 02:06:56 +01001037# define EDQUOT ENOSPC
Denis Vlasenko25098f72006-09-14 15:46:33 +00001038#endif
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001039/* Convert each NFSERR_BLAH into EBLAH */
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02001040static const uint8_t nfs_err_stat[] ALIGN1 = {
Denys Vlasenkocc428142009-12-16 02:06:56 +01001041 1, 2, 5, 6, 13, 17,
1042 19, 20, 21, 22, 27, 28,
1043 30, 63, 66, 69, 70, 71
1044};
Denys Vlasenkodc1fd2e2010-05-19 17:01:29 +02001045#if ( \
1046 EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \
1047 ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \
1048 EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
1049typedef uint8_t nfs_err_type;
1050#else
1051typedef uint16_t nfs_err_type;
1052#endif
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02001053static const nfs_err_type nfs_err_errnum[] ALIGN2 = {
Denys Vlasenkocc428142009-12-16 02:06:56 +01001054 EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST,
1055 ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC,
1056 EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
Denis Vlasenko25098f72006-09-14 15:46:33 +00001057};
Denis Vlasenko25098f72006-09-14 15:46:33 +00001058static char *nfs_strerror(int status)
1059{
1060 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001061
Denys Vlasenkocc428142009-12-16 02:06:56 +01001062 for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
1063 if (nfs_err_stat[i] == status)
1064 return strerror(nfs_err_errnum[i]);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001065 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001066 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001067}
1068
1069static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
1070{
Alexander Shishkin54779a42010-10-22 13:35:47 +02001071 return xdr_opaque(xdrs, objp, FHSIZE);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001072}
1073
1074static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
1075{
1076 if (!xdr_u_int(xdrs, &objp->fhs_status))
Denys Vlasenko69675782013-01-14 01:34:48 +01001077 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +02001078 if (objp->fhs_status == 0)
1079 return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001080 return TRUE;
1081}
1082
1083static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
1084{
Alexander Shishkin54779a42010-10-22 13:35:47 +02001085 return xdr_string(xdrs, objp, MNTPATHLEN);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001086}
1087
1088static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
1089{
Alexander Shishkin54779a42010-10-22 13:35:47 +02001090 return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
Denys Vlasenko69675782013-01-14 01:34:48 +01001091 (unsigned int *) &objp->fhandle3_len,
1092 FHSIZE3);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001093}
1094
1095static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
1096{
1097 if (!xdr_fhandle3(xdrs, &objp->fhandle))
1098 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +02001099 return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
Denys Vlasenko69675782013-01-14 01:34:48 +01001100 &(objp->auth_flavours.auth_flavours_len),
1101 ~0,
1102 sizeof(int),
1103 (xdrproc_t) xdr_int);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001104}
1105
1106static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
1107{
Alexander Shishkin54779a42010-10-22 13:35:47 +02001108 return xdr_enum(xdrs, (enum_t *) objp);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001109}
1110
1111static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
1112{
1113 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
1114 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +02001115 if (objp->fhs_status == MNT_OK)
1116 return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001117 return TRUE;
1118}
1119
1120#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
1121
Denis Vlasenko25098f72006-09-14 15:46:33 +00001122/*
1123 * Unfortunately, the kernel prints annoying console messages
1124 * in case of an unexpected nfs mount version (instead of
1125 * just returning some error). Therefore we'll have to try
1126 * and figure out what version the kernel expects.
1127 *
1128 * Variables:
1129 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
1130 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
1131 * nfs_mount_version: version this source and running kernel can handle
1132 */
1133static void
1134find_kernel_nfs_mount_version(void)
1135{
Denis Vlasenkob9256052007-09-28 10:29:17 +00001136 int kernel_version;
1137
1138 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001139 return;
1140
1141 nfs_mount_version = 4; /* default */
1142
1143 kernel_version = get_linux_version_code();
1144 if (kernel_version) {
Denys Vlasenkocc428142009-12-16 02:06:56 +01001145 if (kernel_version < KERNEL_VERSION(2,2,18))
Denis Vlasenko25098f72006-09-14 15:46:33 +00001146 nfs_mount_version = 3;
1147 /* else v4 since 2.3.99pre4 */
1148 }
1149}
1150
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +00001151static void
Denis Vlasenkob9256052007-09-28 10:29:17 +00001152get_mountport(struct pmap *pm_mnt,
1153 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001154 long unsigned prog,
1155 long unsigned version,
1156 long unsigned proto,
1157 long unsigned port)
1158{
1159 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001160
1161 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001162/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
1163 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001164 pmap = pmap_getmaps(server_addr);
1165
1166 if (version > MAX_NFSPROT)
1167 version = MAX_NFSPROT;
1168 if (!prog)
1169 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001170 pm_mnt->pm_prog = prog;
1171 pm_mnt->pm_vers = version;
1172 pm_mnt->pm_prot = proto;
1173 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001174
Denis Vlasenko25098f72006-09-14 15:46:33 +00001175 while (pmap) {
1176 if (pmap->pml_map.pm_prog != prog)
1177 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001178 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001179 goto next;
1180 if (version > 2 && pmap->pml_map.pm_vers != version)
1181 goto next;
1182 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1183 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001184 if (pmap->pml_map.pm_vers > MAX_NFSPROT
1185 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1186 || (port && pmap->pml_map.pm_port != port)
1187 ) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001188 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001189 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001190 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1191 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001192 pmap = pmap->pml_next;
1193 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001194 if (!pm_mnt->pm_vers)
1195 pm_mnt->pm_vers = MOUNTVERS;
1196 if (!pm_mnt->pm_port)
1197 pm_mnt->pm_port = MOUNTPORT;
1198 if (!pm_mnt->pm_prot)
1199 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001200}
1201
Denis Vlasenkof0000652007-09-04 18:30:26 +00001202#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001203static int daemonize(void)
1204{
Denis Vlasenko25098f72006-09-14 15:46:33 +00001205 int pid = fork();
1206 if (pid < 0) /* error */
1207 return -errno;
1208 if (pid > 0) /* parent */
1209 return 0;
1210 /* child */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001211 close(0);
1212 xopen(bb_dev_null, O_RDWR);
1213 xdup2(0, 1);
1214 xdup2(0, 2);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001215 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +00001216 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001217 logmode = LOGMODE_SYSLOG;
1218 return 1;
1219}
Denis Vlasenkof0000652007-09-04 18:30:26 +00001220#else
Denys Vlasenko8c317f02019-05-14 17:26:47 +02001221static inline int daemonize(void)
1222{
1223 return -ENOSYS;
1224}
Denis Vlasenkof0000652007-09-04 18:30:26 +00001225#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001226
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001227/* TODO */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001228static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001229{
1230 return 0;
1231}
1232
1233/* RPC strerror analogs are terminally idiotic:
1234 * *mandatory* prefix and \n at end.
1235 * This hopefully helps. Usage:
1236 * error_msg_rpc(clnt_*error*(" ")) */
1237static void error_msg_rpc(const char *msg)
1238{
Denis Vlasenko23514fe2006-09-19 14:07:52 +00001239 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001240 while (msg[0] == ' ' || msg[0] == ':') msg++;
1241 len = strlen(msg);
1242 while (len && msg[len-1] == '\n') len--;
1243 bb_error_msg("%.*s", len, msg);
1244}
1245
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001246/* NB: mp->xxx fields may be trashed on exit */
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001247static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001248{
1249 CLIENT *mclient;
1250 char *hostname;
1251 char *pathname;
1252 char *mounthost;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001253 /* prior to 2.6.23, kernel took NFS options in a form of this struct
1254 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1255 * then data pointer is interpreted as a string. */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001256 struct nfs_mount_data data;
1257 char *opt;
Denys Vlasenko24966162020-10-06 02:36:47 +02001258 char *tokstate;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001259 struct hostent *hp;
1260 struct sockaddr_in server_addr;
1261 struct sockaddr_in mount_server_addr;
1262 int msock, fsock;
1263 union {
1264 struct fhstatus nfsv2;
1265 struct mountres3 nfsv3;
1266 } status;
1267 int daemonized;
1268 char *s;
1269 int port;
1270 int mountport;
1271 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001272#if BB_MMU
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001273 smallint bg = 0;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001274#else
1275 enum { bg = 0 };
1276#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001277 int retry;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001278 int mountprog;
1279 int mountvers;
1280 int nfsprog;
1281 int nfsvers;
1282 int retval;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001283 /* these all are one-bit really. gcc 4.3.1 likes this combination: */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001284 smallint tcp;
1285 smallint soft;
1286 int intr;
1287 int posix;
1288 int nocto;
1289 int noac;
1290 int nordirplus;
1291 int nolock;
Dave Lovefae473c2011-11-10 15:19:25 +01001292 int noacl;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001293
1294 find_kernel_nfs_mount_version();
1295
1296 daemonized = 0;
1297 mounthost = NULL;
1298 retval = ETIMEDOUT;
1299 msock = fsock = -1;
1300 mclient = NULL;
1301
1302 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1303
1304 filteropts = xstrdup(filteropts); /* going to trash it later... */
1305
1306 hostname = xstrdup(mp->mnt_fsname);
1307 /* mount_main() guarantees that ':' is there */
1308 s = strchr(hostname, ':');
1309 pathname = s + 1;
1310 *s = '\0';
1311 /* Ignore all but first hostname in replicated mounts
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001312 * until they can be fully supported. (mack@sgi.com) */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001313 s = strchr(hostname, ',');
1314 if (s) {
1315 *s = '\0';
James Byrne69374872019-07-02 11:35:03 +02001316 bb_simple_error_msg("warning: multiple hostnames not supported");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001317 }
1318
1319 server_addr.sin_family = AF_INET;
1320 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1321 hp = gethostbyname(hostname);
1322 if (hp == NULL) {
James Byrne69374872019-07-02 11:35:03 +02001323 bb_simple_herror_msg(hostname);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001324 goto fail;
1325 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001326 if (hp->h_length != (int)sizeof(struct in_addr)) {
James Byrne69374872019-07-02 11:35:03 +02001327 bb_simple_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001328 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001329 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001330 }
1331
1332 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1333
1334 /* add IP address to mtab options for use when unmounting */
1335
1336 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1337 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1338 } else {
1339 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1340 mp->mnt_opts[0] ? "," : "",
1341 inet_ntoa(server_addr.sin_addr));
1342 free(mp->mnt_opts);
1343 mp->mnt_opts = tmp;
1344 }
1345
1346 /* Set default options.
1347 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1348 * let the kernel decide.
1349 * timeo is filled in after we know whether it'll be TCP or UDP. */
1350 memset(&data, 0, sizeof(data));
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001351 data.retrans = 3;
1352 data.acregmin = 3;
1353 data.acregmax = 60;
1354 data.acdirmin = 30;
1355 data.acdirmax = 60;
1356 data.namlen = NAME_MAX;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001357
Denis Vlasenko25098f72006-09-14 15:46:33 +00001358 soft = 0;
1359 intr = 0;
1360 posix = 0;
1361 nocto = 0;
1362 nolock = 0;
1363 noac = 0;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001364 nordirplus = 0;
Dave Lovefae473c2011-11-10 15:19:25 +01001365 noacl = 0;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001366 retry = 10000; /* 10000 minutes ~ 1 week */
Bernhard Reutner-Fischer88206292011-05-04 19:03:30 +02001367 tcp = 1; /* nfs-utils uses tcp per default */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001368
1369 mountprog = MOUNTPROG;
1370 mountvers = 0;
1371 port = 0;
1372 mountport = 0;
1373 nfsprog = 100003;
1374 nfsvers = 0;
1375
1376 /* parse options */
Denys Vlasenko24966162020-10-06 02:36:47 +02001377 if (filteropts) for (opt = strtok_r(filteropts, ",", &tokstate); opt; opt = strtok_r(NULL, ",", &tokstate)) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001378 char *opteq = strchr(opt, '=');
1379 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001380 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001381 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001382 /* 0 */ "rsize\0"
1383 /* 1 */ "wsize\0"
1384 /* 2 */ "timeo\0"
1385 /* 3 */ "retrans\0"
1386 /* 4 */ "acregmin\0"
1387 /* 5 */ "acregmax\0"
1388 /* 6 */ "acdirmin\0"
1389 /* 7 */ "acdirmax\0"
1390 /* 8 */ "actimeo\0"
1391 /* 9 */ "retry\0"
1392 /* 10 */ "port\0"
1393 /* 11 */ "mountport\0"
1394 /* 12 */ "mounthost\0"
1395 /* 13 */ "mountprog\0"
1396 /* 14 */ "mountvers\0"
1397 /* 15 */ "nfsprog\0"
1398 /* 16 */ "nfsvers\0"
1399 /* 17 */ "vers\0"
1400 /* 18 */ "proto\0"
1401 /* 19 */ "namlen\0"
1402 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001403
1404 *opteq++ = '\0';
1405 idx = index_in_strings(options, opt);
1406 switch (idx) {
1407 case 12: // "mounthost"
1408 mounthost = xstrndup(opteq,
1409 strcspn(opteq, " \t\n\r,"));
1410 continue;
1411 case 18: // "proto"
Denys Vlasenko8dff01d2015-03-12 17:48:34 +01001412 if (is_prefixed_with(opteq, "tcp"))
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001413 tcp = 1;
Denys Vlasenko8dff01d2015-03-12 17:48:34 +01001414 else if (is_prefixed_with(opteq, "udp"))
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001415 tcp = 0;
1416 else
James Byrne69374872019-07-02 11:35:03 +02001417 bb_simple_error_msg("warning: unrecognized proto= option");
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001418 continue;
1419 case 20: // "addr" - ignore
1420 continue;
Peter Korsgaard301fe502011-02-21 17:52:13 +01001421 case -1: // unknown
1422 if (vfsflags & MS_REMOUNT)
1423 continue;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001424 }
1425
Denys Vlasenko77832482010-08-12 14:14:45 +02001426 val = xatoi_positive(opteq);
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001427 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001428 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001429 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001430 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001431 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001432 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001433 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001434 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001435 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001436 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001437 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001438 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001439 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001440 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001441 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001442 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001443 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001444 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001445 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001446 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001447 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001448 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001449 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001450 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001451 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001452 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001453 data.acregmin = val;
1454 data.acregmax = val;
1455 data.acdirmin = val;
1456 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001457 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001458 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001459 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001460 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001461 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001462 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001463 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001464 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001465 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001466 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001467 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001468 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001469 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001470 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001471 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001472 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001473 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001474 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001475 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001476 case 16: // "nfsvers"
1477 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001478 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001479 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001480 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001481 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001482 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001483 //else
1484 // bb_error_msg("warning: option namlen is not supported\n");
1485 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001486 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001487 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1488 goto fail;
1489 }
1490 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001491 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001492 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001493 "bg\0"
1494 "fg\0"
1495 "soft\0"
1496 "hard\0"
1497 "intr\0"
1498 "posix\0"
1499 "cto\0"
1500 "ac\0"
1501 "tcp\0"
1502 "udp\0"
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001503 "lock\0"
Dave Lovefae473c2011-11-10 15:19:25 +01001504 "rdirplus\0"
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001505 "acl\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001506 int val = 1;
Denys Vlasenko8dff01d2015-03-12 17:48:34 +01001507 if (is_prefixed_with(opt, "no")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001508 val = 0;
1509 opt += 2;
1510 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001511 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001512 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001513#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001514 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001515#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001516 break;
1517 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001518#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001519 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001520#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001521 break;
1522 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001523 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001524 break;
1525 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001526 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001527 break;
1528 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001529 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001530 break;
1531 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001532 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001533 break;
1534 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001535 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001536 break;
1537 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001538 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001539 break;
1540 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001541 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001542 break;
1543 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001544 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001545 break;
1546 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001547 if (nfs_mount_version >= 3)
1548 nolock = !val;
1549 else
James Byrne69374872019-07-02 11:35:03 +02001550 bb_simple_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001551 break;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001552 case 11: //rdirplus
1553 nordirplus = !val;
1554 break;
Dave Lovefae473c2011-11-10 15:19:25 +01001555 case 12: // acl
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001556 noacl = !val;
Dave Lovefae473c2011-11-10 15:19:25 +01001557 break;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001558 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001559 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1560 goto fail;
1561 }
1562 }
1563 }
1564 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1565
1566 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1567 | (intr ? NFS_MOUNT_INTR : 0)
1568 | (posix ? NFS_MOUNT_POSIX : 0)
1569 | (nocto ? NFS_MOUNT_NOCTO : 0)
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001570 | (noac ? NFS_MOUNT_NOAC : 0)
Dave Lovefae473c2011-11-10 15:19:25 +01001571 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001572 | (noacl ? NFS_MOUNT_NOACL : 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001573 if (nfs_mount_version >= 2)
1574 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1575 if (nfs_mount_version >= 3)
1576 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1577 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1578 bb_error_msg("NFSv%d not supported", nfsvers);
1579 goto fail;
1580 }
1581 if (nfsvers && !mountvers)
1582 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1583 if (nfsvers && nfsvers < mountvers) {
1584 mountvers = nfsvers;
1585 }
1586
1587 /* Adjust options if none specified */
1588 if (!data.timeo)
1589 data.timeo = tcp ? 70 : 7;
1590
Denis Vlasenko25098f72006-09-14 15:46:33 +00001591 data.version = nfs_mount_version;
1592
1593 if (vfsflags & MS_REMOUNT)
1594 goto do_mount;
1595
1596 /*
1597 * If the previous mount operation on the same host was
1598 * backgrounded, and the "bg" for this mount is also set,
1599 * give up immediately, to avoid the initial timeout.
1600 */
1601 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001602 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001603 if (daemonized <= 0) { /* parent or error */
1604 retval = -daemonized;
1605 goto ret;
1606 }
1607 }
1608
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001609 /* Create mount daemon client */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001610 /* See if the nfs host = mount host. */
1611 if (mounthost) {
1612 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1613 mount_server_addr.sin_family = AF_INET;
1614 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1615 } else {
1616 hp = gethostbyname(mounthost);
1617 if (hp == NULL) {
James Byrne69374872019-07-02 11:35:03 +02001618 bb_simple_herror_msg(mounthost);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001619 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001620 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001621 if (hp->h_length != (int)sizeof(struct in_addr)) {
James Byrne69374872019-07-02 11:35:03 +02001622 bb_simple_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001623 }
1624 mount_server_addr.sin_family = AF_INET;
Denys Vlasenko99069332010-02-27 19:38:19 +01001625 memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001626 }
1627 }
1628
1629 /*
1630 * The following loop implements the mount retries. When the mount
1631 * times out, and the "bg" option is set, we background ourself
1632 * and continue trying.
1633 *
1634 * The case where the mount point is not present and the "bg"
1635 * option is set, is treated as a timeout. This is done to
1636 * support nested mounts.
1637 *
1638 * The "retry" count specified by the user is the number of
1639 * minutes to retry before giving up.
1640 */
1641 {
1642 struct timeval total_timeout;
1643 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001644 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001645 time_t t;
1646 time_t prevt;
1647 time_t timeout;
1648
1649 retry_timeout.tv_sec = 3;
1650 retry_timeout.tv_usec = 0;
1651 total_timeout.tv_sec = 20;
1652 total_timeout.tv_usec = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001653/* FIXME: use monotonic()? */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001654 timeout = time(NULL) + 60 * retry;
1655 prevt = 0;
1656 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001657 retry:
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001658 /* Be careful not to use too many CPU cycles */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001659 if (t - prevt < 30)
1660 sleep(30);
1661
Denis Vlasenkob9256052007-09-28 10:29:17 +00001662 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001663 mountprog,
1664 mountvers,
1665 proto,
1666 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001667 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001668
1669 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001670 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001671 msock = RPC_ANYSOCK;
1672
Denis Vlasenkob9256052007-09-28 10:29:17 +00001673 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001674 case IPPROTO_UDP:
1675 mclient = clntudp_create(&mount_server_addr,
Denys Vlasenko69675782013-01-14 01:34:48 +01001676 pm_mnt.pm_prog,
1677 pm_mnt.pm_vers,
1678 retry_timeout,
1679 &msock);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001680 if (mclient)
1681 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001682 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001683 msock = RPC_ANYSOCK;
1684 case IPPROTO_TCP:
1685 mclient = clnttcp_create(&mount_server_addr,
Denys Vlasenko69675782013-01-14 01:34:48 +01001686 pm_mnt.pm_prog,
1687 pm_mnt.pm_vers,
1688 &msock, 0, 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001689 break;
1690 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001691 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001692 }
1693 if (!mclient) {
1694 if (!daemonized && prevt == 0)
1695 error_msg_rpc(clnt_spcreateerror(" "));
1696 } else {
1697 enum clnt_stat clnt_stat;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001698
1699 /* Try to mount hostname:pathname */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001700 mclient->cl_auth = authunix_create_default();
1701
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001702 /* Make pointers in xdr_mountres3 NULL so
Denis Vlasenko25098f72006-09-14 15:46:33 +00001703 * that xdr_array allocates memory for us
1704 */
1705 memset(&status, 0, sizeof(status));
1706
Denis Vlasenkob9256052007-09-28 10:29:17 +00001707 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001708 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
Denys Vlasenko69675782013-01-14 01:34:48 +01001709 (xdrproc_t) xdr_dirpath,
1710 (caddr_t) &pathname,
1711 (xdrproc_t) xdr_mountres3,
1712 (caddr_t) &status,
1713 total_timeout);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001714 else
1715 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
Denys Vlasenko69675782013-01-14 01:34:48 +01001716 (xdrproc_t) xdr_dirpath,
1717 (caddr_t) &pathname,
1718 (xdrproc_t) xdr_fhstatus,
1719 (caddr_t) &status,
1720 total_timeout);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001721
1722 if (clnt_stat == RPC_SUCCESS)
1723 goto prepare_kernel_data; /* we're done */
1724 if (errno != ECONNREFUSED) {
1725 error_msg_rpc(clnt_sperror(mclient, " "));
1726 goto fail; /* don't retry */
1727 }
1728 /* Connection refused */
1729 if (!daemonized && prevt == 0) /* print just once */
1730 error_msg_rpc(clnt_sperror(mclient, " "));
1731 auth_destroy(mclient->cl_auth);
1732 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001733 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001734 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001735 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001736 }
1737
1738 /* Timeout. We are going to retry... maybe */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001739 if (!bg)
1740 goto fail;
1741 if (!daemonized) {
1742 daemonized = daemonize();
1743 if (daemonized <= 0) { /* parent or error */
1744 retval = -daemonized;
1745 goto ret;
1746 }
1747 }
1748 prevt = t;
1749 t = time(NULL);
1750 if (t >= timeout)
1751 /* TODO error message */
1752 goto fail;
1753
1754 goto retry;
1755 }
1756
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001757 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001758
1759 if (nfsvers == 2) {
1760 if (status.nfsv2.fhs_status != 0) {
1761 bb_error_msg("%s:%s failed, reason given by server: %s",
1762 hostname, pathname,
1763 nfs_strerror(status.nfsv2.fhs_status));
1764 goto fail;
1765 }
1766 memcpy(data.root.data,
1767 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1768 NFS_FHSIZE);
1769 data.root.size = NFS_FHSIZE;
1770 memcpy(data.old_root.data,
1771 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1772 NFS_FHSIZE);
1773 } else {
1774 fhandle3 *my_fhandle;
1775 if (status.nfsv3.fhs_status != 0) {
1776 bb_error_msg("%s:%s failed, reason given by server: %s",
1777 hostname, pathname,
1778 nfs_strerror(status.nfsv3.fhs_status));
1779 goto fail;
1780 }
1781 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1782 memset(data.old_root.data, 0, NFS_FHSIZE);
1783 memset(&data.root, 0, sizeof(data.root));
1784 data.root.size = my_fhandle->fhandle3_len;
1785 memcpy(data.root.data,
1786 (char *) my_fhandle->fhandle3_val,
1787 my_fhandle->fhandle3_len);
1788
1789 data.flags |= NFS_MOUNT_VER3;
1790 }
1791
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001792 /* Create nfs socket for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001793 if (tcp) {
1794 if (nfs_mount_version < 3) {
James Byrne69374872019-07-02 11:35:03 +02001795 bb_simple_error_msg("NFS over TCP is not supported");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001796 goto fail;
1797 }
1798 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1799 } else
1800 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1801 if (fsock < 0) {
James Byrne69374872019-07-02 11:35:03 +02001802 bb_simple_perror_msg("nfs socket");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001803 goto fail;
1804 }
1805 if (bindresvport(fsock, 0) < 0) {
James Byrne69374872019-07-02 11:35:03 +02001806 bb_simple_perror_msg("nfs bindresvport");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001807 goto fail;
1808 }
1809 if (port == 0) {
1810 server_addr.sin_port = PMAPPORT;
1811 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1812 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1813 if (port == 0)
1814 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001815 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001816 server_addr.sin_port = htons(port);
1817
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001818 /* Prepare data structure for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001819 data.fd = fsock;
1820 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1821 strncpy(data.hostname, hostname, sizeof(data.hostname));
1822
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001823 /* Clean up */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001824 auth_destroy(mclient->cl_auth);
1825 clnt_destroy(mclient);
1826 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001827 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001828
1829 if (bg) {
1830 /* We must wait until mount directory is available */
1831 struct stat statbuf;
1832 int delay = 1;
1833 while (stat(mp->mnt_dir, &statbuf) == -1) {
1834 if (!daemonized) {
1835 daemonized = daemonize();
1836 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001837/* FIXME: parent doesn't close fsock - ??! */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001838 retval = -daemonized;
1839 goto ret;
1840 }
1841 }
1842 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1843 delay *= 2;
1844 if (delay > 30)
1845 delay = 30;
1846 }
1847 }
1848
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001849 /* Perform actual mount */
1850 do_mount:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001851 retval = mount_it_now(mp, vfsflags, (char*)&data);
1852 goto ret;
1853
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001854 /* Abort */
1855 fail:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001856 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001857 if (mclient) {
1858 auth_destroy(mclient->cl_auth);
1859 clnt_destroy(mclient);
1860 }
1861 close(msock);
1862 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001863 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001864 close(fsock);
1865
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001866 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001867 free(hostname);
1868 free(mounthost);
1869 free(filteropts);
1870 return retval;
1871}
1872
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001873#else // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001874
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001875/* Linux 2.6.23+ supports nfs mounts with options passed as a string.
1876 * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
1877 * (However, note that then you lose any chances that NFS over IPv6 would work).
1878 */
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001879static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001880{
1881 len_and_sockaddr *lsa;
1882 char *opts;
1883 char *end;
1884 char *dotted;
1885 int ret;
1886
1887# if ENABLE_FEATURE_IPV6
1888 end = strchr(mp->mnt_fsname, ']');
1889 if (end && end[1] == ':')
1890 end++;
1891 else
1892# endif
1893 /* mount_main() guarantees that ':' is there */
1894 end = strchr(mp->mnt_fsname, ':');
1895
1896 *end = '\0';
Denys Vlasenko39b23312011-11-10 17:01:39 +01001897 lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001898 *end = ':';
1899 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1900 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1901 opts = xasprintf("%s%saddr=%s",
1902 filteropts ? filteropts : "",
1903 filteropts ? "," : "",
1904 dotted
1905 );
1906 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1907 ret = mount_it_now(mp, vfsflags, opts);
1908 if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1909
1910 return ret;
1911}
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001912
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001913#endif // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001914
Steffen Trumtrar6561e072020-07-29 10:43:53 +02001915// Find "...,NAME=NUM,..." in the option string, remove "NAME=NUM" option
1916// and return NUM.
1917// Return 0 if not found.
1918// All instances must be parsed and removed (for example, since kernel 5.4
1919// squashfs: Unknown parameter 'sizelimit'
1920// will result if loopback mount option "sizelimit=NNN" is not removed
1921// and squashfs sees it in option string).
1922static unsigned long long cut_out_ull_opt(char *opts, const char *name_eq)
1923{
1924 unsigned long long ret = 0;
1925
1926 if (!opts) // allow NULL opts (simplifies callers' work)
1927 return ret;
1928
1929 for (;;) {
1930 char *end;
1931 char *opt;
1932
1933 // Find comma-delimited "NAME="
1934 for (;;) {
1935 opt = strstr(opts, name_eq);
1936 if (!opt)
1937 return ret;
1938 if (opt == opts)
1939 break; // found it (it's first opt)
1940 if (opt[-1] == ',') {
1941 opts = opt - 1;
1942 break; // found it (it's not a first opt)
1943 }
1944 // False positive like "VNAME=", we are at "N".
1945 // - skip it, loop back to searching
1946 opts = opt + 1;
1947 }
1948
1949 ret = bb_strtoull(opt + strlen(name_eq), &end, 0);
1950 if (errno && errno != EINVAL) {
1951 err:
1952 bb_error_msg_and_die("bad option '%s'", opt);
1953 }
1954 if (*end == '\0') {
1955 // It is "[,]NAME=NUM\0" - truncate it and return
1956 *opts = '\0';
1957 return ret;
1958 }
1959 if (*end != ',')
1960 goto err;
1961 // We are at trailing comma
1962 // Remove "NAME=NUM," and loop back to check for duplicate opts
1963 overlapping_strcpy(opt, end + 1);
1964 }
1965}
1966
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001967// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1968// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001969// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001970static int singlemount(struct mntent *mp, int ignore_busy)
1971{
Denys Vlasenkoab518ee2017-03-16 16:49:37 +01001972 int loopfd = -1;
Denis Vlasenkob4133682008-02-18 13:05:38 +00001973 int rc = -1;
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001974 unsigned long vfsflags;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001975 char *loopFile = NULL, *filteropts = NULL;
1976 llist_t *fl = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001977 struct stat st;
1978
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001979 errno = 0;
1980
Denys Vlasenkocf4dc4a2021-10-08 02:16:41 +02001981 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts, NULL);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001982
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001983 // Treat fstype "auto" as unspecified
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001984 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1985 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001986
Denis Vlasenko2535f122007-09-15 13:28:30 +00001987 // Might this be a virtual filesystem?
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001988 if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1989 char *args[35];
1990 char *s;
1991 int n;
1992 // fsname: "cmd#arg1#arg2..."
1993 // WARNING: allows execution of arbitrary commands!
1994 // Try "mount 'sh#-c#sh' bogus_dir".
1995 // It is safe ONLY because non-root
1996 // cannot use two-argument mount command
1997 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1998 // "mount: can't find sh#-c#sh in /etc/fstab"
1999 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
2000
2001 s = mp->mnt_fsname;
2002 n = 0;
2003 args[n++] = s;
2004 while (*s && n < 35 - 2) {
2005 if (*s++ == '#' && *s != '#') {
2006 s[-1] = '\0';
2007 args[n++] = s;
Denis Vlasenko2535f122007-09-15 13:28:30 +00002008 }
2009 }
Denis Vlasenko2535f122007-09-15 13:28:30 +00002010 args[n++] = mp->mnt_dir;
2011 args[n] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +01002012 rc = spawn_and_wait(args);
Denis Vlasenko2535f122007-09-15 13:28:30 +00002013 goto report_error;
2014 }
2015
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002016 // Might this be an CIFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002017 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002018 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
2019 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
2020 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002021 ) {
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002022 int len;
2023 char c;
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01002024 char *hostname, *share;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00002025 len_and_sockaddr *lsa;
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01002026
2027 // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002028
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01002029 hostname = mp->mnt_fsname + 2;
2030 len = strcspn(hostname, "/\\");
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01002031 share = hostname + len + 1;
2032 if (len == 0 // 3rd char is a [back]slash (IOW: empty hostname)
2033 || share[-1] == '\0' // no [back]slash after hostname
2034 || share[0] == '\0' // empty share name
Martin Santesson406ea152013-01-16 00:47:19 +01002035 ) {
Denis Vlasenko5c329932009-04-12 12:16:21 +00002036 goto report_error;
Martin Santesson406ea152013-01-16 00:47:19 +01002037 }
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01002038 c = share[-1];
2039 share[-1] = '\0';
2040 len = strcspn(share, "/\\");
Martin Santesson406ea152013-01-16 00:47:19 +01002041
2042 // "unc=\\hostname\share" option is mandatory
2043 // after CIFS option parsing was rewritten in Linux 3.4.
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01002044 // Must use backslashes.
2045 // If /dir1/dir2 is present, also add "prefixpath=dir1/dir2"
Martin Santesson406ea152013-01-16 00:47:19 +01002046 {
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01002047 char *unc = xasprintf(
2048 share[len] != '\0' /* "/dir1/dir2" exists? */
2049 ? "unc=\\\\%s\\%.*s,prefixpath=%s"
2050 : "unc=\\\\%s\\%.*s",
2051 hostname,
2052 len, share,
2053 share + len + 1 /* "dir1/dir2" */
2054 );
Denys Vlasenkocf4dc4a2021-10-08 02:16:41 +02002055 parse_mount_options(unc, &filteropts, NULL);
Denys Vlasenko9b7ebfe2013-01-22 11:00:45 +01002056 if (ENABLE_FEATURE_CLEAN_UP) free(unc);
Martin Santesson406ea152013-01-16 00:47:19 +01002057 }
2058
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01002059 lsa = host2sockaddr(hostname, 0);
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01002060 share[-1] = c;
Denis Vlasenko5c329932009-04-12 12:16:21 +00002061 if (!lsa)
2062 goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002063
Denys Vlasenkob09ab442016-09-27 21:02:35 +02002064 // If there is no "ip=..." option yet
2065 if (!is_prefixed_with(filteropts, ",ip="+1)
2066 && !strstr(filteropts, ",ip=")
2067 ) {
2068 char *dotted, *ip;
2069 // Insert "ip=..." option into options
2070 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
2071 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
2072 ip = xasprintf("ip=%s", dotted);
2073 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
Denys Vlasenko5093c8c2016-09-26 20:36:30 +02002074// Note: IPv6 scoped addresses ("host%iface", see RFC 4007) should be
Denys Vlasenko3c18e302016-09-26 19:53:04 +02002075// handled by libc in getnameinfo() (inside xmalloc_sockaddr2dotted_noport()).
2076// Currently, glibc does not support that (has no NI_NUMERICSCOPE),
2077// musl apparently does. This results in "ip=numericIPv6%iface_name"
2078// (instead of _numeric_ iface_id) with glibc.
Denys Vlasenko5093c8c2016-09-26 20:36:30 +02002079// This probably should be fixed in glibc, not here.
Denys Vlasenkob09ab442016-09-27 21:02:35 +02002080// The workaround is to manually specify correct "ip=ADDR%n" option.
Denys Vlasenkocf4dc4a2021-10-08 02:16:41 +02002081 parse_mount_options(ip, &filteropts, NULL);
Denys Vlasenkob09ab442016-09-27 21:02:35 +02002082 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
2083 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002084
Denis Vlasenko06c0a712007-01-29 22:51:44 +00002085 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002086 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002087
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002088 goto report_error;
2089 }
2090
2091 // Might this be an NFS filesystem?
Denys Vlasenkob1eedfc2020-12-05 14:14:11 +01002092 if (!(vfsflags & (MS_BIND | MS_MOVE))
2093 && (!mp->mnt_type || is_prefixed_with(mp->mnt_type, "nfs"))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002094 ) {
Denys Vlasenkob1eedfc2020-12-05 14:14:11 +01002095 char *colon = strchr(mp->mnt_fsname, ':');
Denys Vlasenko56ee5762020-12-09 20:56:43 +01002096 if (colon // looks like "hostname:..."
2097 && strchrnul(mp->mnt_fsname, '/') > colon // "hostname:" has no slashes
Denys Vlasenkob1eedfc2020-12-05 14:14:11 +01002098 ) {
2099 if (!mp->mnt_type)
2100 mp->mnt_type = (char*)"nfs";
2101 rc = nfsmount(mp, vfsflags, filteropts);
2102 goto report_error;
2103 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002104 }
2105
2106 // Look at the file. (Not found isn't a failure for remount, or for
2107 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00002108 // (We use stat, not lstat, in order to allow
2109 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00002110 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002111 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
2112 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002113 // Do we need to allocate a loopback device for it?
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002114 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
Steffen Trumtrar6561e072020-07-29 10:43:53 +02002115 unsigned long long offset;
2116 unsigned long long sizelimit;
2117
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002118 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002119 mp->mnt_fsname = NULL; // will receive malloced loop dev name
Denys Vlasenkoab518ee2017-03-16 16:49:37 +01002120
Steffen Trumtrar6561e072020-07-29 10:43:53 +02002121 // Parse and remove loopback options
2122 offset = cut_out_ull_opt(filteropts, "offset=");
2123 sizelimit = cut_out_ull_opt(filteropts, "sizelimit=");
2124
Denys Vlasenkoab518ee2017-03-16 16:49:37 +01002125 // mount always creates AUTOCLEARed loopdevs, so that umounting
2126 // drops them without any code in the userspace.
2127 // This happens since circa linux-2.6.25:
2128 // commit 96c5865559cee0f9cbc5173f3c949f6ce3525581
2129 // Date: Wed Feb 6 01:36:27 2008 -0800
2130 // Subject: Allow auto-destruction of loop devices
2131 loopfd = set_loop(&mp->mnt_fsname,
2132 loopFile,
Steffen Trumtrar6561e072020-07-29 10:43:53 +02002133 offset,
2134 sizelimit,
Denys Vlasenkoab518ee2017-03-16 16:49:37 +01002135 ((vfsflags & MS_RDONLY) ? BB_LO_FLAGS_READ_ONLY : 0)
2136 | BB_LO_FLAGS_AUTOCLEAR
2137 );
2138 if (loopfd < 0) {
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00002139 if (errno == EPERM || errno == EACCES)
James Byrne69374872019-07-02 11:35:03 +02002140 bb_simple_error_msg(bb_msg_perm_denied_are_you_root);
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00002141 else
James Byrne69374872019-07-02 11:35:03 +02002142 bb_simple_perror_msg("can't setup loop device");
Denys Vlasenko56ee5762020-12-09 20:56:43 +01002143 return loopfd; // was "return errno", but it can be 0 here
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002144 }
2145
2146 // Autodetect bind mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002147 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
2148 vfsflags |= MS_BIND;
2149 }
2150
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002151 // If we know the fstype (or don't need to), jump straight
2152 // to the actual mount.
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02002153 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +01002154 char *next;
2155 for (;;) {
2156 next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
2157 if (next)
2158 *next = '\0';
2159 rc = mount_it_now(mp, vfsflags, filteropts);
2160 if (rc == 0 || !next)
2161 break;
2162 mp->mnt_type = next + 1;
2163 }
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02002164 } else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002165 // Loop through filesystem types until mount succeeds
2166 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002167
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01002168 // Initialize list of block backed filesystems.
2169 // This has to be done here so that during "mount -a",
2170 // mounts after /proc shows up can autodetect.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002171 if (!fslist) {
2172 fslist = get_block_backed_filesystems();
2173 if (ENABLE_FEATURE_CLEAN_UP && fslist)
2174 atexit(delete_block_backed_filesystems);
2175 }
2176
2177 for (fl = fslist; fl; fl = fl->link) {
2178 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00002179 rc = mount_it_now(mp, vfsflags, filteropts);
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +01002180 if (rc == 0)
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02002181 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002182 }
2183 }
2184
2185 // If mount failed, clean up loop file (if any).
Denys Vlasenkoab518ee2017-03-16 16:49:37 +01002186 // (Newer kernels which support LO_FLAGS_AUTOCLEAR should not need this,
2187 // merely "close(loopfd)" should do it?)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002188 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
2189 del_loop(mp->mnt_fsname);
2190 if (ENABLE_FEATURE_CLEAN_UP) {
2191 free(loopFile);
Denys Vlasenkoecf25cb2016-06-20 11:04:04 +02002192 /* No, "rc != 0" needs it: free(mp->mnt_fsname); */
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002193 }
2194 }
2195
Denis Vlasenko5870ad92007-02-04 02:39:55 +00002196 report_error:
2197 if (ENABLE_FEATURE_CLEAN_UP)
2198 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002199
Denys Vlasenkoab518ee2017-03-16 16:49:37 +01002200 if (loopfd >= 0)
2201 close(loopfd);
2202
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00002203 if (errno == EBUSY && ignore_busy)
2204 return 0;
Denys Vlasenko911d2652015-12-30 20:11:34 +01002205 if (errno == ENOENT && (vfsflags & MOUNT_NOFAIL))
2206 return 0;
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02002207 if (rc != 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00002208 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002209 return rc;
2210}
2211
Michael Abbott6b5accb2009-12-04 03:33:07 +01002212// -O support
2213// -O interprets a list of filter options which select whether a mount
2214// point will be mounted: only mounts with options matching *all* filtering
2215// options will be selected.
2216// By default each -O filter option must be present in the list of mount
2217// options, but if it is prefixed by "no" then it must be absent.
2218// For example,
2219// -O a,nob,c matches -o a,c but fails to match -o a,b,c
2220// (and also fails to match -o a because -o c is absent).
2221//
2222// It is different from -t in that each option is matched exactly; a leading
2223// "no" at the beginning of one option does not negate the rest.
2224static int match_opt(const char *fs_opt_in, const char *O_opt)
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002225{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002226 if (!O_opt)
Michael Abbott6b5accb2009-12-04 03:33:07 +01002227 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002228
Michael Abbott6b5accb2009-12-04 03:33:07 +01002229 while (*O_opt) {
2230 const char *fs_opt = fs_opt_in;
2231 int O_len;
2232 int match;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002233
Michael Abbott6b5accb2009-12-04 03:33:07 +01002234 // If option begins with "no" then treat as an inverted match:
2235 // matching is a failure
2236 match = 0;
2237 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
2238 match = 1;
2239 O_opt += 2;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002240 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01002241 // Isolate the current O option
2242 O_len = strchrnul(O_opt, ',') - O_opt;
2243 // Check for a match against existing options
2244 while (1) {
2245 if (strncmp(fs_opt, O_opt, O_len) == 0
2246 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
2247 ) {
2248 if (match)
2249 return 0; // "no" prefix, but option found
2250 match = 1; // current O option found, go check next one
2251 break;
2252 }
2253 fs_opt = strchr(fs_opt, ',');
2254 if (!fs_opt)
2255 break;
2256 fs_opt++;
2257 }
2258 if (match == 0)
2259 return 0; // match wanted but not found
2260 if (O_opt[O_len] == '\0') // end?
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002261 break;
Michael Abbott6b5accb2009-12-04 03:33:07 +01002262 // Step to the next O option
2263 O_opt += O_len + 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002264 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01002265 // If we get here then everything matched
2266 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002267}
2268
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002269// Parse options, if necessary parse fstab/mtab, and call singlemount for
2270// each directory to be mounted.
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00002271int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002272int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002273{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002274 char *cmdopts = xzalloc(1);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002275 char *fstype = NULL;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002276 char *O_optmatch = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002277 char *storage_path;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00002278 llist_t *lst_o = NULL;
Isaac Dunham7b434a62015-03-11 16:07:24 +01002279 const char *fstabname = "/etc/fstab";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002280 FILE *fstab;
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002281 int i, j;
2282 int rc = EXIT_SUCCESS;
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002283 unsigned long cmdopt_flags;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00002284 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002285 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002286 IF_NOT_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002287
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002288 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00002289
Denys Vlasenko16714242011-09-21 01:59:15 +02002290 INIT_G();
2291
Denis Vlasenkof732e962008-02-18 12:07:49 +00002292 // Parse long options, like --bind and --move. Note that -o option
2293 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002294 for (i = j = 1; argv[i]; i++) {
2295 if (argv[i][0] == '-' && argv[i][1] == '-')
2296 append_mount_options(&cmdopts, argv[i] + 2);
2297 else
2298 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002299 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002300 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002301
2302 // Parse remaining options
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002303 // Max 2 params; -o is a list, -v is a counter
Denys Vlasenko22542ec2017-08-08 21:55:02 +02002304 opt = getopt32(argv, "^"
2305 OPTION_STR
2306 "\0" "?2"IF_FEATURE_MOUNT_VERBOSE("vv"),
2307 &lst_o, &fstype, &O_optmatch
2308 IF_FEATURE_MOUNT_OTHERTAB(, &fstabname)
2309 IF_FEATURE_MOUNT_VERBOSE(, &verbose)
2310 );
2311
Denis Vlasenkof9dde912008-10-18 19:15:57 +00002312 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00002313 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2314 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00002315 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002316
2317 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002318 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00002319 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002320 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2321
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002322 if (!mountTable)
2323 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002324
Denis Vlasenko2535f122007-09-15 13:28:30 +00002325 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002326 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002327 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002328 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00002329 // util-linux 2.12a happily shows rootfs...
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002330 //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002331
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002332 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002333 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2334 mtpair->mnt_dir, mtpair->mnt_type,
2335 mtpair->mnt_opts);
2336 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002337 if (ENABLE_FEATURE_CLEAN_UP)
2338 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002339 return EXIT_SUCCESS;
2340 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002341 storage_path = NULL;
2342 } else {
2343 // When we have two arguments, the second is the directory and we can
2344 // skip looking at fstab entirely. We can always abspath() the directory
2345 // argument when we get it.
2346 if (argv[1]) {
2347 if (nonroot)
James Byrne69374872019-07-02 11:35:03 +02002348 bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002349 mtpair->mnt_fsname = argv[0];
2350 mtpair->mnt_dir = argv[1];
2351 mtpair->mnt_type = fstype;
2352 mtpair->mnt_opts = cmdopts;
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002353 resolve_mount_spec(&mtpair->mnt_fsname);
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002354 rc = singlemount(mtpair, /*ignore_busy:*/ 0);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002355 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00002356 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002357 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002358 }
2359
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002360 // Past this point, we are handling either "mount -a [opts]"
2361 // or "mount [opts] single_param"
2362
Denys Vlasenkocf4dc4a2021-10-08 02:16:41 +02002363 cmdopt_flags = parse_mount_options(cmdopts, NULL, &option_mask32);
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002364 if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
James Byrne69374872019-07-02 11:35:03 +02002365 bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00002366
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002367 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002368 if (ENABLE_FEATURE_MOUNT_FLAGS
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002369 && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002370 ) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002371 // verbose_mount(source, target, type, flags, data)
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002372 rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002373 if (rc)
2374 bb_simple_perror_msg_and_die(argv[0]);
2375 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002376 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00002377
Isaac Dunham7b434a62015-03-11 16:07:24 +01002378 // A malicious user could overmount /usr without this.
2379 if (ENABLE_FEATURE_MOUNT_OTHERTAB && nonroot)
2380 fstabname = "/etc/fstab";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002381 // Open either fstab or mtab
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002382 if (cmdopt_flags & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002383 // WARNING. I am not sure this matches util-linux's
2384 // behavior. It's possible util-linux does not
2385 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002386 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002387 }
2388 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00002389 if (!fstab)
Denys Vlasenko651a2692010-03-23 16:25:17 +01002390 bb_perror_msg_and_die("can't read '%s'", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002391
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002392 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00002393 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002394 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002395 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002396
2397 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00002398 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002399 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002400 GETMNTENT_BUFSIZE/2)
2401 ) { // End of fstab/mtab is reached
2402 mtcur = mtother; // the thing we found last time
2403 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002404 }
2405
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002406 // If we're trying to mount something specific and this isn't it,
2407 // skip it. Note we must match the exact text in fstab (ala
2408 // "proc") or a full path from root
2409 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002410
2411 // Is this what we're looking for?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002412 if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2413 && strcmp(storage_path, mtcur->mnt_fsname) != 0
2414 && strcmp(argv[0], mtcur->mnt_dir) != 0
2415 && strcmp(storage_path, mtcur->mnt_dir) != 0
2416 ) {
2417 continue; // no
2418 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002419
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002420 // Remember this entry. Something later may have
2421 // overmounted it, and we want the _last_ match.
2422 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002423
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002424 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002425 } else {
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002426 struct mntent *mp;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002427 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002428 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002429 if (nonroot)
James Byrne69374872019-07-02 11:35:03 +02002430 bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002431
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002432 // Does type match? (NULL matches always)
Denys Vlasenko35b54a32017-01-30 00:45:05 +01002433 if (!fstype_matches(mtcur->mnt_type, fstype))
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002434 continue;
2435
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002436 // Skip noauto and swap anyway
Denys Vlasenkocf4dc4a2021-10-08 02:16:41 +02002437 if ((parse_mount_options(mtcur->mnt_opts, NULL, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002438 // swap is bogus "fstype", parse_mount_options can't check fstypes
2439 || strcasecmp(mtcur->mnt_type, "swap") == 0
2440 ) {
2441 continue;
2442 }
2443
2444 // Does (at least one) option match?
2445 // (NULL matches always)
2446 if (!match_opt(mtcur->mnt_opts, O_optmatch))
2447 continue;
2448
2449 resolve_mount_spec(&mtcur->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002450
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002451 // NFS mounts want this to be xrealloc-able
2452 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002453
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002454 // If nothing is mounted on this directory...
2455 // (otherwise repeated "mount -a" mounts everything again)
2456 mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2457 // We do not check fsname match of found mount point -
2458 // "/" may have fsname of "/dev/root" while fstab
2459 // says "/dev/something_else".
2460 if (mp) {
Denys Vlasenko86566762009-12-10 21:32:28 +01002461 if (verbose) {
2462 bb_error_msg("according to %s, "
2463 "%s is already mounted on %s",
2464 bb_path_mtab_file,
2465 mp->mnt_fsname, mp->mnt_dir);
2466 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002467 } else {
2468 // ...mount this thing
2469 if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2470 // Count number of failed mounts
2471 rc++;
2472 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002473 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002474 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002475 }
2476 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002477
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002478 // End of fstab/mtab is reached.
2479 // Were we looking for something specific?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002480 if (argv[0]) { // yes
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002481 unsigned long l;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002482
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002483 // If we didn't find anything, complain
2484 if (!mtcur->mnt_fsname)
2485 bb_error_msg_and_die("can't find %s in %s",
2486 argv[0], fstabname);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002487
2488 // What happens when we try to "mount swap_partition"?
2489 // (fstab containts "swap_partition swap swap defaults 0 0")
2490 // util-linux-ng 2.13.1 does this:
2491 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2492 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2493 // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory)
2494 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2495 // exit_group(32) = ?
2496#if 0
2497 // In case we want to simply skip swap partitions:
Denys Vlasenkocf4dc4a2021-10-08 02:16:41 +02002498 l = parse_mount_options(mtcur->mnt_opts, NULL, NULL);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002499 if ((l & MOUNT_SWAP)
2500 // swap is bogus "fstype", parse_mount_options can't check fstypes
2501 || strcasecmp(mtcur->mnt_type, "swap") == 0
2502 ) {
2503 goto ret;
2504 }
2505#endif
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002506 if (nonroot) {
2507 // fstab must have "users" or "user"
Denys Vlasenkocf4dc4a2021-10-08 02:16:41 +02002508 l = parse_mount_options(mtcur->mnt_opts, NULL, NULL);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002509 if (!(l & MOUNT_USERS))
James Byrne69374872019-07-02 11:35:03 +02002510 bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002511 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002512
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002513 //util-linux-2.12 does not do this check.
2514 //// If nothing is mounted on this directory...
2515 //// (otherwise repeated "mount FOO" mounts FOO again)
2516 //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2517 //if (mp) {
2518 // bb_error_msg("according to %s, "
2519 // "%s is already mounted on %s",
2520 // bb_path_mtab_file,
2521 // mp->mnt_fsname, mp->mnt_dir);
2522 //} else {
2523 // ...mount the last thing we found
2524 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2525 append_mount_options(&(mtcur->mnt_opts), cmdopts);
2526 resolve_mount_spec(&mtpair->mnt_fsname);
2527 rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2528 if (ENABLE_FEATURE_CLEAN_UP)
2529 free(mtcur->mnt_opts);
2530 //}
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002531 }
2532
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002533 //ret:
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002534 if (ENABLE_FEATURE_CLEAN_UP)
2535 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002536 if (ENABLE_FEATURE_CLEAN_UP) {
2537 free(storage_path);
2538 free(cmdopts);
2539 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002540
2541//TODO: exitcode should be ORed mask of (from "man mount"):
2542// 0 success
2543// 1 incorrect invocation or permissions
2544// 2 system error (out of memory, cannot fork, no more loop devices)
2545// 4 internal mount bug or missing nfs support in mount
2546// 8 user interrupt
2547//16 problems writing or locking /etc/mtab
2548//32 mount failure
2549//64 some mount succeeded
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002550 return rc;
2551}