blob: cb40c802db1f48984cec05b5d5b7d7aadb5ea876 [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
21//config: bool "mount"
22//config: default y
23//config: select PLATFORM_LINUX
24//config: help
25//config: All files and filesystems in Unix are arranged into one big directory
26//config: tree. The 'mount' utility is used to graft a filesystem onto a
27//config: particular part of the tree. A filesystem can either live on a block
28//config: device, or it can be accessible over the network, as is the case with
29//config: NFS filesystems. Most people using BusyBox will also want to enable
30//config: the 'mount' utility.
31//config:
32//config:config FEATURE_MOUNT_FAKE
33//config: bool "Support option -f"
34//config: default y
35//config: depends on MOUNT
36//config: help
37//config: Enable support for faking a file system mount.
38//config:
39//config:config FEATURE_MOUNT_VERBOSE
40//config: bool "Support option -v"
41//config: default y
42//config: depends on MOUNT
43//config: help
44//config: Enable multi-level -v[vv...] verbose messages. Useful if you
45//config: debug mount problems and want to see what is exactly passed
46//config: to the kernel.
47//config:
48//config:config FEATURE_MOUNT_HELPERS
49//config: bool "Support mount helpers"
50//config: default n
51//config: depends on MOUNT
52//config: help
53//config: Enable mounting of virtual file systems via external helpers.
54//config: E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call
55//config: "obexfs -b00.11.22.33.44.55 /mnt"
56//config: Also "mount -t sometype [-o opts] fs /mnt" will try
57//config: "sometype [-o opts] fs /mnt" if simple mount syscall fails.
58//config: The idea is to use such virtual filesystems in /etc/fstab.
59//config:
60//config:config FEATURE_MOUNT_LABEL
61//config: bool "Support specifying devices by label or UUID"
62//config: default y
63//config: depends on MOUNT
64//config: select VOLUMEID
65//config: help
66//config: This allows for specifying a device by label or uuid, rather than by
67//config: name. This feature utilizes the same functionality as blkid/findfs.
68//config: This also enables label or uuid support for swapon.
69//config:
70//config:config FEATURE_MOUNT_NFS
71//config: bool "Support mounting NFS file systems on Linux < 2.6.23"
72//config: default n
73//config: depends on MOUNT
74//config: select FEATURE_HAVE_RPC
75//config: select FEATURE_SYSLOG
76//config: help
77//config: Enable mounting of NFS file systems on Linux kernels prior
78//config: to version 2.6.23. Note that in this case mounting of NFS
79//config: over IPv6 will not be possible.
80//config:
81//config: Note that this option links in RPC support from libc,
82//config: which is rather large (~10 kbytes on uclibc).
83//config:
84//config:config FEATURE_MOUNT_CIFS
85//config: bool "Support mounting CIFS/SMB file systems"
86//config: default y
87//config: depends on MOUNT
88//config: help
89//config: Enable support for samba mounts.
90//config:
91//config:config FEATURE_MOUNT_FLAGS
92//config: depends on MOUNT
93//config: bool "Support lots of -o flags in mount"
94//config: default y
95//config: help
96//config: Without this, mount only supports ro/rw/remount. With this, it
97//config: supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime,
98//config: noatime, diratime, nodiratime, loud, bind, move, shared, slave,
99//config: private, unbindable, rshared, rslave, rprivate, and runbindable.
100//config:
101//config:config FEATURE_MOUNT_FSTAB
102//config: depends on MOUNT
103//config: bool "Support /etc/fstab and -a"
104//config: default y
105//config: help
106//config: Support mount all and looking for files in /etc/fstab.
107//config:
108//config:config FEATURE_MOUNT_OTHERTAB
109//config: depends on FEATURE_MOUNT_FSTAB
110//config: bool "Support -T <alt_fstab>"
111//config: default y
112//config: help
113//config: Support mount -T (specifying an alternate fstab)
114
Pere Orga5bc8c002011-04-11 03:29:49 +0200115//usage:#define mount_trivial_usage
Isaac Dunham7b434a62015-03-11 16:07:24 +0100116//usage: "[OPTIONS] [-o OPT] DEVICE NODE"
Pere Orga5bc8c002011-04-11 03:29:49 +0200117//usage:#define mount_full_usage "\n\n"
118//usage: "Mount a filesystem. Filesystem autodetection requires /proc.\n"
Pere Orga5bc8c002011-04-11 03:29:49 +0200119//usage: "\n -a Mount all filesystems in fstab"
120//usage: IF_FEATURE_MOUNT_FAKE(
121//usage: IF_FEATURE_MTAB_SUPPORT(
122//usage: "\n -f Update /etc/mtab, but don't mount"
123//usage: )
124//usage: IF_NOT_FEATURE_MTAB_SUPPORT(
125//usage: "\n -f Dry run"
126//usage: )
127//usage: )
128//usage: IF_FEATURE_MOUNT_HELPERS(
129//usage: "\n -i Don't run mount helper"
130//usage: )
131//usage: IF_FEATURE_MTAB_SUPPORT(
132//usage: "\n -n Don't update /etc/mtab"
133//usage: )
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100134//usage: IF_FEATURE_MOUNT_VERBOSE(
135//usage: "\n -v Verbose"
136//usage: )
137////usage: "\n -s Sloppy (ignored)"
Pere Orga5bc8c002011-04-11 03:29:49 +0200138//usage: "\n -r Read-only mount"
Isaac Dunham7b434a62015-03-11 16:07:24 +0100139////usage: "\n -w Read-write mount (default)"
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +0100140//usage: "\n -t FSTYPE[,...] Filesystem type(s)"
Isaac Dunham7b434a62015-03-11 16:07:24 +0100141//usage: IF_FEATURE_MOUNT_OTHERTAB(
142//usage: "\n -T FILE Read FILE instead of /etc/fstab"
143//usage: )
Pere Orga5bc8c002011-04-11 03:29:49 +0200144//usage: "\n -O OPT Mount only filesystems with option OPT (-a only)"
145//usage: "\n-o OPT:"
146//usage: IF_FEATURE_MOUNT_LOOP(
147//usage: "\n loop Ignored (loop devices are autodetected)"
148//usage: )
149//usage: IF_FEATURE_MOUNT_FLAGS(
150//usage: "\n [a]sync Writes are [a]synchronous"
151//usage: "\n [no]atime Disable/enable updates to inode access times"
152//usage: "\n [no]diratime Disable/enable atime updates to directories"
153//usage: "\n [no]relatime Disable/enable atime updates relative to modification time"
154//usage: "\n [no]dev (Dis)allow use of special device files"
155//usage: "\n [no]exec (Dis)allow use of executable files"
156//usage: "\n [no]suid (Dis)allow set-user-id-root programs"
157//usage: "\n [r]shared Convert [recursively] to a shared subtree"
158//usage: "\n [r]slave Convert [recursively] to a slave subtree"
159//usage: "\n [r]private Convert [recursively] to a private subtree"
160//usage: "\n [un]bindable Make mount point [un]able to be bind mounted"
161//usage: "\n [r]bind Bind a file or directory [recursively] to another location"
162//usage: "\n move Relocate an existing mount point"
163//usage: )
164//usage: "\n remount Remount a mounted filesystem, changing flags"
Isaac Dunham7b434a62015-03-11 16:07:24 +0100165//usage: "\n ro Same as -r"
Pere Orga5bc8c002011-04-11 03:29:49 +0200166//usage: "\n"
167//usage: "\nThere are filesystem-specific -o flags."
168//usage:
169//usage:#define mount_example_usage
170//usage: "$ mount\n"
171//usage: "/dev/hda3 on / type minix (rw)\n"
172//usage: "proc on /proc type proc (rw)\n"
173//usage: "devpts on /dev/pts type devpts (rw)\n"
174//usage: "$ mount /dev/fd0 /mnt -t msdos -o ro\n"
175//usage: "$ mount /tmp/diskimage /opt -t ext2 -o loop\n"
176//usage: "$ mount cd_image.iso mydir\n"
177//usage:#define mount_notes_usage
178//usage: "Returns 0 for success, number of failed mounts for -a, or errno for one mount."
179
Eric Andersencc8ed391999-10-05 16:24:54 +0000180#include <mntent.h>
Bernhard Reutner-Fischerf4701962008-01-27 12:50:12 +0000181#include <syslog.h>
Denys Vlasenkoda49f582009-07-08 02:58:38 +0200182#include <sys/mount.h>
Denys Vlasenko102ff762009-11-21 17:14:08 +0100183// Grab more as needed from util-linux's mount/mount_constants.h
184#ifndef MS_DIRSYNC
185# define MS_DIRSYNC (1 << 7) // Directory modifications are synchronous
186#endif
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200187#ifndef MS_UNION
188# define MS_UNION (1 << 8)
189#endif
Denys Vlasenkoda49f582009-07-08 02:58:38 +0200190#ifndef MS_BIND
191# define MS_BIND (1 << 12)
192#endif
193#ifndef MS_MOVE
194# define MS_MOVE (1 << 13)
195#endif
196#ifndef MS_RECURSIVE
197# define MS_RECURSIVE (1 << 14)
198#endif
199#ifndef MS_SILENT
200# define MS_SILENT (1 << 15)
201#endif
Denys Vlasenko102ff762009-11-21 17:14:08 +0100202// The shared subtree stuff, which went in around 2.6.15
Denys Vlasenkoda49f582009-07-08 02:58:38 +0200203#ifndef MS_UNBINDABLE
204# define MS_UNBINDABLE (1 << 17)
205#endif
206#ifndef MS_PRIVATE
207# define MS_PRIVATE (1 << 18)
208#endif
209#ifndef MS_SLAVE
210# define MS_SLAVE (1 << 19)
211#endif
212#ifndef MS_SHARED
213# define MS_SHARED (1 << 20)
214#endif
215#ifndef MS_RELATIME
216# define MS_RELATIME (1 << 21)
217#endif
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200218#ifndef MS_STRICTATIME
219# define MS_STRICTATIME (1 << 24)
220#endif
221
222/* Any ~MS_FOO value has this bit set: */
223#define BB_MS_INVERTED_VALUE (1u << 31)
Eric Andersenbd22ed82000-07-08 18:55:24 +0000224
Denys Vlasenko102ff762009-11-21 17:14:08 +0100225#include "libbb.h"
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000226#if ENABLE_FEATURE_MOUNT_LABEL
Natanael Copa9aff2992009-09-20 04:28:22 +0200227# include "volume_id.h"
228#else
229# define resolve_mount_spec(fsname) ((void)0)
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000230#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000231
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000232// Needed for nfs support only
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000233#include <sys/utsname.h>
234#undef TRUE
235#undef FALSE
Denys Vlasenkocc428142009-12-16 02:06:56 +0100236#if ENABLE_FEATURE_MOUNT_NFS
237/* This is just a warning of a common mistake. Possibly this should be a
238 * uclibc faq entry rather than in busybox... */
239# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
240# error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
241# endif
242# include <rpc/rpc.h>
243# include <rpc/pmap_prot.h>
244# include <rpc/pmap_clnt.h>
245#endif
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000246
247
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000248#if defined(__dietlibc__)
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000249// 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
250// dietlibc-0.30 does not have implementation of getmntent_r()
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +0000251static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000252 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000253{
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000254 struct mntent* ment = getmntent(stream);
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +0000255 return memcpy(result, ment, sizeof(*ment));
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000256}
257#endif
258
259
Rob Landleydc0955b2006-03-14 18:16:25 +0000260// Not real flags, but we want to be able to check for this.
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000261enum {
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000262 MOUNT_USERS = (1 << 28) * ENABLE_DESKTOP,
263 MOUNT_NOAUTO = (1 << 29),
264 MOUNT_SWAP = (1 << 30),
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000265};
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000266
267
Isaac Dunham7b434a62015-03-11 16:07:24 +0100268#define OPTION_STR "o:t:rwanfvsiO:" IF_FEATURE_MOUNT_OTHERTAB("T:")
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000269enum {
270 OPT_o = (1 << 0),
271 OPT_t = (1 << 1),
272 OPT_r = (1 << 2),
273 OPT_w = (1 << 3),
274 OPT_a = (1 << 4),
275 OPT_n = (1 << 5),
276 OPT_f = (1 << 6),
277 OPT_v = (1 << 7),
278 OPT_s = (1 << 8),
279 OPT_i = (1 << 9),
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +0000280 OPT_O = (1 << 10),
Isaac Dunham7b434a62015-03-11 16:07:24 +0100281 OPT_T = (1 << 11),
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000282};
283
284#if ENABLE_FEATURE_MTAB_SUPPORT
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200285#define USE_MTAB (!(option_mask32 & OPT_n))
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000286#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200287#define USE_MTAB 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000288#endif
289
290#if ENABLE_FEATURE_MOUNT_FAKE
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200291#define FAKE_IT (option_mask32 & OPT_f)
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000292#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200293#define FAKE_IT 0
294#endif
295
296#if ENABLE_FEATURE_MOUNT_HELPERS
297#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
298#else
299#define HELPERS_ALLOWED 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000300#endif
301
302
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000303// TODO: more "user" flag compatibility.
304// "user" option (from mount manpage):
305// Only the user that mounted a filesystem can unmount it again.
306// If any user should be able to unmount, then use users instead of user
307// in the fstab line. The owner option is similar to the user option,
308// with the restriction that the user must be the owner of the special file.
309// This may be useful e.g. for /dev/fd if a login script makes
310// the console user owner of this device.
Rob Landley3ba7bd12006-08-09 19:51:13 +0000311
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000312// Standard mount options (from -o options or --options),
313// with corresponding flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000314static const int32_t mount_options[] = {
Rob Landleye3781b72006-08-08 01:39:49 +0000315 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
Rob Landleydc0955b2006-03-14 18:16:25 +0000316
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000317 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000318 /* "loop" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000319 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000320
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000321 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000322 /* "defaults" */ 0,
323 /* "quiet" 0 - do not filter out, vfat wants to see it */
324 /* "noauto" */ MOUNT_NOAUTO,
325 /* "sw" */ MOUNT_SWAP,
326 /* "swap" */ MOUNT_SWAP,
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000327 IF_DESKTOP(/* "user" */ MOUNT_USERS,)
328 IF_DESKTOP(/* "users" */ MOUNT_USERS,)
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000329 /* "_netdev" */ 0,
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100330 IF_DESKTOP(/* "comment=" */ 0,) /* systemd uses this in fstab */
Rob Landleye3781b72006-08-08 01:39:49 +0000331 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000332
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000333 IF_FEATURE_MOUNT_FLAGS(
Rob Landleye3781b72006-08-08 01:39:49 +0000334 // vfs flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000335 /* "nosuid" */ MS_NOSUID,
336 /* "suid" */ ~MS_NOSUID,
337 /* "dev" */ ~MS_NODEV,
338 /* "nodev" */ MS_NODEV,
339 /* "exec" */ ~MS_NOEXEC,
340 /* "noexec" */ MS_NOEXEC,
341 /* "sync" */ MS_SYNCHRONOUS,
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +0000342 /* "dirsync" */ MS_DIRSYNC,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000343 /* "async" */ ~MS_SYNCHRONOUS,
344 /* "atime" */ ~MS_NOATIME,
345 /* "noatime" */ MS_NOATIME,
346 /* "diratime" */ ~MS_NODIRATIME,
347 /* "nodiratime" */ MS_NODIRATIME,
Denis Vlasenko580ce2d2008-07-08 02:56:53 +0000348 /* "mand" */ MS_MANDLOCK,
349 /* "nomand" */ ~MS_MANDLOCK,
Bernhard Reutner-Fischerfb5902c2008-08-06 18:14:38 +0000350 /* "relatime" */ MS_RELATIME,
351 /* "norelatime" */ ~MS_RELATIME,
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200352 /* "strictatime" */ MS_STRICTATIME,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000353 /* "loud" */ ~MS_SILENT,
Roman Borisov19311bf2011-03-24 15:08:43 +0300354 /* "rbind" */ MS_BIND|MS_RECURSIVE,
Eric Andersen9601a1c2006-03-20 18:07:50 +0000355
Rob Landleye3781b72006-08-08 01:39:49 +0000356 // action flags
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200357 /* "union" */ MS_UNION,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000358 /* "bind" */ MS_BIND,
359 /* "move" */ MS_MOVE,
360 /* "shared" */ MS_SHARED,
361 /* "slave" */ MS_SLAVE,
362 /* "private" */ MS_PRIVATE,
363 /* "unbindable" */ MS_UNBINDABLE,
364 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
365 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
Roman Borisovd3679d22011-03-23 11:20:25 +0300366 /* "rprivate" */ MS_PRIVATE|MS_RECURSIVE,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000367 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
Rob Landleye3781b72006-08-08 01:39:49 +0000368 )
369
370 // Always understood.
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000371 /* "ro" */ MS_RDONLY, // vfs flag
372 /* "rw" */ ~MS_RDONLY, // vfs flag
373 /* "remount" */ MS_REMOUNT // action flag
Eric Andersencc8ed391999-10-05 16:24:54 +0000374};
375
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000376static const char mount_option_str[] =
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000377 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000378 "loop\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000379 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000380 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000381 "defaults\0"
382 // "quiet\0" - do not filter out, vfat wants to see it
383 "noauto\0"
384 "sw\0"
385 "swap\0"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000386 IF_DESKTOP("user\0")
387 IF_DESKTOP("users\0")
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000388 "_netdev\0"
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100389 IF_DESKTOP("comment=\0") /* systemd uses this in fstab */
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000390 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000391 IF_FEATURE_MOUNT_FLAGS(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000392 // vfs flags
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000393 "nosuid\0"
394 "suid\0"
395 "dev\0"
396 "nodev\0"
397 "exec\0"
398 "noexec\0"
399 "sync\0"
400 "dirsync\0"
401 "async\0"
402 "atime\0"
403 "noatime\0"
404 "diratime\0"
405 "nodiratime\0"
406 "mand\0"
407 "nomand\0"
408 "relatime\0"
409 "norelatime\0"
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200410 "strictatime\0"
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000411 "loud\0"
Roman Borisov19311bf2011-03-24 15:08:43 +0300412 "rbind\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000413
414 // action flags
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200415 "union\0"
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000416 "bind\0"
417 "move\0"
Roman Borisov945fd172011-02-25 14:50:39 +0300418 "make-shared\0"
419 "make-slave\0"
420 "make-private\0"
421 "make-unbindable\0"
422 "make-rshared\0"
423 "make-rslave\0"
424 "make-rprivate\0"
425 "make-runbindable\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000426 )
427
428 // Always understood.
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000429 "ro\0" // vfs flag
430 "rw\0" // vfs flag
431 "remount\0" // action flag
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000432;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000433
Denis Vlasenkof732e962008-02-18 12:07:49 +0000434
435struct globals {
436#if ENABLE_FEATURE_MOUNT_NFS
437 smalluint nfs_mount_version;
438#endif
439#if ENABLE_FEATURE_MOUNT_VERBOSE
440 unsigned verbose;
441#endif
442 llist_t *fslist;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000443 char getmntent_buf[1];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100444} FIX_ALIASING;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000445enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
Denis Vlasenkof732e962008-02-18 12:07:49 +0000446#define G (*(struct globals*)&bb_common_bufsiz1)
447#define nfs_mount_version (G.nfs_mount_version)
Denis Vlasenkob4133682008-02-18 13:05:38 +0000448#if ENABLE_FEATURE_MOUNT_VERBOSE
Denis Vlasenkof732e962008-02-18 12:07:49 +0000449#define verbose (G.verbose )
Denis Vlasenkob4133682008-02-18 13:05:38 +0000450#else
451#define verbose 0
452#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000453#define fslist (G.fslist )
454#define getmntent_buf (G.getmntent_buf )
Denys Vlasenko16714242011-09-21 01:59:15 +0200455#define INIT_G() do { } while (0)
Denis Vlasenkof732e962008-02-18 12:07:49 +0000456
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100457#if ENABLE_FEATURE_MTAB_SUPPORT
458/*
459 * update_mtab_entry_on_move() is used to update entry in case of mount --move.
460 * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
461 * input mntent and replace it by new one.
462 */
463static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
464{
465 struct mntent *entries, *m;
466 int i, count;
467 FILE *mountTable;
468
469 mountTable = setmntent(bb_path_mtab_file, "r");
470 if (!mountTable) {
471 bb_perror_msg(bb_path_mtab_file);
472 return;
473 }
474
475 entries = NULL;
476 count = 0;
477 while ((m = getmntent(mountTable)) != NULL) {
478 entries = xrealloc_vector(entries, 3, count);
479 entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
480 entries[count].mnt_dir = xstrdup(m->mnt_dir);
481 entries[count].mnt_type = xstrdup(m->mnt_type);
482 entries[count].mnt_opts = xstrdup(m->mnt_opts);
483 entries[count].mnt_freq = m->mnt_freq;
484 entries[count].mnt_passno = m->mnt_passno;
485 count++;
486 }
487 endmntent(mountTable);
488
489 mountTable = setmntent(bb_path_mtab_file, "w");
490 if (mountTable) {
491 for (i = 0; i < count; i++) {
492 if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
493 addmntent(mountTable, &entries[i]);
494 else
495 addmntent(mountTable, mp);
496 }
497 endmntent(mountTable);
498 } else if (errno != EROFS)
499 bb_perror_msg(bb_path_mtab_file);
500
501 if (ENABLE_FEATURE_CLEAN_UP) {
502 for (i = 0; i < count; i++) {
503 free(entries[i].mnt_fsname);
504 free(entries[i].mnt_dir);
505 free(entries[i].mnt_type);
506 free(entries[i].mnt_opts);
507 }
508 free(entries);
509 }
510}
511#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000512
513#if ENABLE_FEATURE_MOUNT_VERBOSE
514static int verbose_mount(const char *source, const char *target,
515 const char *filesystemtype,
516 unsigned long mountflags, const void *data)
517{
518 int rc;
519
520 errno = 0;
521 rc = mount(source, target, filesystemtype, mountflags, data);
522 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000523 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000524 source, target, filesystemtype,
525 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000526 return rc;
527}
528#else
529#define verbose_mount(...) mount(__VA_ARGS__)
530#endif
531
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000532// Append mount options to string
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000533static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000534{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000535 if (*oldopts && **oldopts) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000536 // Do not insert options which are already there
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000537 while (newopts[0]) {
538 char *p;
539 int len = strlen(newopts);
540 p = strchr(newopts, ',');
541 if (p) len = p - newopts;
542 p = *oldopts;
543 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000544 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000545 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000546 goto skip;
547 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000548 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000549 p++;
550 }
551 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
552 free(*oldopts);
553 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000554 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000555 newopts += len;
556 while (newopts[0] == ',') newopts++;
557 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000558 } else {
559 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000560 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000561 }
562}
563
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000564// Use the mount_options list to parse options into flags.
Alexander Shishkin77650952010-10-28 06:10:03 +0200565// Also update list of unrecognized options if unrecognized != NULL
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200566static unsigned long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000567{
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200568 unsigned long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000569
Rob Landley6a6798b2005-08-10 20:35:54 +0000570 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000571 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000572 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000573 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000574 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000575
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000576 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000577
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000578// FIXME: use hasmntopt()
Rob Landley6a6798b2005-08-10 20:35:54 +0000579 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000580 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200581 unsigned opt_len = strlen(option_str);
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100582
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200583 if (strncasecmp(option_str, options, opt_len) == 0
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100584 && (options[opt_len] == '\0'
585 /* or is it "comment=" thingy in fstab? */
586 IF_FEATURE_MOUNT_FSTAB(IF_DESKTOP( || option_str[opt_len-1] == '=' ))
587 )
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200588 ) {
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200589 unsigned long fl = mount_options[i];
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200590 if (fl & BB_MS_INVERTED_VALUE)
Alexander Shishkin77650952010-10-28 06:10:03 +0200591 flags &= fl;
592 else
593 flags |= fl;
594 goto found;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000595 }
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200596 option_str += opt_len + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000597 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200598 // We did not recognize this option.
599 // If "unrecognized" is not NULL, append option there.
600 // Note that we should not append *empty* option -
601 // in this case we want to pass NULL, not "", to "data"
602 // parameter of mount(2) syscall.
603 // This is crucial for filesystems that don't accept
604 // any arbitrary mount options, like cgroup fs:
605 // "mount -t cgroup none /mnt"
606 if (options[0] && unrecognized) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000607 // Add it to strflags, to pass on to kernel
Alexander Shishkin77650952010-10-28 06:10:03 +0200608 char *p = *unrecognized;
609 unsigned len = p ? strlen(p) : 0;
610 *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000611
Rob Landley6a6798b2005-08-10 20:35:54 +0000612 // Comma separated if it's not the first one
Alexander Shishkin77650952010-10-28 06:10:03 +0200613 if (len) p[len++] = ',';
614 strcpy(p + len, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000615 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200616 found:
Denis Vlasenko2535f122007-09-15 13:28:30 +0000617 if (!comma)
618 break;
619 // Advance to next option
620 *comma = ',';
621 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000622 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000623
Rob Landleydc0955b2006-03-14 18:16:25 +0000624 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000625}
626
Rob Landleydc0955b2006-03-14 18:16:25 +0000627// Return a list of all block device backed filesystems
Rob Landleydc0955b2006-03-14 18:16:25 +0000628static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000629{
Denis Vlasenko87468852007-04-13 23:22:00 +0000630 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000631 "/etc/filesystems",
632 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000633 };
634 char *fs, *buf;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200635 llist_t *list = NULL;
Rob Landleydc0955b2006-03-14 18:16:25 +0000636 int i;
637 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000638
Denis Vlasenko87468852007-04-13 23:22:00 +0000639 for (i = 0; i < 2; i++) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000640 f = fopen_for_read(filesystems[i]);
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000641 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000642
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000643 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denys Vlasenko8dff01d2015-03-12 17:48:34 +0100644 if (is_prefixed_with(buf, "nodev") && isspace(buf[5]))
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100645 goto next;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000646 fs = skip_whitespace(buf);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200647 if (*fs == '#' || *fs == '*' || !*fs)
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100648 goto next;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000649
Denis Vlasenko372686b2006-10-12 22:42:33 +0000650 llist_add_to_end(&list, xstrdup(fs));
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100651 next:
Denis Vlasenko372686b2006-10-12 22:42:33 +0000652 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000653 }
654 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
655 }
656
657 return list;
658}
659
Rob Landleydc0955b2006-03-14 18:16:25 +0000660#if ENABLE_FEATURE_CLEAN_UP
661static void delete_block_backed_filesystems(void)
662{
Rob Landleya6b5b602006-05-08 19:03:07 +0000663 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000664}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000665#else
666void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000667#endif
668
Rob Landleydc0955b2006-03-14 18:16:25 +0000669// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000670// NB: mp->xxx fields may be trashed on exit
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200671static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000672{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000673 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000674
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200675 if (FAKE_IT) {
Denis Vlasenkob4133682008-02-18 13:05:38 +0000676 if (verbose >= 2)
677 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
678 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
679 vfsflags, filteropts);
680 goto mtab;
681 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000682
Rob Landleydc0955b2006-03-14 18:16:25 +0000683 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000684 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000685 errno = 0;
686 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000687 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000688
689 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000690 // helper program mount.<mnt_type>
Denys Vlasenkoba986032009-09-15 23:00:09 +0200691 if (HELPERS_ALLOWED && rc && mp->mnt_type) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200692 char *args[8];
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000693 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000694 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000695 rc = 1;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200696 if (FAKE_IT)
697 args[rc++] = (char *)"-f";
698 if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
699 args[rc++] = (char *)"-n";
Denis Vlasenko5c329932009-04-12 12:16:21 +0000700 args[rc++] = mp->mnt_fsname;
701 args[rc++] = mp->mnt_dir;
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000702 if (filteropts) {
703 args[rc++] = (char *)"-o";
704 args[rc++] = filteropts;
705 }
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000706 args[rc] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +0100707 rc = spawn_and_wait(args);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000708 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000709 if (!rc)
710 break;
711 errno = errno_save;
712 }
713
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000714 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000715 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000716 if (!(vfsflags & MS_SILENT))
717 bb_error_msg("%s is write-protected, mounting read-only",
718 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000719 vfsflags |= MS_RDONLY;
720 }
721
Rob Landleydc0955b2006-03-14 18:16:25 +0000722 // Abort entirely if permission denied.
723
724 if (rc && errno == EPERM)
725 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
726
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000727 // If the mount was successful, and we're maintaining an old-style
728 // mtab file by hand, add the new entry to it now.
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000729 mtab:
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200730 if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000731 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000732 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000733 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000734 int i;
735
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000736 if (!mountTable) {
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100737 bb_perror_msg(bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000738 goto ret;
739 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000740
741 // Add vfs string flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000742 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
743 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
744 append_mount_options(&(mp->mnt_opts), option_str);
745 option_str += strlen(option_str) + 1;
746 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000747
748 // Remove trailing / (if any) from directory we mounted on
Denis Vlasenko727ef942006-09-14 13:19:19 +0000749 i = strlen(mp->mnt_dir) - 1;
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100750 while (i > 0 && mp->mnt_dir[i] == '/')
Denys Vlasenkoc6450c92011-02-28 11:09:49 +0100751 mp->mnt_dir[i--] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000752
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000753 // Convert to canonical pathnames as needed
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000754 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100755 fsname = NULL;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000756 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000757 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000758 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000759 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000760 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000761
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100762 // Write and close
763#if ENABLE_FEATURE_MTAB_SUPPORT
764 if (vfsflags & MS_MOVE)
765 update_mtab_entry_on_move(mp);
766 else
767#endif
768 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000769 endmntent(mountTable);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100770
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000771 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000772 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000773 free(fsname);
774 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000775 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000776 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000777 return rc;
778}
779
Denis Vlasenko25098f72006-09-14 15:46:33 +0000780#if ENABLE_FEATURE_MOUNT_NFS
781
782/*
783 * Linux NFS mount
784 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
785 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +0200786 * Licensed under GPLv2, see file LICENSE in this source tree.
Denis Vlasenko25098f72006-09-14 15:46:33 +0000787 *
788 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
789 * numbers to be specified on the command line.
790 *
791 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
792 * Omit the call to connect() for Linux version 1.3.11 or later.
793 *
794 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
795 * Implemented the "bg", "fg" and "retry" mount options for NFS.
796 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000797 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000798 * - added Native Language Support
799 *
800 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
801 * plus NFSv3 stuff.
802 */
803
Denis Vlasenko25098f72006-09-14 15:46:33 +0000804#define MOUNTPORT 635
805#define MNTPATHLEN 1024
806#define MNTNAMLEN 255
807#define FHSIZE 32
808#define FHSIZE3 64
809
810typedef char fhandle[FHSIZE];
811
812typedef struct {
813 unsigned int fhandle3_len;
814 char *fhandle3_val;
815} fhandle3;
816
817enum mountstat3 {
818 MNT_OK = 0,
819 MNT3ERR_PERM = 1,
820 MNT3ERR_NOENT = 2,
821 MNT3ERR_IO = 5,
822 MNT3ERR_ACCES = 13,
823 MNT3ERR_NOTDIR = 20,
824 MNT3ERR_INVAL = 22,
825 MNT3ERR_NAMETOOLONG = 63,
826 MNT3ERR_NOTSUPP = 10004,
827 MNT3ERR_SERVERFAULT = 10006,
828};
829typedef enum mountstat3 mountstat3;
830
831struct fhstatus {
832 unsigned int fhs_status;
833 union {
834 fhandle fhs_fhandle;
835 } fhstatus_u;
836};
837typedef struct fhstatus fhstatus;
838
839struct mountres3_ok {
840 fhandle3 fhandle;
841 struct {
842 unsigned int auth_flavours_len;
843 char *auth_flavours_val;
844 } auth_flavours;
845};
846typedef struct mountres3_ok mountres3_ok;
847
848struct mountres3 {
849 mountstat3 fhs_status;
850 union {
851 mountres3_ok mountinfo;
852 } mountres3_u;
853};
854typedef struct mountres3 mountres3;
855
856typedef char *dirpath;
857
858typedef char *name;
859
860typedef struct mountbody *mountlist;
861
862struct mountbody {
863 name ml_hostname;
864 dirpath ml_directory;
865 mountlist ml_next;
866};
867typedef struct mountbody mountbody;
868
869typedef struct groupnode *groups;
870
871struct groupnode {
872 name gr_name;
873 groups gr_next;
874};
875typedef struct groupnode groupnode;
876
877typedef struct exportnode *exports;
878
879struct exportnode {
880 dirpath ex_dir;
881 groups ex_groups;
882 exports ex_next;
883};
884typedef struct exportnode exportnode;
885
886struct ppathcnf {
887 int pc_link_max;
888 short pc_max_canon;
889 short pc_max_input;
890 short pc_name_max;
891 short pc_path_max;
892 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000893 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000894 char pc_xxx;
895 short pc_mask[2];
896};
897typedef struct ppathcnf ppathcnf;
898
899#define MOUNTPROG 100005
900#define MOUNTVERS 1
901
902#define MOUNTPROC_NULL 0
903#define MOUNTPROC_MNT 1
904#define MOUNTPROC_DUMP 2
905#define MOUNTPROC_UMNT 3
906#define MOUNTPROC_UMNTALL 4
907#define MOUNTPROC_EXPORT 5
908#define MOUNTPROC_EXPORTALL 6
909
910#define MOUNTVERS_POSIX 2
911
912#define MOUNTPROC_PATHCONF 7
913
914#define MOUNT_V3 3
915
916#define MOUNTPROC3_NULL 0
917#define MOUNTPROC3_MNT 1
918#define MOUNTPROC3_DUMP 2
919#define MOUNTPROC3_UMNT 3
920#define MOUNTPROC3_UMNTALL 4
921#define MOUNTPROC3_EXPORT 5
922
923enum {
924#ifndef NFS_FHSIZE
925 NFS_FHSIZE = 32,
926#endif
927#ifndef NFS_PORT
928 NFS_PORT = 2049
929#endif
930};
931
Denis Vlasenko25098f72006-09-14 15:46:33 +0000932/*
933 * We want to be able to compile mount on old kernels in such a way
934 * that the binary will work well on more recent kernels.
935 * Thus, if necessary we teach nfsmount.c the structure of new fields
936 * that will come later.
937 *
938 * Moreover, the new kernel includes conflict with glibc includes
939 * so it is easiest to ignore the kernel altogether (at compile time).
940 */
941
942struct nfs2_fh {
Dave Lovefae473c2011-11-10 15:19:25 +0100943 char data[32];
Denis Vlasenko25098f72006-09-14 15:46:33 +0000944};
945struct nfs3_fh {
Dave Lovefae473c2011-11-10 15:19:25 +0100946 unsigned short size;
947 unsigned char data[64];
Denis Vlasenko25098f72006-09-14 15:46:33 +0000948};
949
950struct nfs_mount_data {
Dave Lovefae473c2011-11-10 15:19:25 +0100951 int version; /* 1 */
952 int fd; /* 1 */
953 struct nfs2_fh old_root; /* 1 */
954 int flags; /* 1 */
955 int rsize; /* 1 */
956 int wsize; /* 1 */
957 int timeo; /* 1 */
958 int retrans; /* 1 */
959 int acregmin; /* 1 */
960 int acregmax; /* 1 */
961 int acdirmin; /* 1 */
962 int acdirmax; /* 1 */
963 struct sockaddr_in addr; /* 1 */
964 char hostname[256]; /* 1 */
965 int namlen; /* 2 */
966 unsigned int bsize; /* 3 */
967 struct nfs3_fh root; /* 4 */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000968};
969
970/* bits in the flags field */
971enum {
972 NFS_MOUNT_SOFT = 0x0001, /* 1 */
973 NFS_MOUNT_INTR = 0x0002, /* 1 */
974 NFS_MOUNT_SECURE = 0x0004, /* 1 */
975 NFS_MOUNT_POSIX = 0x0008, /* 1 */
976 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
977 NFS_MOUNT_NOAC = 0x0020, /* 1 */
978 NFS_MOUNT_TCP = 0x0040, /* 2 */
979 NFS_MOUNT_VER3 = 0x0080, /* 3 */
980 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000981 NFS_MOUNT_NONLM = 0x0200, /* 3 */
Dave Lovefae473c2011-11-10 15:19:25 +0100982 NFS_MOUNT_NOACL = 0x0800, /* 4 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000983 NFS_MOUNT_NORDIRPLUS = 0x4000
Denis Vlasenko25098f72006-09-14 15:46:33 +0000984};
985
986
987/*
988 * We need to translate between nfs status return values and
989 * the local errno values which may not be the same.
990 *
991 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
992 * "after #include <errno.h> the symbol errno is reserved for any use,
993 * it cannot even be used as a struct tag or field name".
994 */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000995#ifndef EDQUOT
Denys Vlasenkocc428142009-12-16 02:06:56 +0100996# define EDQUOT ENOSPC
Denis Vlasenko25098f72006-09-14 15:46:33 +0000997#endif
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000998/* Convert each NFSERR_BLAH into EBLAH */
Denys Vlasenkocc428142009-12-16 02:06:56 +0100999static const uint8_t nfs_err_stat[] = {
1000 1, 2, 5, 6, 13, 17,
1001 19, 20, 21, 22, 27, 28,
1002 30, 63, 66, 69, 70, 71
1003};
Denys Vlasenkodc1fd2e2010-05-19 17:01:29 +02001004#if ( \
1005 EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \
1006 ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \
1007 EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
1008typedef uint8_t nfs_err_type;
1009#else
1010typedef uint16_t nfs_err_type;
1011#endif
1012static const nfs_err_type nfs_err_errnum[] = {
Denys Vlasenkocc428142009-12-16 02:06:56 +01001013 EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST,
1014 ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC,
1015 EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
Denis Vlasenko25098f72006-09-14 15:46:33 +00001016};
Denis Vlasenko25098f72006-09-14 15:46:33 +00001017static char *nfs_strerror(int status)
1018{
1019 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001020
Denys Vlasenkocc428142009-12-16 02:06:56 +01001021 for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
1022 if (nfs_err_stat[i] == status)
1023 return strerror(nfs_err_errnum[i]);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001024 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001025 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001026}
1027
1028static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
1029{
Alexander Shishkin54779a42010-10-22 13:35:47 +02001030 return xdr_opaque(xdrs, objp, FHSIZE);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001031}
1032
1033static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
1034{
1035 if (!xdr_u_int(xdrs, &objp->fhs_status))
Denys Vlasenko69675782013-01-14 01:34:48 +01001036 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +02001037 if (objp->fhs_status == 0)
1038 return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001039 return TRUE;
1040}
1041
1042static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
1043{
Alexander Shishkin54779a42010-10-22 13:35:47 +02001044 return xdr_string(xdrs, objp, MNTPATHLEN);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001045}
1046
1047static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
1048{
Alexander Shishkin54779a42010-10-22 13:35:47 +02001049 return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
Denys Vlasenko69675782013-01-14 01:34:48 +01001050 (unsigned int *) &objp->fhandle3_len,
1051 FHSIZE3);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001052}
1053
1054static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
1055{
1056 if (!xdr_fhandle3(xdrs, &objp->fhandle))
1057 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +02001058 return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
Denys Vlasenko69675782013-01-14 01:34:48 +01001059 &(objp->auth_flavours.auth_flavours_len),
1060 ~0,
1061 sizeof(int),
1062 (xdrproc_t) xdr_int);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001063}
1064
1065static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
1066{
Alexander Shishkin54779a42010-10-22 13:35:47 +02001067 return xdr_enum(xdrs, (enum_t *) objp);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001068}
1069
1070static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
1071{
1072 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
1073 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +02001074 if (objp->fhs_status == MNT_OK)
1075 return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001076 return TRUE;
1077}
1078
1079#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
1080
Denis Vlasenko25098f72006-09-14 15:46:33 +00001081/*
1082 * Unfortunately, the kernel prints annoying console messages
1083 * in case of an unexpected nfs mount version (instead of
1084 * just returning some error). Therefore we'll have to try
1085 * and figure out what version the kernel expects.
1086 *
1087 * Variables:
1088 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
1089 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
1090 * nfs_mount_version: version this source and running kernel can handle
1091 */
1092static void
1093find_kernel_nfs_mount_version(void)
1094{
Denis Vlasenkob9256052007-09-28 10:29:17 +00001095 int kernel_version;
1096
1097 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001098 return;
1099
1100 nfs_mount_version = 4; /* default */
1101
1102 kernel_version = get_linux_version_code();
1103 if (kernel_version) {
Denys Vlasenkocc428142009-12-16 02:06:56 +01001104 if (kernel_version < KERNEL_VERSION(2,2,18))
Denis Vlasenko25098f72006-09-14 15:46:33 +00001105 nfs_mount_version = 3;
1106 /* else v4 since 2.3.99pre4 */
1107 }
1108}
1109
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +00001110static void
Denis Vlasenkob9256052007-09-28 10:29:17 +00001111get_mountport(struct pmap *pm_mnt,
1112 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001113 long unsigned prog,
1114 long unsigned version,
1115 long unsigned proto,
1116 long unsigned port)
1117{
1118 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001119
1120 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001121/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
1122 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001123 pmap = pmap_getmaps(server_addr);
1124
1125 if (version > MAX_NFSPROT)
1126 version = MAX_NFSPROT;
1127 if (!prog)
1128 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001129 pm_mnt->pm_prog = prog;
1130 pm_mnt->pm_vers = version;
1131 pm_mnt->pm_prot = proto;
1132 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001133
Denis Vlasenko25098f72006-09-14 15:46:33 +00001134 while (pmap) {
1135 if (pmap->pml_map.pm_prog != prog)
1136 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001137 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001138 goto next;
1139 if (version > 2 && pmap->pml_map.pm_vers != version)
1140 goto next;
1141 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1142 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001143 if (pmap->pml_map.pm_vers > MAX_NFSPROT
1144 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1145 || (port && pmap->pml_map.pm_port != port)
1146 ) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001147 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001148 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001149 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1150 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001151 pmap = pmap->pml_next;
1152 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001153 if (!pm_mnt->pm_vers)
1154 pm_mnt->pm_vers = MOUNTVERS;
1155 if (!pm_mnt->pm_port)
1156 pm_mnt->pm_port = MOUNTPORT;
1157 if (!pm_mnt->pm_prot)
1158 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001159}
1160
Denis Vlasenkof0000652007-09-04 18:30:26 +00001161#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001162static int daemonize(void)
1163{
Denis Vlasenko25098f72006-09-14 15:46:33 +00001164 int pid = fork();
1165 if (pid < 0) /* error */
1166 return -errno;
1167 if (pid > 0) /* parent */
1168 return 0;
1169 /* child */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001170 close(0);
1171 xopen(bb_dev_null, O_RDWR);
1172 xdup2(0, 1);
1173 xdup2(0, 2);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001174 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +00001175 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001176 logmode = LOGMODE_SYSLOG;
1177 return 1;
1178}
Denis Vlasenkof0000652007-09-04 18:30:26 +00001179#else
1180static inline int daemonize(void) { return -ENOSYS; }
1181#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001182
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001183/* TODO */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001184static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001185{
1186 return 0;
1187}
1188
1189/* RPC strerror analogs are terminally idiotic:
1190 * *mandatory* prefix and \n at end.
1191 * This hopefully helps. Usage:
1192 * error_msg_rpc(clnt_*error*(" ")) */
1193static void error_msg_rpc(const char *msg)
1194{
Denis Vlasenko23514fe2006-09-19 14:07:52 +00001195 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001196 while (msg[0] == ' ' || msg[0] == ':') msg++;
1197 len = strlen(msg);
1198 while (len && msg[len-1] == '\n') len--;
1199 bb_error_msg("%.*s", len, msg);
1200}
1201
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001202/* NB: mp->xxx fields may be trashed on exit */
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001203static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001204{
1205 CLIENT *mclient;
1206 char *hostname;
1207 char *pathname;
1208 char *mounthost;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001209 /* prior to 2.6.23, kernel took NFS options in a form of this struct
1210 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1211 * then data pointer is interpreted as a string. */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001212 struct nfs_mount_data data;
1213 char *opt;
1214 struct hostent *hp;
1215 struct sockaddr_in server_addr;
1216 struct sockaddr_in mount_server_addr;
1217 int msock, fsock;
1218 union {
1219 struct fhstatus nfsv2;
1220 struct mountres3 nfsv3;
1221 } status;
1222 int daemonized;
1223 char *s;
1224 int port;
1225 int mountport;
1226 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001227#if BB_MMU
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001228 smallint bg = 0;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001229#else
1230 enum { bg = 0 };
1231#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001232 int retry;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001233 int mountprog;
1234 int mountvers;
1235 int nfsprog;
1236 int nfsvers;
1237 int retval;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001238 /* these all are one-bit really. gcc 4.3.1 likes this combination: */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001239 smallint tcp;
1240 smallint soft;
1241 int intr;
1242 int posix;
1243 int nocto;
1244 int noac;
1245 int nordirplus;
1246 int nolock;
Dave Lovefae473c2011-11-10 15:19:25 +01001247 int noacl;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001248
1249 find_kernel_nfs_mount_version();
1250
1251 daemonized = 0;
1252 mounthost = NULL;
1253 retval = ETIMEDOUT;
1254 msock = fsock = -1;
1255 mclient = NULL;
1256
1257 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1258
1259 filteropts = xstrdup(filteropts); /* going to trash it later... */
1260
1261 hostname = xstrdup(mp->mnt_fsname);
1262 /* mount_main() guarantees that ':' is there */
1263 s = strchr(hostname, ':');
1264 pathname = s + 1;
1265 *s = '\0';
1266 /* Ignore all but first hostname in replicated mounts
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001267 * until they can be fully supported. (mack@sgi.com) */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001268 s = strchr(hostname, ',');
1269 if (s) {
1270 *s = '\0';
1271 bb_error_msg("warning: multiple hostnames not supported");
1272 }
1273
1274 server_addr.sin_family = AF_INET;
1275 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1276 hp = gethostbyname(hostname);
1277 if (hp == NULL) {
1278 bb_herror_msg("%s", hostname);
1279 goto fail;
1280 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001281 if (hp->h_length != (int)sizeof(struct in_addr)) {
1282 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001283 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001284 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001285 }
1286
1287 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1288
1289 /* add IP address to mtab options for use when unmounting */
1290
1291 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1292 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1293 } else {
1294 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1295 mp->mnt_opts[0] ? "," : "",
1296 inet_ntoa(server_addr.sin_addr));
1297 free(mp->mnt_opts);
1298 mp->mnt_opts = tmp;
1299 }
1300
1301 /* Set default options.
1302 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1303 * let the kernel decide.
1304 * timeo is filled in after we know whether it'll be TCP or UDP. */
1305 memset(&data, 0, sizeof(data));
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001306 data.retrans = 3;
1307 data.acregmin = 3;
1308 data.acregmax = 60;
1309 data.acdirmin = 30;
1310 data.acdirmax = 60;
1311 data.namlen = NAME_MAX;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001312
Denis Vlasenko25098f72006-09-14 15:46:33 +00001313 soft = 0;
1314 intr = 0;
1315 posix = 0;
1316 nocto = 0;
1317 nolock = 0;
1318 noac = 0;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001319 nordirplus = 0;
Dave Lovefae473c2011-11-10 15:19:25 +01001320 noacl = 0;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001321 retry = 10000; /* 10000 minutes ~ 1 week */
Bernhard Reutner-Fischer88206292011-05-04 19:03:30 +02001322 tcp = 1; /* nfs-utils uses tcp per default */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001323
1324 mountprog = MOUNTPROG;
1325 mountvers = 0;
1326 port = 0;
1327 mountport = 0;
1328 nfsprog = 100003;
1329 nfsvers = 0;
1330
1331 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001332 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001333 char *opteq = strchr(opt, '=');
1334 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001335 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001336 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001337 /* 0 */ "rsize\0"
1338 /* 1 */ "wsize\0"
1339 /* 2 */ "timeo\0"
1340 /* 3 */ "retrans\0"
1341 /* 4 */ "acregmin\0"
1342 /* 5 */ "acregmax\0"
1343 /* 6 */ "acdirmin\0"
1344 /* 7 */ "acdirmax\0"
1345 /* 8 */ "actimeo\0"
1346 /* 9 */ "retry\0"
1347 /* 10 */ "port\0"
1348 /* 11 */ "mountport\0"
1349 /* 12 */ "mounthost\0"
1350 /* 13 */ "mountprog\0"
1351 /* 14 */ "mountvers\0"
1352 /* 15 */ "nfsprog\0"
1353 /* 16 */ "nfsvers\0"
1354 /* 17 */ "vers\0"
1355 /* 18 */ "proto\0"
1356 /* 19 */ "namlen\0"
1357 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001358
1359 *opteq++ = '\0';
1360 idx = index_in_strings(options, opt);
1361 switch (idx) {
1362 case 12: // "mounthost"
1363 mounthost = xstrndup(opteq,
1364 strcspn(opteq, " \t\n\r,"));
1365 continue;
1366 case 18: // "proto"
Denys Vlasenko8dff01d2015-03-12 17:48:34 +01001367 if (is_prefixed_with(opteq, "tcp"))
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001368 tcp = 1;
Denys Vlasenko8dff01d2015-03-12 17:48:34 +01001369 else if (is_prefixed_with(opteq, "udp"))
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001370 tcp = 0;
1371 else
1372 bb_error_msg("warning: unrecognized proto= option");
1373 continue;
1374 case 20: // "addr" - ignore
1375 continue;
Peter Korsgaard301fe502011-02-21 17:52:13 +01001376 case -1: // unknown
1377 if (vfsflags & MS_REMOUNT)
1378 continue;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001379 }
1380
Denys Vlasenko77832482010-08-12 14:14:45 +02001381 val = xatoi_positive(opteq);
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001382 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001383 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001384 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001385 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001386 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001387 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001388 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001389 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001390 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001391 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001392 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001393 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001394 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001395 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001396 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001397 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001398 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001399 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001400 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001401 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001402 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001403 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001404 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001405 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001406 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001407 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001408 data.acregmin = val;
1409 data.acregmax = val;
1410 data.acdirmin = val;
1411 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001412 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001413 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001414 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001415 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001416 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001417 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001418 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001419 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001420 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001421 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001422 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001423 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001424 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001425 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001426 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001427 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001428 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001429 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001430 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001431 case 16: // "nfsvers"
1432 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001433 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001434 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001435 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001436 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001437 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001438 //else
1439 // bb_error_msg("warning: option namlen is not supported\n");
1440 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001441 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001442 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1443 goto fail;
1444 }
1445 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001446 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001447 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001448 "bg\0"
1449 "fg\0"
1450 "soft\0"
1451 "hard\0"
1452 "intr\0"
1453 "posix\0"
1454 "cto\0"
1455 "ac\0"
1456 "tcp\0"
1457 "udp\0"
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001458 "lock\0"
Dave Lovefae473c2011-11-10 15:19:25 +01001459 "rdirplus\0"
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001460 "acl\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001461 int val = 1;
Denys Vlasenko8dff01d2015-03-12 17:48:34 +01001462 if (is_prefixed_with(opt, "no")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001463 val = 0;
1464 opt += 2;
1465 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001466 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001467 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001468#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001469 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001470#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001471 break;
1472 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001473#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001474 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001475#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001476 break;
1477 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001478 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001479 break;
1480 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001481 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001482 break;
1483 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001484 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001485 break;
1486 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001487 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001488 break;
1489 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001490 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001491 break;
1492 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001493 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001494 break;
1495 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001496 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001497 break;
1498 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001499 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001500 break;
1501 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001502 if (nfs_mount_version >= 3)
1503 nolock = !val;
1504 else
1505 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001506 break;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001507 case 11: //rdirplus
1508 nordirplus = !val;
1509 break;
Dave Lovefae473c2011-11-10 15:19:25 +01001510 case 12: // acl
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001511 noacl = !val;
Dave Lovefae473c2011-11-10 15:19:25 +01001512 break;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001513 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001514 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1515 goto fail;
1516 }
1517 }
1518 }
1519 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1520
1521 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1522 | (intr ? NFS_MOUNT_INTR : 0)
1523 | (posix ? NFS_MOUNT_POSIX : 0)
1524 | (nocto ? NFS_MOUNT_NOCTO : 0)
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001525 | (noac ? NFS_MOUNT_NOAC : 0)
Dave Lovefae473c2011-11-10 15:19:25 +01001526 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001527 | (noacl ? NFS_MOUNT_NOACL : 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001528 if (nfs_mount_version >= 2)
1529 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1530 if (nfs_mount_version >= 3)
1531 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1532 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1533 bb_error_msg("NFSv%d not supported", nfsvers);
1534 goto fail;
1535 }
1536 if (nfsvers && !mountvers)
1537 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1538 if (nfsvers && nfsvers < mountvers) {
1539 mountvers = nfsvers;
1540 }
1541
1542 /* Adjust options if none specified */
1543 if (!data.timeo)
1544 data.timeo = tcp ? 70 : 7;
1545
Denis Vlasenko25098f72006-09-14 15:46:33 +00001546 data.version = nfs_mount_version;
1547
1548 if (vfsflags & MS_REMOUNT)
1549 goto do_mount;
1550
1551 /*
1552 * If the previous mount operation on the same host was
1553 * backgrounded, and the "bg" for this mount is also set,
1554 * give up immediately, to avoid the initial timeout.
1555 */
1556 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001557 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001558 if (daemonized <= 0) { /* parent or error */
1559 retval = -daemonized;
1560 goto ret;
1561 }
1562 }
1563
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001564 /* Create mount daemon client */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001565 /* See if the nfs host = mount host. */
1566 if (mounthost) {
1567 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1568 mount_server_addr.sin_family = AF_INET;
1569 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1570 } else {
1571 hp = gethostbyname(mounthost);
1572 if (hp == NULL) {
1573 bb_herror_msg("%s", mounthost);
1574 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001575 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001576 if (hp->h_length != (int)sizeof(struct in_addr)) {
1577 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001578 }
1579 mount_server_addr.sin_family = AF_INET;
Denys Vlasenko99069332010-02-27 19:38:19 +01001580 memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001581 }
1582 }
1583
1584 /*
1585 * The following loop implements the mount retries. When the mount
1586 * times out, and the "bg" option is set, we background ourself
1587 * and continue trying.
1588 *
1589 * The case where the mount point is not present and the "bg"
1590 * option is set, is treated as a timeout. This is done to
1591 * support nested mounts.
1592 *
1593 * The "retry" count specified by the user is the number of
1594 * minutes to retry before giving up.
1595 */
1596 {
1597 struct timeval total_timeout;
1598 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001599 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001600 time_t t;
1601 time_t prevt;
1602 time_t timeout;
1603
1604 retry_timeout.tv_sec = 3;
1605 retry_timeout.tv_usec = 0;
1606 total_timeout.tv_sec = 20;
1607 total_timeout.tv_usec = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001608/* FIXME: use monotonic()? */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001609 timeout = time(NULL) + 60 * retry;
1610 prevt = 0;
1611 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001612 retry:
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001613 /* Be careful not to use too many CPU cycles */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001614 if (t - prevt < 30)
1615 sleep(30);
1616
Denis Vlasenkob9256052007-09-28 10:29:17 +00001617 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001618 mountprog,
1619 mountvers,
1620 proto,
1621 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001622 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001623
1624 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001625 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001626 msock = RPC_ANYSOCK;
1627
Denis Vlasenkob9256052007-09-28 10:29:17 +00001628 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001629 case IPPROTO_UDP:
1630 mclient = clntudp_create(&mount_server_addr,
Denys Vlasenko69675782013-01-14 01:34:48 +01001631 pm_mnt.pm_prog,
1632 pm_mnt.pm_vers,
1633 retry_timeout,
1634 &msock);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001635 if (mclient)
1636 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001637 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001638 msock = RPC_ANYSOCK;
1639 case IPPROTO_TCP:
1640 mclient = clnttcp_create(&mount_server_addr,
Denys Vlasenko69675782013-01-14 01:34:48 +01001641 pm_mnt.pm_prog,
1642 pm_mnt.pm_vers,
1643 &msock, 0, 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001644 break;
1645 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001646 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001647 }
1648 if (!mclient) {
1649 if (!daemonized && prevt == 0)
1650 error_msg_rpc(clnt_spcreateerror(" "));
1651 } else {
1652 enum clnt_stat clnt_stat;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001653
1654 /* Try to mount hostname:pathname */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001655 mclient->cl_auth = authunix_create_default();
1656
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001657 /* Make pointers in xdr_mountres3 NULL so
Denis Vlasenko25098f72006-09-14 15:46:33 +00001658 * that xdr_array allocates memory for us
1659 */
1660 memset(&status, 0, sizeof(status));
1661
Denis Vlasenkob9256052007-09-28 10:29:17 +00001662 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001663 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
Denys Vlasenko69675782013-01-14 01:34:48 +01001664 (xdrproc_t) xdr_dirpath,
1665 (caddr_t) &pathname,
1666 (xdrproc_t) xdr_mountres3,
1667 (caddr_t) &status,
1668 total_timeout);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001669 else
1670 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
Denys Vlasenko69675782013-01-14 01:34:48 +01001671 (xdrproc_t) xdr_dirpath,
1672 (caddr_t) &pathname,
1673 (xdrproc_t) xdr_fhstatus,
1674 (caddr_t) &status,
1675 total_timeout);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001676
1677 if (clnt_stat == RPC_SUCCESS)
1678 goto prepare_kernel_data; /* we're done */
1679 if (errno != ECONNREFUSED) {
1680 error_msg_rpc(clnt_sperror(mclient, " "));
1681 goto fail; /* don't retry */
1682 }
1683 /* Connection refused */
1684 if (!daemonized && prevt == 0) /* print just once */
1685 error_msg_rpc(clnt_sperror(mclient, " "));
1686 auth_destroy(mclient->cl_auth);
1687 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001688 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001689 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001690 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001691 }
1692
1693 /* Timeout. We are going to retry... maybe */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001694 if (!bg)
1695 goto fail;
1696 if (!daemonized) {
1697 daemonized = daemonize();
1698 if (daemonized <= 0) { /* parent or error */
1699 retval = -daemonized;
1700 goto ret;
1701 }
1702 }
1703 prevt = t;
1704 t = time(NULL);
1705 if (t >= timeout)
1706 /* TODO error message */
1707 goto fail;
1708
1709 goto retry;
1710 }
1711
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001712 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001713
1714 if (nfsvers == 2) {
1715 if (status.nfsv2.fhs_status != 0) {
1716 bb_error_msg("%s:%s failed, reason given by server: %s",
1717 hostname, pathname,
1718 nfs_strerror(status.nfsv2.fhs_status));
1719 goto fail;
1720 }
1721 memcpy(data.root.data,
1722 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1723 NFS_FHSIZE);
1724 data.root.size = NFS_FHSIZE;
1725 memcpy(data.old_root.data,
1726 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1727 NFS_FHSIZE);
1728 } else {
1729 fhandle3 *my_fhandle;
1730 if (status.nfsv3.fhs_status != 0) {
1731 bb_error_msg("%s:%s failed, reason given by server: %s",
1732 hostname, pathname,
1733 nfs_strerror(status.nfsv3.fhs_status));
1734 goto fail;
1735 }
1736 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1737 memset(data.old_root.data, 0, NFS_FHSIZE);
1738 memset(&data.root, 0, sizeof(data.root));
1739 data.root.size = my_fhandle->fhandle3_len;
1740 memcpy(data.root.data,
1741 (char *) my_fhandle->fhandle3_val,
1742 my_fhandle->fhandle3_len);
1743
1744 data.flags |= NFS_MOUNT_VER3;
1745 }
1746
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001747 /* Create nfs socket for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001748 if (tcp) {
1749 if (nfs_mount_version < 3) {
1750 bb_error_msg("NFS over TCP is not supported");
1751 goto fail;
1752 }
1753 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1754 } else
1755 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1756 if (fsock < 0) {
1757 bb_perror_msg("nfs socket");
1758 goto fail;
1759 }
1760 if (bindresvport(fsock, 0) < 0) {
1761 bb_perror_msg("nfs bindresvport");
1762 goto fail;
1763 }
1764 if (port == 0) {
1765 server_addr.sin_port = PMAPPORT;
1766 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1767 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1768 if (port == 0)
1769 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001770 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001771 server_addr.sin_port = htons(port);
1772
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001773 /* Prepare data structure for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001774 data.fd = fsock;
1775 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1776 strncpy(data.hostname, hostname, sizeof(data.hostname));
1777
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001778 /* Clean up */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001779 auth_destroy(mclient->cl_auth);
1780 clnt_destroy(mclient);
1781 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001782 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001783
1784 if (bg) {
1785 /* We must wait until mount directory is available */
1786 struct stat statbuf;
1787 int delay = 1;
1788 while (stat(mp->mnt_dir, &statbuf) == -1) {
1789 if (!daemonized) {
1790 daemonized = daemonize();
1791 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001792/* FIXME: parent doesn't close fsock - ??! */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001793 retval = -daemonized;
1794 goto ret;
1795 }
1796 }
1797 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1798 delay *= 2;
1799 if (delay > 30)
1800 delay = 30;
1801 }
1802 }
1803
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001804 /* Perform actual mount */
1805 do_mount:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001806 retval = mount_it_now(mp, vfsflags, (char*)&data);
1807 goto ret;
1808
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001809 /* Abort */
1810 fail:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001811 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001812 if (mclient) {
1813 auth_destroy(mclient->cl_auth);
1814 clnt_destroy(mclient);
1815 }
1816 close(msock);
1817 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001818 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001819 close(fsock);
1820
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001821 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001822 free(hostname);
1823 free(mounthost);
1824 free(filteropts);
1825 return retval;
1826}
1827
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001828#else // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001829
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001830/* Linux 2.6.23+ supports nfs mounts with options passed as a string.
1831 * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
1832 * (However, note that then you lose any chances that NFS over IPv6 would work).
1833 */
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001834static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001835{
1836 len_and_sockaddr *lsa;
1837 char *opts;
1838 char *end;
1839 char *dotted;
1840 int ret;
1841
1842# if ENABLE_FEATURE_IPV6
1843 end = strchr(mp->mnt_fsname, ']');
1844 if (end && end[1] == ':')
1845 end++;
1846 else
1847# endif
1848 /* mount_main() guarantees that ':' is there */
1849 end = strchr(mp->mnt_fsname, ':');
1850
1851 *end = '\0';
Denys Vlasenko39b23312011-11-10 17:01:39 +01001852 lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001853 *end = ':';
1854 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1855 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1856 opts = xasprintf("%s%saddr=%s",
1857 filteropts ? filteropts : "",
1858 filteropts ? "," : "",
1859 dotted
1860 );
1861 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1862 ret = mount_it_now(mp, vfsflags, opts);
1863 if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1864
1865 return ret;
1866}
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001867
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001868#endif // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001869
1870// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1871// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001872// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001873static int singlemount(struct mntent *mp, int ignore_busy)
1874{
Denis Vlasenkob4133682008-02-18 13:05:38 +00001875 int rc = -1;
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001876 unsigned long vfsflags;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001877 char *loopFile = NULL, *filteropts = NULL;
1878 llist_t *fl = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001879 struct stat st;
1880
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001881 errno = 0;
1882
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001883 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1884
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001885 // Treat fstype "auto" as unspecified
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001886 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1887 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001888
Denis Vlasenko2535f122007-09-15 13:28:30 +00001889 // Might this be a virtual filesystem?
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001890 if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1891 char *args[35];
1892 char *s;
1893 int n;
1894 // fsname: "cmd#arg1#arg2..."
1895 // WARNING: allows execution of arbitrary commands!
1896 // Try "mount 'sh#-c#sh' bogus_dir".
1897 // It is safe ONLY because non-root
1898 // cannot use two-argument mount command
1899 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1900 // "mount: can't find sh#-c#sh in /etc/fstab"
1901 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1902
1903 s = mp->mnt_fsname;
1904 n = 0;
1905 args[n++] = s;
1906 while (*s && n < 35 - 2) {
1907 if (*s++ == '#' && *s != '#') {
1908 s[-1] = '\0';
1909 args[n++] = s;
Denis Vlasenko2535f122007-09-15 13:28:30 +00001910 }
1911 }
Denis Vlasenko2535f122007-09-15 13:28:30 +00001912 args[n++] = mp->mnt_dir;
1913 args[n] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +01001914 rc = spawn_and_wait(args);
Denis Vlasenko2535f122007-09-15 13:28:30 +00001915 goto report_error;
1916 }
1917
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001918 // Might this be an CIFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001919 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001920 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1921 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1922 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001923 ) {
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001924 int len;
1925 char c;
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001926 char *hostname, *share;
1927 char *dotted, *ip;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001928 len_and_sockaddr *lsa;
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001929
1930 // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001931
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001932 hostname = mp->mnt_fsname + 2;
1933 len = strcspn(hostname, "/\\");
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001934 share = hostname + len + 1;
1935 if (len == 0 // 3rd char is a [back]slash (IOW: empty hostname)
1936 || share[-1] == '\0' // no [back]slash after hostname
1937 || share[0] == '\0' // empty share name
Martin Santesson406ea152013-01-16 00:47:19 +01001938 ) {
Denis Vlasenko5c329932009-04-12 12:16:21 +00001939 goto report_error;
Martin Santesson406ea152013-01-16 00:47:19 +01001940 }
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001941 c = share[-1];
1942 share[-1] = '\0';
1943 len = strcspn(share, "/\\");
Martin Santesson406ea152013-01-16 00:47:19 +01001944
1945 // "unc=\\hostname\share" option is mandatory
1946 // after CIFS option parsing was rewritten in Linux 3.4.
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001947 // Must use backslashes.
1948 // If /dir1/dir2 is present, also add "prefixpath=dir1/dir2"
Martin Santesson406ea152013-01-16 00:47:19 +01001949 {
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001950 char *unc = xasprintf(
1951 share[len] != '\0' /* "/dir1/dir2" exists? */
1952 ? "unc=\\\\%s\\%.*s,prefixpath=%s"
1953 : "unc=\\\\%s\\%.*s",
1954 hostname,
1955 len, share,
1956 share + len + 1 /* "dir1/dir2" */
1957 );
Denys Vlasenko9b7ebfe2013-01-22 11:00:45 +01001958 parse_mount_options(unc, &filteropts);
1959 if (ENABLE_FEATURE_CLEAN_UP) free(unc);
Martin Santesson406ea152013-01-16 00:47:19 +01001960 }
1961
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001962 lsa = host2sockaddr(hostname, 0);
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001963 share[-1] = c;
Denis Vlasenko5c329932009-04-12 12:16:21 +00001964 if (!lsa)
1965 goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001966
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001967 // Insert "ip=..." option into options
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001968 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001969 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001970 ip = xasprintf("ip=%s", dotted);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001971 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001972 parse_mount_options(ip, &filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001973 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001974
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001975 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001976 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001977
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001978 goto report_error;
1979 }
1980
1981 // Might this be an NFS filesystem?
Denys Vlasenko8dff01d2015-03-12 17:48:34 +01001982 if ((!mp->mnt_type || is_prefixed_with(mp->mnt_type, "nfs"))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001983 && strchr(mp->mnt_fsname, ':') != NULL
1984 ) {
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001985 if (!mp->mnt_type)
1986 mp->mnt_type = (char*)"nfs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001987 rc = nfsmount(mp, vfsflags, filteropts);
1988 goto report_error;
1989 }
1990
1991 // Look at the file. (Not found isn't a failure for remount, or for
1992 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001993 // (We use stat, not lstat, in order to allow
1994 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001995 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001996 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1997 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001998 // Do we need to allocate a loopback device for it?
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001999 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
2000 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002001 mp->mnt_fsname = NULL; // will receive malloced loop dev name
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002002 if (set_loop(&mp->mnt_fsname, loopFile, 0, /*ro:*/ (vfsflags & MS_RDONLY)) < 0) {
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00002003 if (errno == EPERM || errno == EACCES)
2004 bb_error_msg(bb_msg_perm_denied_are_you_root);
2005 else
Denys Vlasenko6331cf02009-11-13 09:08:27 +01002006 bb_perror_msg("can't setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00002007 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002008 }
2009
2010 // Autodetect bind mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002011 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
2012 vfsflags |= MS_BIND;
2013 }
2014
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002015 // If we know the fstype (or don't need to), jump straight
2016 // to the actual mount.
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02002017 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +01002018 char *next;
2019 for (;;) {
2020 next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
2021 if (next)
2022 *next = '\0';
2023 rc = mount_it_now(mp, vfsflags, filteropts);
2024 if (rc == 0 || !next)
2025 break;
2026 mp->mnt_type = next + 1;
2027 }
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02002028 } else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002029 // Loop through filesystem types until mount succeeds
2030 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002031
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01002032 // Initialize list of block backed filesystems.
2033 // This has to be done here so that during "mount -a",
2034 // mounts after /proc shows up can autodetect.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002035 if (!fslist) {
2036 fslist = get_block_backed_filesystems();
2037 if (ENABLE_FEATURE_CLEAN_UP && fslist)
2038 atexit(delete_block_backed_filesystems);
2039 }
2040
2041 for (fl = fslist; fl; fl = fl->link) {
2042 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00002043 rc = mount_it_now(mp, vfsflags, filteropts);
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +01002044 if (rc == 0)
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02002045 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002046 }
2047 }
2048
2049 // If mount failed, clean up loop file (if any).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002050 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
2051 del_loop(mp->mnt_fsname);
2052 if (ENABLE_FEATURE_CLEAN_UP) {
2053 free(loopFile);
2054 free(mp->mnt_fsname);
2055 }
2056 }
2057
Denis Vlasenko5870ad92007-02-04 02:39:55 +00002058 report_error:
2059 if (ENABLE_FEATURE_CLEAN_UP)
2060 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002061
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00002062 if (errno == EBUSY && ignore_busy)
2063 return 0;
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02002064 if (rc != 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00002065 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002066 return rc;
2067}
2068
Michael Abbott6b5accb2009-12-04 03:33:07 +01002069// -O support
2070// -O interprets a list of filter options which select whether a mount
2071// point will be mounted: only mounts with options matching *all* filtering
2072// options will be selected.
2073// By default each -O filter option must be present in the list of mount
2074// options, but if it is prefixed by "no" then it must be absent.
2075// For example,
2076// -O a,nob,c matches -o a,c but fails to match -o a,b,c
2077// (and also fails to match -o a because -o c is absent).
2078//
2079// It is different from -t in that each option is matched exactly; a leading
2080// "no" at the beginning of one option does not negate the rest.
2081static int match_opt(const char *fs_opt_in, const char *O_opt)
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002082{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002083 if (!O_opt)
Michael Abbott6b5accb2009-12-04 03:33:07 +01002084 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002085
Michael Abbott6b5accb2009-12-04 03:33:07 +01002086 while (*O_opt) {
2087 const char *fs_opt = fs_opt_in;
2088 int O_len;
2089 int match;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002090
Michael Abbott6b5accb2009-12-04 03:33:07 +01002091 // If option begins with "no" then treat as an inverted match:
2092 // matching is a failure
2093 match = 0;
2094 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
2095 match = 1;
2096 O_opt += 2;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002097 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01002098 // Isolate the current O option
2099 O_len = strchrnul(O_opt, ',') - O_opt;
2100 // Check for a match against existing options
2101 while (1) {
2102 if (strncmp(fs_opt, O_opt, O_len) == 0
2103 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
2104 ) {
2105 if (match)
2106 return 0; // "no" prefix, but option found
2107 match = 1; // current O option found, go check next one
2108 break;
2109 }
2110 fs_opt = strchr(fs_opt, ',');
2111 if (!fs_opt)
2112 break;
2113 fs_opt++;
2114 }
2115 if (match == 0)
2116 return 0; // match wanted but not found
2117 if (O_opt[O_len] == '\0') // end?
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002118 break;
Michael Abbott6b5accb2009-12-04 03:33:07 +01002119 // Step to the next O option
2120 O_opt += O_len + 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002121 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01002122 // If we get here then everything matched
2123 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002124}
2125
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002126// Parse options, if necessary parse fstab/mtab, and call singlemount for
2127// each directory to be mounted.
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00002128int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002129int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002130{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002131 char *cmdopts = xzalloc(1);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002132 char *fstype = NULL;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002133 char *O_optmatch = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002134 char *storage_path;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00002135 llist_t *lst_o = NULL;
Isaac Dunham7b434a62015-03-11 16:07:24 +01002136 const char *fstabname = "/etc/fstab";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002137 FILE *fstab;
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002138 int i, j;
2139 int rc = EXIT_SUCCESS;
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002140 unsigned long cmdopt_flags;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00002141 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002142 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002143 IF_NOT_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002144
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002145 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00002146
Denys Vlasenko16714242011-09-21 01:59:15 +02002147 INIT_G();
2148
Denis Vlasenkof732e962008-02-18 12:07:49 +00002149 // Parse long options, like --bind and --move. Note that -o option
2150 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002151 for (i = j = 1; argv[i]; i++) {
2152 if (argv[i][0] == '-' && argv[i][1] == '-')
2153 append_mount_options(&cmdopts, argv[i] + 2);
2154 else
2155 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002156 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002157 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002158
2159 // Parse remaining options
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002160 // Max 2 params; -o is a list, -v is a counter
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002161 opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002162 opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
Isaac Dunham7b434a62015-03-11 16:07:24 +01002163 IF_FEATURE_MOUNT_OTHERTAB(, &fstabname)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002164 IF_FEATURE_MOUNT_VERBOSE(, &verbose));
Denis Vlasenkof9dde912008-10-18 19:15:57 +00002165 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00002166 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2167 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00002168 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002169
2170 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002171 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00002172 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002173 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2174
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002175 if (!mountTable)
2176 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002177
Denis Vlasenko2535f122007-09-15 13:28:30 +00002178 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002179 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002180 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002181 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00002182 // util-linux 2.12a happily shows rootfs...
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002183 //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002184
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002185 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002186 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2187 mtpair->mnt_dir, mtpair->mnt_type,
2188 mtpair->mnt_opts);
2189 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002190 if (ENABLE_FEATURE_CLEAN_UP)
2191 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002192 return EXIT_SUCCESS;
2193 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002194 storage_path = NULL;
2195 } else {
2196 // When we have two arguments, the second is the directory and we can
2197 // skip looking at fstab entirely. We can always abspath() the directory
2198 // argument when we get it.
2199 if (argv[1]) {
2200 if (nonroot)
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002201 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002202 mtpair->mnt_fsname = argv[0];
2203 mtpair->mnt_dir = argv[1];
2204 mtpair->mnt_type = fstype;
2205 mtpair->mnt_opts = cmdopts;
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002206 resolve_mount_spec(&mtpair->mnt_fsname);
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002207 rc = singlemount(mtpair, /*ignore_busy:*/ 0);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002208 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00002209 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002210 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002211 }
2212
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002213 // Past this point, we are handling either "mount -a [opts]"
2214 // or "mount [opts] single_param"
2215
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002216 cmdopt_flags = parse_mount_options(cmdopts, NULL);
2217 if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002218 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00002219
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002220 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002221 if (ENABLE_FEATURE_MOUNT_FLAGS
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002222 && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002223 ) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002224 // verbose_mount(source, target, type, flags, data)
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002225 rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002226 if (rc)
2227 bb_simple_perror_msg_and_die(argv[0]);
2228 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002229 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00002230
Isaac Dunham7b434a62015-03-11 16:07:24 +01002231 // A malicious user could overmount /usr without this.
2232 if (ENABLE_FEATURE_MOUNT_OTHERTAB && nonroot)
2233 fstabname = "/etc/fstab";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002234 // Open either fstab or mtab
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002235 if (cmdopt_flags & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002236 // WARNING. I am not sure this matches util-linux's
2237 // behavior. It's possible util-linux does not
2238 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002239 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002240 }
2241 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00002242 if (!fstab)
Denys Vlasenko651a2692010-03-23 16:25:17 +01002243 bb_perror_msg_and_die("can't read '%s'", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002244
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002245 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00002246 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002247 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002248 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002249
2250 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00002251 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002252 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002253 GETMNTENT_BUFSIZE/2)
2254 ) { // End of fstab/mtab is reached
2255 mtcur = mtother; // the thing we found last time
2256 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002257 }
2258
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002259 // If we're trying to mount something specific and this isn't it,
2260 // skip it. Note we must match the exact text in fstab (ala
2261 // "proc") or a full path from root
2262 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002263
2264 // Is this what we're looking for?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002265 if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2266 && strcmp(storage_path, mtcur->mnt_fsname) != 0
2267 && strcmp(argv[0], mtcur->mnt_dir) != 0
2268 && strcmp(storage_path, mtcur->mnt_dir) != 0
2269 ) {
2270 continue; // no
2271 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002272
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002273 // Remember this entry. Something later may have
2274 // overmounted it, and we want the _last_ match.
2275 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002276
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002277 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002278 } else {
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002279 struct mntent *mp;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002280 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002281 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002282 if (nonroot)
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002283 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002284
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002285 // Does type match? (NULL matches always)
2286 if (!match_fstype(mtcur, fstype))
2287 continue;
2288
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002289 // Skip noauto and swap anyway
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002290 if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2291 // swap is bogus "fstype", parse_mount_options can't check fstypes
2292 || strcasecmp(mtcur->mnt_type, "swap") == 0
2293 ) {
2294 continue;
2295 }
2296
2297 // Does (at least one) option match?
2298 // (NULL matches always)
2299 if (!match_opt(mtcur->mnt_opts, O_optmatch))
2300 continue;
2301
2302 resolve_mount_spec(&mtcur->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002303
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002304 // NFS mounts want this to be xrealloc-able
2305 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002306
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002307 // If nothing is mounted on this directory...
2308 // (otherwise repeated "mount -a" mounts everything again)
2309 mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2310 // We do not check fsname match of found mount point -
2311 // "/" may have fsname of "/dev/root" while fstab
2312 // says "/dev/something_else".
2313 if (mp) {
Denys Vlasenko86566762009-12-10 21:32:28 +01002314 if (verbose) {
2315 bb_error_msg("according to %s, "
2316 "%s is already mounted on %s",
2317 bb_path_mtab_file,
2318 mp->mnt_fsname, mp->mnt_dir);
2319 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002320 } else {
2321 // ...mount this thing
2322 if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2323 // Count number of failed mounts
2324 rc++;
2325 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002326 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002327 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002328 }
2329 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002330
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002331 // End of fstab/mtab is reached.
2332 // Were we looking for something specific?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002333 if (argv[0]) { // yes
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002334 unsigned long l;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002335
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002336 // If we didn't find anything, complain
2337 if (!mtcur->mnt_fsname)
2338 bb_error_msg_and_die("can't find %s in %s",
2339 argv[0], fstabname);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002340
2341 // What happens when we try to "mount swap_partition"?
2342 // (fstab containts "swap_partition swap swap defaults 0 0")
2343 // util-linux-ng 2.13.1 does this:
2344 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2345 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2346 // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory)
2347 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2348 // exit_group(32) = ?
2349#if 0
2350 // In case we want to simply skip swap partitions:
2351 l = parse_mount_options(mtcur->mnt_opts, NULL);
2352 if ((l & MOUNT_SWAP)
2353 // swap is bogus "fstype", parse_mount_options can't check fstypes
2354 || strcasecmp(mtcur->mnt_type, "swap") == 0
2355 ) {
2356 goto ret;
2357 }
2358#endif
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002359 if (nonroot) {
2360 // fstab must have "users" or "user"
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002361 l = parse_mount_options(mtcur->mnt_opts, NULL);
2362 if (!(l & MOUNT_USERS))
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002363 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002364 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002365
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002366 //util-linux-2.12 does not do this check.
2367 //// If nothing is mounted on this directory...
2368 //// (otherwise repeated "mount FOO" mounts FOO again)
2369 //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2370 //if (mp) {
2371 // bb_error_msg("according to %s, "
2372 // "%s is already mounted on %s",
2373 // bb_path_mtab_file,
2374 // mp->mnt_fsname, mp->mnt_dir);
2375 //} else {
2376 // ...mount the last thing we found
2377 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2378 append_mount_options(&(mtcur->mnt_opts), cmdopts);
2379 resolve_mount_spec(&mtpair->mnt_fsname);
2380 rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2381 if (ENABLE_FEATURE_CLEAN_UP)
2382 free(mtcur->mnt_opts);
2383 //}
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002384 }
2385
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002386 //ret:
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002387 if (ENABLE_FEATURE_CLEAN_UP)
2388 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002389 if (ENABLE_FEATURE_CLEAN_UP) {
2390 free(storage_path);
2391 free(cmdopts);
2392 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002393
2394//TODO: exitcode should be ORed mask of (from "man mount"):
2395// 0 success
2396// 1 incorrect invocation or permissions
2397// 2 system error (out of memory, cannot fork, no more loop devices)
2398// 4 internal mount bug or missing nfs support in mount
2399// 8 user interrupt
2400//16 problems writing or locking /etc/mtab
2401//32 mount failure
2402//64 some mount succeeded
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002403 return rc;
2404}