blob: 6f1ce6734bde24bb52c557ad01db8df9a19c3f03 [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
20//usage:#define mount_trivial_usage
21//usage: "[OPTIONS] [-o OPTS] DEVICE NODE"
22//usage:#define mount_full_usage "\n\n"
23//usage: "Mount a filesystem. Filesystem autodetection requires /proc.\n"
Pere Orga5bc8c002011-04-11 03:29:49 +020024//usage: "\n -a Mount all filesystems in fstab"
25//usage: IF_FEATURE_MOUNT_FAKE(
26//usage: IF_FEATURE_MTAB_SUPPORT(
27//usage: "\n -f Update /etc/mtab, but don't mount"
28//usage: )
29//usage: IF_NOT_FEATURE_MTAB_SUPPORT(
30//usage: "\n -f Dry run"
31//usage: )
32//usage: )
33//usage: IF_FEATURE_MOUNT_HELPERS(
34//usage: "\n -i Don't run mount helper"
35//usage: )
36//usage: IF_FEATURE_MTAB_SUPPORT(
37//usage: "\n -n Don't update /etc/mtab"
38//usage: )
Tanguy Pruvot823694d2012-11-18 13:20:29 +010039//usage: IF_FEATURE_MOUNT_VERBOSE(
40//usage: "\n -v Verbose"
41//usage: )
42////usage: "\n -s Sloppy (ignored)"
Pere Orga5bc8c002011-04-11 03:29:49 +020043//usage: "\n -r Read-only mount"
44//usage: "\n -w Read-write mount (default)"
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +020045//usage: "\n -t FSTYPE[,...] Filesystem type(s)"
Pere Orga5bc8c002011-04-11 03:29:49 +020046//usage: "\n -O OPT Mount only filesystems with option OPT (-a only)"
47//usage: "\n-o OPT:"
48//usage: IF_FEATURE_MOUNT_LOOP(
49//usage: "\n loop Ignored (loop devices are autodetected)"
50//usage: )
51//usage: IF_FEATURE_MOUNT_FLAGS(
52//usage: "\n [a]sync Writes are [a]synchronous"
53//usage: "\n [no]atime Disable/enable updates to inode access times"
54//usage: "\n [no]diratime Disable/enable atime updates to directories"
55//usage: "\n [no]relatime Disable/enable atime updates relative to modification time"
56//usage: "\n [no]dev (Dis)allow use of special device files"
57//usage: "\n [no]exec (Dis)allow use of executable files"
58//usage: "\n [no]suid (Dis)allow set-user-id-root programs"
59//usage: "\n [r]shared Convert [recursively] to a shared subtree"
60//usage: "\n [r]slave Convert [recursively] to a slave subtree"
61//usage: "\n [r]private Convert [recursively] to a private subtree"
62//usage: "\n [un]bindable Make mount point [un]able to be bind mounted"
63//usage: "\n [r]bind Bind a file or directory [recursively] to another location"
64//usage: "\n move Relocate an existing mount point"
65//usage: )
66//usage: "\n remount Remount a mounted filesystem, changing flags"
67//usage: "\n ro/rw Same as -r/-w"
68//usage: "\n"
69//usage: "\nThere are filesystem-specific -o flags."
70//usage:
71//usage:#define mount_example_usage
72//usage: "$ mount\n"
73//usage: "/dev/hda3 on / type minix (rw)\n"
74//usage: "proc on /proc type proc (rw)\n"
75//usage: "devpts on /dev/pts type devpts (rw)\n"
76//usage: "$ mount /dev/fd0 /mnt -t msdos -o ro\n"
77//usage: "$ mount /tmp/diskimage /opt -t ext2 -o loop\n"
78//usage: "$ mount cd_image.iso mydir\n"
79//usage:#define mount_notes_usage
80//usage: "Returns 0 for success, number of failed mounts for -a, or errno for one mount."
81
Eric Andersencc8ed391999-10-05 16:24:54 +000082#include <mntent.h>
Bernhard Reutner-Fischerf4701962008-01-27 12:50:12 +000083#include <syslog.h>
Denys Vlasenkoda49f582009-07-08 02:58:38 +020084#include <sys/mount.h>
Denys Vlasenko102ff762009-11-21 17:14:08 +010085// Grab more as needed from util-linux's mount/mount_constants.h
86#ifndef MS_DIRSYNC
87# define MS_DIRSYNC (1 << 7) // Directory modifications are synchronous
88#endif
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +020089#ifndef MS_UNION
90# define MS_UNION (1 << 8)
91#endif
Denys Vlasenkoda49f582009-07-08 02:58:38 +020092#ifndef MS_BIND
93# define MS_BIND (1 << 12)
94#endif
95#ifndef MS_MOVE
96# define MS_MOVE (1 << 13)
97#endif
98#ifndef MS_RECURSIVE
99# define MS_RECURSIVE (1 << 14)
100#endif
101#ifndef MS_SILENT
102# define MS_SILENT (1 << 15)
103#endif
Denys Vlasenko102ff762009-11-21 17:14:08 +0100104// The shared subtree stuff, which went in around 2.6.15
Denys Vlasenkoda49f582009-07-08 02:58:38 +0200105#ifndef MS_UNBINDABLE
106# define MS_UNBINDABLE (1 << 17)
107#endif
108#ifndef MS_PRIVATE
109# define MS_PRIVATE (1 << 18)
110#endif
111#ifndef MS_SLAVE
112# define MS_SLAVE (1 << 19)
113#endif
114#ifndef MS_SHARED
115# define MS_SHARED (1 << 20)
116#endif
117#ifndef MS_RELATIME
118# define MS_RELATIME (1 << 21)
119#endif
Tanguy Pruvot823694d2012-11-18 13:20:29 +0100120#ifndef MS_STRICTATIME
121# define MS_STRICTATIME (1 << 24)
122#endif
123
124/* Any ~MS_FOO value has this bit set: */
125#define BB_MS_INVERTED_VALUE (1u << 31)
Eric Andersenbd22ed82000-07-08 18:55:24 +0000126
Denys Vlasenko102ff762009-11-21 17:14:08 +0100127#include "libbb.h"
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000128#if ENABLE_FEATURE_MOUNT_LABEL
Natanael Copa9aff2992009-09-20 04:28:22 +0200129# include "volume_id.h"
130#else
131# define resolve_mount_spec(fsname) ((void)0)
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000132#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000133
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000134// Needed for nfs support only
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000135#include <sys/utsname.h>
136#undef TRUE
137#undef FALSE
Denys Vlasenkocc428142009-12-16 02:06:56 +0100138#if ENABLE_FEATURE_MOUNT_NFS
139/* This is just a warning of a common mistake. Possibly this should be a
140 * uclibc faq entry rather than in busybox... */
141# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
142# error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
143# endif
144# include <rpc/rpc.h>
145# include <rpc/pmap_prot.h>
146# include <rpc/pmap_clnt.h>
147#endif
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000148
149
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000150#if defined(__dietlibc__)
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000151// 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
152// dietlibc-0.30 does not have implementation of getmntent_r()
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +0000153static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000154 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000155{
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000156 struct mntent* ment = getmntent(stream);
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +0000157 return memcpy(result, ment, sizeof(*ment));
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000158}
159#endif
160
161
Rob Landleydc0955b2006-03-14 18:16:25 +0000162// Not real flags, but we want to be able to check for this.
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000163enum {
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000164 MOUNT_USERS = (1 << 28) * ENABLE_DESKTOP,
165 MOUNT_NOAUTO = (1 << 29),
166 MOUNT_SWAP = (1 << 30),
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000167};
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000168
169
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +0000170#define OPTION_STR "o:t:rwanfvsiO:"
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000171enum {
172 OPT_o = (1 << 0),
173 OPT_t = (1 << 1),
174 OPT_r = (1 << 2),
175 OPT_w = (1 << 3),
176 OPT_a = (1 << 4),
177 OPT_n = (1 << 5),
178 OPT_f = (1 << 6),
179 OPT_v = (1 << 7),
180 OPT_s = (1 << 8),
181 OPT_i = (1 << 9),
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +0000182 OPT_O = (1 << 10),
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000183};
184
185#if ENABLE_FEATURE_MTAB_SUPPORT
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200186#define USE_MTAB (!(option_mask32 & OPT_n))
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000187#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200188#define USE_MTAB 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000189#endif
190
191#if ENABLE_FEATURE_MOUNT_FAKE
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200192#define FAKE_IT (option_mask32 & OPT_f)
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000193#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200194#define FAKE_IT 0
195#endif
196
197#if ENABLE_FEATURE_MOUNT_HELPERS
198#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
199#else
200#define HELPERS_ALLOWED 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000201#endif
202
203
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000204// TODO: more "user" flag compatibility.
205// "user" option (from mount manpage):
206// Only the user that mounted a filesystem can unmount it again.
207// If any user should be able to unmount, then use users instead of user
208// in the fstab line. The owner option is similar to the user option,
209// with the restriction that the user must be the owner of the special file.
210// This may be useful e.g. for /dev/fd if a login script makes
211// the console user owner of this device.
Rob Landley3ba7bd12006-08-09 19:51:13 +0000212
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000213// Standard mount options (from -o options or --options),
214// with corresponding flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000215static const int32_t mount_options[] = {
Rob Landleye3781b72006-08-08 01:39:49 +0000216 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
Rob Landleydc0955b2006-03-14 18:16:25 +0000217
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000218 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000219 /* "loop" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000220 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000221
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000222 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000223 /* "defaults" */ 0,
224 /* "quiet" 0 - do not filter out, vfat wants to see it */
225 /* "noauto" */ MOUNT_NOAUTO,
226 /* "sw" */ MOUNT_SWAP,
227 /* "swap" */ MOUNT_SWAP,
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000228 IF_DESKTOP(/* "user" */ MOUNT_USERS,)
229 IF_DESKTOP(/* "users" */ MOUNT_USERS,)
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000230 /* "_netdev" */ 0,
Tanguy Pruvot823694d2012-11-18 13:20:29 +0100231 IF_DESKTOP(/* "comment=" */ 0,) /* systemd uses this in fstab */
Rob Landleye3781b72006-08-08 01:39:49 +0000232 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000233
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000234 IF_FEATURE_MOUNT_FLAGS(
Rob Landleye3781b72006-08-08 01:39:49 +0000235 // vfs flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000236 /* "nosuid" */ MS_NOSUID,
237 /* "suid" */ ~MS_NOSUID,
238 /* "dev" */ ~MS_NODEV,
239 /* "nodev" */ MS_NODEV,
240 /* "exec" */ ~MS_NOEXEC,
241 /* "noexec" */ MS_NOEXEC,
242 /* "sync" */ MS_SYNCHRONOUS,
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +0000243 /* "dirsync" */ MS_DIRSYNC,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000244 /* "async" */ ~MS_SYNCHRONOUS,
245 /* "atime" */ ~MS_NOATIME,
246 /* "noatime" */ MS_NOATIME,
247 /* "diratime" */ ~MS_NODIRATIME,
248 /* "nodiratime" */ MS_NODIRATIME,
Denis Vlasenko580ce2d2008-07-08 02:56:53 +0000249 /* "mand" */ MS_MANDLOCK,
250 /* "nomand" */ ~MS_MANDLOCK,
Bernhard Reutner-Fischerfb5902c2008-08-06 18:14:38 +0000251 /* "relatime" */ MS_RELATIME,
252 /* "norelatime" */ ~MS_RELATIME,
Tanguy Pruvot823694d2012-11-18 13:20:29 +0100253 /* "strictatime" */ MS_STRICTATIME,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000254 /* "loud" */ ~MS_SILENT,
Roman Borisov19311bf2011-03-24 15:08:43 +0300255 /* "rbind" */ MS_BIND|MS_RECURSIVE,
Eric Andersen9601a1c2006-03-20 18:07:50 +0000256
Rob Landleye3781b72006-08-08 01:39:49 +0000257 // action flags
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200258 /* "union" */ MS_UNION,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000259 /* "bind" */ MS_BIND,
260 /* "move" */ MS_MOVE,
261 /* "shared" */ MS_SHARED,
262 /* "slave" */ MS_SLAVE,
263 /* "private" */ MS_PRIVATE,
264 /* "unbindable" */ MS_UNBINDABLE,
265 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
266 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
Roman Borisovd3679d22011-03-23 11:20:25 +0300267 /* "rprivate" */ MS_PRIVATE|MS_RECURSIVE,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000268 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
Rob Landleye3781b72006-08-08 01:39:49 +0000269 )
270
271 // Always understood.
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000272 /* "ro" */ MS_RDONLY, // vfs flag
273 /* "rw" */ ~MS_RDONLY, // vfs flag
274 /* "remount" */ MS_REMOUNT // action flag
Eric Andersencc8ed391999-10-05 16:24:54 +0000275};
276
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000277static const char mount_option_str[] =
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000278 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000279 "loop\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000280 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000281 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000282 "defaults\0"
283 // "quiet\0" - do not filter out, vfat wants to see it
284 "noauto\0"
285 "sw\0"
286 "swap\0"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000287 IF_DESKTOP("user\0")
288 IF_DESKTOP("users\0")
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000289 "_netdev\0"
Tanguy Pruvot823694d2012-11-18 13:20:29 +0100290 IF_DESKTOP("comment=\0") /* systemd uses this in fstab */
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000291 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000292 IF_FEATURE_MOUNT_FLAGS(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000293 // vfs flags
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000294 "nosuid\0"
295 "suid\0"
296 "dev\0"
297 "nodev\0"
298 "exec\0"
299 "noexec\0"
300 "sync\0"
301 "dirsync\0"
302 "async\0"
303 "atime\0"
304 "noatime\0"
305 "diratime\0"
306 "nodiratime\0"
307 "mand\0"
308 "nomand\0"
309 "relatime\0"
310 "norelatime\0"
Tanguy Pruvot823694d2012-11-18 13:20:29 +0100311 "strictatime\0"
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000312 "loud\0"
Roman Borisov19311bf2011-03-24 15:08:43 +0300313 "rbind\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000314
315 // action flags
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200316 "union\0"
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000317 "bind\0"
318 "move\0"
Roman Borisov945fd172011-02-25 14:50:39 +0300319 "make-shared\0"
320 "make-slave\0"
321 "make-private\0"
322 "make-unbindable\0"
323 "make-rshared\0"
324 "make-rslave\0"
325 "make-rprivate\0"
326 "make-runbindable\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000327 )
328
329 // Always understood.
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000330 "ro\0" // vfs flag
331 "rw\0" // vfs flag
332 "remount\0" // action flag
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000333;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000334
Denis Vlasenkof732e962008-02-18 12:07:49 +0000335
336struct globals {
337#if ENABLE_FEATURE_MOUNT_NFS
338 smalluint nfs_mount_version;
339#endif
340#if ENABLE_FEATURE_MOUNT_VERBOSE
341 unsigned verbose;
342#endif
343 llist_t *fslist;
maxwen27116ba2015-08-14 21:41:28 +0200344 int user_fstype;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000345 char getmntent_buf[1];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100346} FIX_ALIASING;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000347enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
Denis Vlasenkof732e962008-02-18 12:07:49 +0000348#define G (*(struct globals*)&bb_common_bufsiz1)
349#define nfs_mount_version (G.nfs_mount_version)
Denis Vlasenkob4133682008-02-18 13:05:38 +0000350#if ENABLE_FEATURE_MOUNT_VERBOSE
Denis Vlasenkof732e962008-02-18 12:07:49 +0000351#define verbose (G.verbose )
Denis Vlasenkob4133682008-02-18 13:05:38 +0000352#else
353#define verbose 0
354#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000355#define fslist (G.fslist )
maxwen27116ba2015-08-14 21:41:28 +0200356#define user_fstype (G.user_fstype )
Denis Vlasenkof732e962008-02-18 12:07:49 +0000357#define getmntent_buf (G.getmntent_buf )
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200358#define INIT_G() do { } while (0)
Denis Vlasenkof732e962008-02-18 12:07:49 +0000359
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100360#if ENABLE_FEATURE_MTAB_SUPPORT
361/*
362 * update_mtab_entry_on_move() is used to update entry in case of mount --move.
363 * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
364 * input mntent and replace it by new one.
365 */
366static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
367{
368 struct mntent *entries, *m;
369 int i, count;
370 FILE *mountTable;
371
372 mountTable = setmntent(bb_path_mtab_file, "r");
373 if (!mountTable) {
374 bb_perror_msg(bb_path_mtab_file);
375 return;
376 }
377
378 entries = NULL;
379 count = 0;
380 while ((m = getmntent(mountTable)) != NULL) {
381 entries = xrealloc_vector(entries, 3, count);
382 entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
383 entries[count].mnt_dir = xstrdup(m->mnt_dir);
384 entries[count].mnt_type = xstrdup(m->mnt_type);
385 entries[count].mnt_opts = xstrdup(m->mnt_opts);
386 entries[count].mnt_freq = m->mnt_freq;
387 entries[count].mnt_passno = m->mnt_passno;
388 count++;
389 }
390 endmntent(mountTable);
391
392 mountTable = setmntent(bb_path_mtab_file, "w");
393 if (mountTable) {
394 for (i = 0; i < count; i++) {
395 if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
396 addmntent(mountTable, &entries[i]);
397 else
398 addmntent(mountTable, mp);
399 }
400 endmntent(mountTable);
401 } else if (errno != EROFS)
402 bb_perror_msg(bb_path_mtab_file);
403
404 if (ENABLE_FEATURE_CLEAN_UP) {
405 for (i = 0; i < count; i++) {
406 free(entries[i].mnt_fsname);
407 free(entries[i].mnt_dir);
408 free(entries[i].mnt_type);
409 free(entries[i].mnt_opts);
410 }
411 free(entries);
412 }
413}
414#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000415
416#if ENABLE_FEATURE_MOUNT_VERBOSE
417static int verbose_mount(const char *source, const char *target,
418 const char *filesystemtype,
419 unsigned long mountflags, const void *data)
420{
421 int rc;
422
423 errno = 0;
424 rc = mount(source, target, filesystemtype, mountflags, data);
425 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000426 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000427 source, target, filesystemtype,
428 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000429 return rc;
430}
431#else
432#define verbose_mount(...) mount(__VA_ARGS__)
433#endif
434
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000435// Append mount options to string
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000436static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000437{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000438 if (*oldopts && **oldopts) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000439 // Do not insert options which are already there
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000440 while (newopts[0]) {
441 char *p;
442 int len = strlen(newopts);
443 p = strchr(newopts, ',');
444 if (p) len = p - newopts;
445 p = *oldopts;
446 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000447 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000448 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000449 goto skip;
450 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000451 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000452 p++;
453 }
454 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
455 free(*oldopts);
456 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000457 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000458 newopts += len;
459 while (newopts[0] == ',') newopts++;
460 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000461 } else {
462 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000463 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000464 }
465}
466
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000467// Use the mount_options list to parse options into flags.
Alexander Shishkin77650952010-10-28 06:10:03 +0200468// Also update list of unrecognized options if unrecognized != NULL
Tanguy Pruvot823694d2012-11-18 13:20:29 +0100469static unsigned long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000470{
Tanguy Pruvot823694d2012-11-18 13:20:29 +0100471 unsigned long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000472
Rob Landley6a6798b2005-08-10 20:35:54 +0000473 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000474 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000475 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000476 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000477 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000478
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000479 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000480
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000481// FIXME: use hasmntopt()
Rob Landley6a6798b2005-08-10 20:35:54 +0000482 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000483 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Tanguy Pruvot823694d2012-11-18 13:20:29 +0100484 unsigned opt_len = strlen(option_str);
485
486 if (strncasecmp(option_str, options, opt_len) == 0
487 && (options[opt_len] == '\0'
488 /* or is it "comment=" thingy in fstab? */
489 IF_FEATURE_MOUNT_FSTAB(IF_DESKTOP( || option_str[opt_len-1] == '=' ))
490 )
491 ) {
492 unsigned long fl = mount_options[i];
493 if (fl & BB_MS_INVERTED_VALUE)
Alexander Shishkin77650952010-10-28 06:10:03 +0200494 flags &= fl;
495 else
496 flags |= fl;
497 goto found;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000498 }
Tanguy Pruvot823694d2012-11-18 13:20:29 +0100499 option_str += opt_len + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000500 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200501 // We did not recognize this option.
502 // If "unrecognized" is not NULL, append option there.
503 // Note that we should not append *empty* option -
504 // in this case we want to pass NULL, not "", to "data"
505 // parameter of mount(2) syscall.
506 // This is crucial for filesystems that don't accept
507 // any arbitrary mount options, like cgroup fs:
508 // "mount -t cgroup none /mnt"
509 if (options[0] && unrecognized) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000510 // Add it to strflags, to pass on to kernel
Alexander Shishkin77650952010-10-28 06:10:03 +0200511 char *p = *unrecognized;
512 unsigned len = p ? strlen(p) : 0;
513 *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000514
Rob Landley6a6798b2005-08-10 20:35:54 +0000515 // Comma separated if it's not the first one
Alexander Shishkin77650952010-10-28 06:10:03 +0200516 if (len) p[len++] = ',';
517 strcpy(p + len, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000518 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200519 found:
Denis Vlasenko2535f122007-09-15 13:28:30 +0000520 if (!comma)
521 break;
522 // Advance to next option
523 *comma = ',';
524 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000525 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000526
Rob Landleydc0955b2006-03-14 18:16:25 +0000527 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000528}
529
Rob Landleydc0955b2006-03-14 18:16:25 +0000530// Return a list of all block device backed filesystems
Rob Landleydc0955b2006-03-14 18:16:25 +0000531static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000532{
Denis Vlasenko87468852007-04-13 23:22:00 +0000533 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000534 "/etc/filesystems",
535 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000536 };
537 char *fs, *buf;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200538 llist_t *list = NULL;
Rob Landleydc0955b2006-03-14 18:16:25 +0000539 int i;
540 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000541
Denis Vlasenko87468852007-04-13 23:22:00 +0000542 for (i = 0; i < 2; i++) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000543 f = fopen_for_read(filesystems[i]);
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000544 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000545
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000546 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200547 if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200548 goto next;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000549 fs = skip_whitespace(buf);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200550 if (*fs == '#' || *fs == '*' || !*fs)
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200551 goto next;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000552
Denis Vlasenko372686b2006-10-12 22:42:33 +0000553 llist_add_to_end(&list, xstrdup(fs));
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200554 next:
Denis Vlasenko372686b2006-10-12 22:42:33 +0000555 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000556 }
557 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
558 }
559
560 return list;
561}
562
Rob Landleydc0955b2006-03-14 18:16:25 +0000563#if ENABLE_FEATURE_CLEAN_UP
564static void delete_block_backed_filesystems(void)
565{
Rob Landleya6b5b602006-05-08 19:03:07 +0000566 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000567}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000568#else
569void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000570#endif
571
Rob Landleydc0955b2006-03-14 18:16:25 +0000572// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000573// NB: mp->xxx fields may be trashed on exit
Tanguy Pruvot823694d2012-11-18 13:20:29 +0100574static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000575{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000576 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000577
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200578 if (FAKE_IT) {
Denis Vlasenkob4133682008-02-18 13:05:38 +0000579 if (verbose >= 2)
580 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
581 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
582 vfsflags, filteropts);
583 goto mtab;
584 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000585
Rob Landleydc0955b2006-03-14 18:16:25 +0000586 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000587 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000588 errno = 0;
589 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000590 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000591
592 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000593 // helper program mount.<mnt_type>
Denys Vlasenkoba986032009-09-15 23:00:09 +0200594 if (HELPERS_ALLOWED && rc && mp->mnt_type) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200595 char *args[8];
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000596 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000597 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000598 rc = 1;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200599 if (FAKE_IT)
600 args[rc++] = (char *)"-f";
601 if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
602 args[rc++] = (char *)"-n";
Denis Vlasenko5c329932009-04-12 12:16:21 +0000603 args[rc++] = mp->mnt_fsname;
604 args[rc++] = mp->mnt_dir;
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000605 if (filteropts) {
606 args[rc++] = (char *)"-o";
607 args[rc++] = filteropts;
608 }
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000609 args[rc] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +0100610 rc = spawn_and_wait(args);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000611 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000612 if (!rc)
613 break;
614 errno = errno_save;
615 }
616
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000617 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000618 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000619 if (!(vfsflags & MS_SILENT))
620 bb_error_msg("%s is write-protected, mounting read-only",
621 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000622 vfsflags |= MS_RDONLY;
623 }
624
Rob Landleydc0955b2006-03-14 18:16:25 +0000625 // Abort entirely if permission denied.
626
627 if (rc && errno == EPERM)
Tanguy Pruvot8aeb3712011-06-30 08:59:26 +0200628 bb_error_msg_and_die("%s", bb_msg_perm_denied_are_you_root);
Rob Landleydc0955b2006-03-14 18:16:25 +0000629
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000630 // If the mount was successful, and we're maintaining an old-style
631 // mtab file by hand, add the new entry to it now.
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000632 mtab:
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200633 if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000634 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000635 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000636 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000637 int i;
638
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000639 if (!mountTable) {
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100640 bb_perror_msg(bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000641 goto ret;
642 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000643
644 // Add vfs string flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000645 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
646 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
647 append_mount_options(&(mp->mnt_opts), option_str);
648 option_str += strlen(option_str) + 1;
649 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000650
651 // Remove trailing / (if any) from directory we mounted on
Denis Vlasenko727ef942006-09-14 13:19:19 +0000652 i = strlen(mp->mnt_dir) - 1;
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100653 while (i > 0 && mp->mnt_dir[i] == '/')
Denys Vlasenkoc6450c92011-02-28 11:09:49 +0100654 mp->mnt_dir[i--] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000655
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000656 // Convert to canonical pathnames as needed
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000657 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100658 fsname = NULL;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000659 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000660 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000661 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000662 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000663 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000664
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100665 // Write and close
666#if ENABLE_FEATURE_MTAB_SUPPORT
667 if (vfsflags & MS_MOVE)
668 update_mtab_entry_on_move(mp);
669 else
670#endif
671 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000672 endmntent(mountTable);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100673
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000674 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000675 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000676 free(fsname);
677 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000678 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000679 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000680 return rc;
681}
682
Denis Vlasenko25098f72006-09-14 15:46:33 +0000683#if ENABLE_FEATURE_MOUNT_NFS
684
685/*
686 * Linux NFS mount
687 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
688 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +0200689 * Licensed under GPLv2, see file LICENSE in this source tree.
Denis Vlasenko25098f72006-09-14 15:46:33 +0000690 *
691 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
692 * numbers to be specified on the command line.
693 *
694 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
695 * Omit the call to connect() for Linux version 1.3.11 or later.
696 *
697 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
698 * Implemented the "bg", "fg" and "retry" mount options for NFS.
699 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000700 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000701 * - added Native Language Support
702 *
703 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
704 * plus NFSv3 stuff.
705 */
706
Denis Vlasenko25098f72006-09-14 15:46:33 +0000707#define MOUNTPORT 635
708#define MNTPATHLEN 1024
709#define MNTNAMLEN 255
710#define FHSIZE 32
711#define FHSIZE3 64
712
713typedef char fhandle[FHSIZE];
714
715typedef struct {
716 unsigned int fhandle3_len;
717 char *fhandle3_val;
718} fhandle3;
719
720enum mountstat3 {
721 MNT_OK = 0,
722 MNT3ERR_PERM = 1,
723 MNT3ERR_NOENT = 2,
724 MNT3ERR_IO = 5,
725 MNT3ERR_ACCES = 13,
726 MNT3ERR_NOTDIR = 20,
727 MNT3ERR_INVAL = 22,
728 MNT3ERR_NAMETOOLONG = 63,
729 MNT3ERR_NOTSUPP = 10004,
730 MNT3ERR_SERVERFAULT = 10006,
731};
732typedef enum mountstat3 mountstat3;
733
734struct fhstatus {
735 unsigned int fhs_status;
736 union {
737 fhandle fhs_fhandle;
738 } fhstatus_u;
739};
740typedef struct fhstatus fhstatus;
741
742struct mountres3_ok {
743 fhandle3 fhandle;
744 struct {
745 unsigned int auth_flavours_len;
746 char *auth_flavours_val;
747 } auth_flavours;
748};
749typedef struct mountres3_ok mountres3_ok;
750
751struct mountres3 {
752 mountstat3 fhs_status;
753 union {
754 mountres3_ok mountinfo;
755 } mountres3_u;
756};
757typedef struct mountres3 mountres3;
758
759typedef char *dirpath;
760
761typedef char *name;
762
763typedef struct mountbody *mountlist;
764
765struct mountbody {
766 name ml_hostname;
767 dirpath ml_directory;
768 mountlist ml_next;
769};
770typedef struct mountbody mountbody;
771
772typedef struct groupnode *groups;
773
774struct groupnode {
775 name gr_name;
776 groups gr_next;
777};
778typedef struct groupnode groupnode;
779
780typedef struct exportnode *exports;
781
782struct exportnode {
783 dirpath ex_dir;
784 groups ex_groups;
785 exports ex_next;
786};
787typedef struct exportnode exportnode;
788
789struct ppathcnf {
790 int pc_link_max;
791 short pc_max_canon;
792 short pc_max_input;
793 short pc_name_max;
794 short pc_path_max;
795 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000796 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000797 char pc_xxx;
798 short pc_mask[2];
799};
800typedef struct ppathcnf ppathcnf;
801
802#define MOUNTPROG 100005
803#define MOUNTVERS 1
804
805#define MOUNTPROC_NULL 0
806#define MOUNTPROC_MNT 1
807#define MOUNTPROC_DUMP 2
808#define MOUNTPROC_UMNT 3
809#define MOUNTPROC_UMNTALL 4
810#define MOUNTPROC_EXPORT 5
811#define MOUNTPROC_EXPORTALL 6
812
813#define MOUNTVERS_POSIX 2
814
815#define MOUNTPROC_PATHCONF 7
816
817#define MOUNT_V3 3
818
819#define MOUNTPROC3_NULL 0
820#define MOUNTPROC3_MNT 1
821#define MOUNTPROC3_DUMP 2
822#define MOUNTPROC3_UMNT 3
823#define MOUNTPROC3_UMNTALL 4
824#define MOUNTPROC3_EXPORT 5
825
826enum {
827#ifndef NFS_FHSIZE
828 NFS_FHSIZE = 32,
829#endif
830#ifndef NFS_PORT
831 NFS_PORT = 2049
832#endif
833};
834
Denis Vlasenko25098f72006-09-14 15:46:33 +0000835/*
836 * We want to be able to compile mount on old kernels in such a way
837 * that the binary will work well on more recent kernels.
838 * Thus, if necessary we teach nfsmount.c the structure of new fields
839 * that will come later.
840 *
841 * Moreover, the new kernel includes conflict with glibc includes
842 * so it is easiest to ignore the kernel altogether (at compile time).
843 */
844
845struct nfs2_fh {
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200846 char data[32];
Denis Vlasenko25098f72006-09-14 15:46:33 +0000847};
848struct nfs3_fh {
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200849 unsigned short size;
850 unsigned char data[64];
Denis Vlasenko25098f72006-09-14 15:46:33 +0000851};
852
853struct nfs_mount_data {
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200854 int version; /* 1 */
855 int fd; /* 1 */
856 struct nfs2_fh old_root; /* 1 */
857 int flags; /* 1 */
858 int rsize; /* 1 */
859 int wsize; /* 1 */
860 int timeo; /* 1 */
861 int retrans; /* 1 */
862 int acregmin; /* 1 */
863 int acregmax; /* 1 */
864 int acdirmin; /* 1 */
865 int acdirmax; /* 1 */
866 struct sockaddr_in addr; /* 1 */
867 char hostname[256]; /* 1 */
868 int namlen; /* 2 */
869 unsigned int bsize; /* 3 */
870 struct nfs3_fh root; /* 4 */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000871};
872
873/* bits in the flags field */
874enum {
875 NFS_MOUNT_SOFT = 0x0001, /* 1 */
876 NFS_MOUNT_INTR = 0x0002, /* 1 */
877 NFS_MOUNT_SECURE = 0x0004, /* 1 */
878 NFS_MOUNT_POSIX = 0x0008, /* 1 */
879 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
880 NFS_MOUNT_NOAC = 0x0020, /* 1 */
881 NFS_MOUNT_TCP = 0x0040, /* 2 */
882 NFS_MOUNT_VER3 = 0x0080, /* 3 */
883 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000884 NFS_MOUNT_NONLM = 0x0200, /* 3 */
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200885 NFS_MOUNT_NOACL = 0x0800, /* 4 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000886 NFS_MOUNT_NORDIRPLUS = 0x4000
Denis Vlasenko25098f72006-09-14 15:46:33 +0000887};
888
889
890/*
891 * We need to translate between nfs status return values and
892 * the local errno values which may not be the same.
893 *
894 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
895 * "after #include <errno.h> the symbol errno is reserved for any use,
896 * it cannot even be used as a struct tag or field name".
897 */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000898#ifndef EDQUOT
Denys Vlasenkocc428142009-12-16 02:06:56 +0100899# define EDQUOT ENOSPC
Denis Vlasenko25098f72006-09-14 15:46:33 +0000900#endif
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000901/* Convert each NFSERR_BLAH into EBLAH */
Denys Vlasenkocc428142009-12-16 02:06:56 +0100902static const uint8_t nfs_err_stat[] = {
903 1, 2, 5, 6, 13, 17,
904 19, 20, 21, 22, 27, 28,
905 30, 63, 66, 69, 70, 71
906};
Denys Vlasenkodc1fd2e2010-05-19 17:01:29 +0200907#if ( \
908 EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \
909 ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \
910 EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
911typedef uint8_t nfs_err_type;
912#else
913typedef uint16_t nfs_err_type;
914#endif
915static const nfs_err_type nfs_err_errnum[] = {
Denys Vlasenkocc428142009-12-16 02:06:56 +0100916 EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST,
917 ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC,
918 EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
Denis Vlasenko25098f72006-09-14 15:46:33 +0000919};
Denis Vlasenko25098f72006-09-14 15:46:33 +0000920static char *nfs_strerror(int status)
921{
922 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000923
Tanguy Pruvot6fef6a32012-05-05 15:26:43 +0200924 for (i = 0; i < (int) ARRAY_SIZE(nfs_err_stat); i++) {
Denys Vlasenkocc428142009-12-16 02:06:56 +0100925 if (nfs_err_stat[i] == status)
926 return strerror(nfs_err_errnum[i]);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000927 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000928 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000929}
930
931static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
932{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200933 return xdr_opaque(xdrs, objp, FHSIZE);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000934}
935
936static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
937{
938 if (!xdr_u_int(xdrs, &objp->fhs_status))
Tanguy Pruvot823694d2012-11-18 13:20:29 +0100939 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200940 if (objp->fhs_status == 0)
941 return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000942 return TRUE;
943}
944
945static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
946{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200947 return xdr_string(xdrs, objp, MNTPATHLEN);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000948}
949
950static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
951{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200952 return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
Tanguy Pruvot823694d2012-11-18 13:20:29 +0100953 (unsigned int *) &objp->fhandle3_len,
954 FHSIZE3);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000955}
956
957static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
958{
959 if (!xdr_fhandle3(xdrs, &objp->fhandle))
960 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200961 return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
Tanguy Pruvot823694d2012-11-18 13:20:29 +0100962 &(objp->auth_flavours.auth_flavours_len),
963 ~0,
964 sizeof(int),
965 (xdrproc_t) xdr_int);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000966}
967
968static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
969{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200970 return xdr_enum(xdrs, (enum_t *) objp);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000971}
972
973static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
974{
975 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
976 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200977 if (objp->fhs_status == MNT_OK)
978 return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000979 return TRUE;
980}
981
982#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
983
Denis Vlasenko25098f72006-09-14 15:46:33 +0000984/*
985 * Unfortunately, the kernel prints annoying console messages
986 * in case of an unexpected nfs mount version (instead of
987 * just returning some error). Therefore we'll have to try
988 * and figure out what version the kernel expects.
989 *
990 * Variables:
991 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
992 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
993 * nfs_mount_version: version this source and running kernel can handle
994 */
995static void
996find_kernel_nfs_mount_version(void)
997{
Denis Vlasenkob9256052007-09-28 10:29:17 +0000998 int kernel_version;
999
1000 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001001 return;
1002
1003 nfs_mount_version = 4; /* default */
1004
1005 kernel_version = get_linux_version_code();
1006 if (kernel_version) {
Denys Vlasenkocc428142009-12-16 02:06:56 +01001007 if (kernel_version < KERNEL_VERSION(2,2,18))
Denis Vlasenko25098f72006-09-14 15:46:33 +00001008 nfs_mount_version = 3;
1009 /* else v4 since 2.3.99pre4 */
1010 }
1011}
1012
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +00001013static void
Denis Vlasenkob9256052007-09-28 10:29:17 +00001014get_mountport(struct pmap *pm_mnt,
1015 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001016 long unsigned prog,
1017 long unsigned version,
1018 long unsigned proto,
1019 long unsigned port)
1020{
1021 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001022
1023 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001024/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
1025 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001026 pmap = pmap_getmaps(server_addr);
1027
1028 if (version > MAX_NFSPROT)
1029 version = MAX_NFSPROT;
1030 if (!prog)
1031 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001032 pm_mnt->pm_prog = prog;
1033 pm_mnt->pm_vers = version;
1034 pm_mnt->pm_prot = proto;
1035 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001036
Denis Vlasenko25098f72006-09-14 15:46:33 +00001037 while (pmap) {
1038 if (pmap->pml_map.pm_prog != prog)
1039 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001040 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001041 goto next;
1042 if (version > 2 && pmap->pml_map.pm_vers != version)
1043 goto next;
1044 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1045 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001046 if (pmap->pml_map.pm_vers > MAX_NFSPROT
1047 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1048 || (port && pmap->pml_map.pm_port != port)
1049 ) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001050 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001051 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001052 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1053 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001054 pmap = pmap->pml_next;
1055 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001056 if (!pm_mnt->pm_vers)
1057 pm_mnt->pm_vers = MOUNTVERS;
1058 if (!pm_mnt->pm_port)
1059 pm_mnt->pm_port = MOUNTPORT;
1060 if (!pm_mnt->pm_prot)
1061 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001062}
1063
Denis Vlasenkof0000652007-09-04 18:30:26 +00001064#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001065static int daemonize(void)
1066{
Denis Vlasenko25098f72006-09-14 15:46:33 +00001067 int pid = fork();
1068 if (pid < 0) /* error */
1069 return -errno;
1070 if (pid > 0) /* parent */
1071 return 0;
1072 /* child */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001073 close(0);
1074 xopen(bb_dev_null, O_RDWR);
1075 xdup2(0, 1);
1076 xdup2(0, 2);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001077 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +00001078 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001079 logmode = LOGMODE_SYSLOG;
1080 return 1;
1081}
Denis Vlasenkof0000652007-09-04 18:30:26 +00001082#else
1083static inline int daemonize(void) { return -ENOSYS; }
1084#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001085
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001086/* TODO */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001087static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001088{
1089 return 0;
1090}
1091
1092/* RPC strerror analogs are terminally idiotic:
1093 * *mandatory* prefix and \n at end.
1094 * This hopefully helps. Usage:
1095 * error_msg_rpc(clnt_*error*(" ")) */
1096static void error_msg_rpc(const char *msg)
1097{
Denis Vlasenko23514fe2006-09-19 14:07:52 +00001098 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001099 while (msg[0] == ' ' || msg[0] == ':') msg++;
1100 len = strlen(msg);
1101 while (len && msg[len-1] == '\n') len--;
1102 bb_error_msg("%.*s", len, msg);
1103}
1104
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001105/* NB: mp->xxx fields may be trashed on exit */
Tanguy Pruvot823694d2012-11-18 13:20:29 +01001106static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001107{
1108 CLIENT *mclient;
1109 char *hostname;
1110 char *pathname;
1111 char *mounthost;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001112 /* prior to 2.6.23, kernel took NFS options in a form of this struct
1113 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1114 * then data pointer is interpreted as a string. */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001115 struct nfs_mount_data data;
1116 char *opt;
1117 struct hostent *hp;
1118 struct sockaddr_in server_addr;
1119 struct sockaddr_in mount_server_addr;
1120 int msock, fsock;
1121 union {
1122 struct fhstatus nfsv2;
1123 struct mountres3 nfsv3;
1124 } status;
1125 int daemonized;
1126 char *s;
1127 int port;
1128 int mountport;
1129 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001130#if BB_MMU
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001131 smallint bg = 0;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001132#else
1133 enum { bg = 0 };
1134#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001135 int retry;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001136 int mountprog;
1137 int mountvers;
1138 int nfsprog;
1139 int nfsvers;
1140 int retval;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001141 /* these all are one-bit really. gcc 4.3.1 likes this combination: */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001142 smallint tcp;
1143 smallint soft;
1144 int intr;
1145 int posix;
1146 int nocto;
1147 int noac;
1148 int nordirplus;
1149 int nolock;
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001150 int noacl;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001151
1152 find_kernel_nfs_mount_version();
1153
1154 daemonized = 0;
1155 mounthost = NULL;
1156 retval = ETIMEDOUT;
1157 msock = fsock = -1;
1158 mclient = NULL;
1159
1160 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1161
1162 filteropts = xstrdup(filteropts); /* going to trash it later... */
1163
1164 hostname = xstrdup(mp->mnt_fsname);
1165 /* mount_main() guarantees that ':' is there */
1166 s = strchr(hostname, ':');
1167 pathname = s + 1;
1168 *s = '\0';
1169 /* Ignore all but first hostname in replicated mounts
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001170 * until they can be fully supported. (mack@sgi.com) */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001171 s = strchr(hostname, ',');
1172 if (s) {
1173 *s = '\0';
1174 bb_error_msg("warning: multiple hostnames not supported");
1175 }
1176
1177 server_addr.sin_family = AF_INET;
1178 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1179 hp = gethostbyname(hostname);
1180 if (hp == NULL) {
1181 bb_herror_msg("%s", hostname);
1182 goto fail;
1183 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001184 if (hp->h_length != (int)sizeof(struct in_addr)) {
1185 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001186 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001187 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001188 }
1189
1190 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1191
1192 /* add IP address to mtab options for use when unmounting */
1193
1194 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1195 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1196 } else {
1197 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1198 mp->mnt_opts[0] ? "," : "",
1199 inet_ntoa(server_addr.sin_addr));
1200 free(mp->mnt_opts);
1201 mp->mnt_opts = tmp;
1202 }
1203
1204 /* Set default options.
1205 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1206 * let the kernel decide.
1207 * timeo is filled in after we know whether it'll be TCP or UDP. */
1208 memset(&data, 0, sizeof(data));
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001209 data.retrans = 3;
1210 data.acregmin = 3;
1211 data.acregmax = 60;
1212 data.acdirmin = 30;
1213 data.acdirmax = 60;
1214 data.namlen = NAME_MAX;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001215
Denis Vlasenko25098f72006-09-14 15:46:33 +00001216 soft = 0;
1217 intr = 0;
1218 posix = 0;
1219 nocto = 0;
1220 nolock = 0;
1221 noac = 0;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001222 nordirplus = 0;
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001223 noacl = 0;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001224 retry = 10000; /* 10000 minutes ~ 1 week */
Bernhard Reutner-Fischer88206292011-05-04 19:03:30 +02001225 tcp = 1; /* nfs-utils uses tcp per default */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001226
1227 mountprog = MOUNTPROG;
1228 mountvers = 0;
1229 port = 0;
1230 mountport = 0;
1231 nfsprog = 100003;
1232 nfsvers = 0;
1233
1234 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001235 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001236 char *opteq = strchr(opt, '=');
1237 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001238 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001239 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001240 /* 0 */ "rsize\0"
1241 /* 1 */ "wsize\0"
1242 /* 2 */ "timeo\0"
1243 /* 3 */ "retrans\0"
1244 /* 4 */ "acregmin\0"
1245 /* 5 */ "acregmax\0"
1246 /* 6 */ "acdirmin\0"
1247 /* 7 */ "acdirmax\0"
1248 /* 8 */ "actimeo\0"
1249 /* 9 */ "retry\0"
1250 /* 10 */ "port\0"
1251 /* 11 */ "mountport\0"
1252 /* 12 */ "mounthost\0"
1253 /* 13 */ "mountprog\0"
1254 /* 14 */ "mountvers\0"
1255 /* 15 */ "nfsprog\0"
1256 /* 16 */ "nfsvers\0"
1257 /* 17 */ "vers\0"
1258 /* 18 */ "proto\0"
1259 /* 19 */ "namlen\0"
1260 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001261
1262 *opteq++ = '\0';
1263 idx = index_in_strings(options, opt);
1264 switch (idx) {
1265 case 12: // "mounthost"
1266 mounthost = xstrndup(opteq,
1267 strcspn(opteq, " \t\n\r,"));
1268 continue;
1269 case 18: // "proto"
1270 if (!strncmp(opteq, "tcp", 3))
1271 tcp = 1;
1272 else if (!strncmp(opteq, "udp", 3))
1273 tcp = 0;
1274 else
1275 bb_error_msg("warning: unrecognized proto= option");
1276 continue;
1277 case 20: // "addr" - ignore
1278 continue;
Peter Korsgaard301fe502011-02-21 17:52:13 +01001279 case -1: // unknown
1280 if (vfsflags & MS_REMOUNT)
1281 continue;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001282 }
1283
Denys Vlasenko77832482010-08-12 14:14:45 +02001284 val = xatoi_positive(opteq);
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001285 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001286 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001287 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001288 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001289 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001290 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001291 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001292 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001293 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001294 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001295 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001296 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001297 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001298 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001299 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001300 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001301 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001302 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001303 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001304 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001305 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001306 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001307 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001308 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001309 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001310 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001311 data.acregmin = val;
1312 data.acregmax = val;
1313 data.acdirmin = val;
1314 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001315 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001316 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001317 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001318 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001319 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001320 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001321 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001322 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001323 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001324 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001325 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001326 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001327 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001328 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001329 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001330 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001331 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001332 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001333 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001334 case 16: // "nfsvers"
1335 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001336 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001337 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001338 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001339 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001340 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001341 //else
1342 // bb_error_msg("warning: option namlen is not supported\n");
1343 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001344 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001345 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1346 goto fail;
1347 }
1348 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001349 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001350 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001351 "bg\0"
1352 "fg\0"
1353 "soft\0"
1354 "hard\0"
1355 "intr\0"
1356 "posix\0"
1357 "cto\0"
1358 "ac\0"
1359 "tcp\0"
1360 "udp\0"
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001361 "lock\0"
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001362 "rdirplus\0"
1363 "acl\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001364 int val = 1;
1365 if (!strncmp(opt, "no", 2)) {
1366 val = 0;
1367 opt += 2;
1368 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001369 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001370 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001371#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001372 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001373#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001374 break;
1375 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001376#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001377 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001378#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001379 break;
1380 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001381 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001382 break;
1383 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001384 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001385 break;
1386 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001387 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001388 break;
1389 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001390 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001391 break;
1392 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001393 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001394 break;
1395 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001396 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001397 break;
1398 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001399 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001400 break;
1401 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001402 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001403 break;
1404 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001405 if (nfs_mount_version >= 3)
1406 nolock = !val;
1407 else
1408 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001409 break;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001410 case 11: //rdirplus
1411 nordirplus = !val;
1412 break;
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001413 case 12: // acl
1414 noacl = !val;
1415 break;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001416 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001417 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1418 goto fail;
1419 }
1420 }
1421 }
1422 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1423
1424 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1425 | (intr ? NFS_MOUNT_INTR : 0)
1426 | (posix ? NFS_MOUNT_POSIX : 0)
1427 | (nocto ? NFS_MOUNT_NOCTO : 0)
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001428 | (noac ? NFS_MOUNT_NOAC : 0)
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001429 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
1430 | (noacl ? NFS_MOUNT_NOACL : 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001431 if (nfs_mount_version >= 2)
1432 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1433 if (nfs_mount_version >= 3)
1434 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1435 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1436 bb_error_msg("NFSv%d not supported", nfsvers);
1437 goto fail;
1438 }
1439 if (nfsvers && !mountvers)
1440 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1441 if (nfsvers && nfsvers < mountvers) {
1442 mountvers = nfsvers;
1443 }
1444
1445 /* Adjust options if none specified */
1446 if (!data.timeo)
1447 data.timeo = tcp ? 70 : 7;
1448
Denis Vlasenko25098f72006-09-14 15:46:33 +00001449 data.version = nfs_mount_version;
1450
1451 if (vfsflags & MS_REMOUNT)
1452 goto do_mount;
1453
1454 /*
1455 * If the previous mount operation on the same host was
1456 * backgrounded, and the "bg" for this mount is also set,
1457 * give up immediately, to avoid the initial timeout.
1458 */
1459 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001460 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001461 if (daemonized <= 0) { /* parent or error */
1462 retval = -daemonized;
1463 goto ret;
1464 }
1465 }
1466
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001467 /* Create mount daemon client */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001468 /* See if the nfs host = mount host. */
1469 if (mounthost) {
1470 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1471 mount_server_addr.sin_family = AF_INET;
1472 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1473 } else {
1474 hp = gethostbyname(mounthost);
1475 if (hp == NULL) {
1476 bb_herror_msg("%s", mounthost);
1477 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001478 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001479 if (hp->h_length != (int)sizeof(struct in_addr)) {
1480 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001481 }
1482 mount_server_addr.sin_family = AF_INET;
Denys Vlasenko99069332010-02-27 19:38:19 +01001483 memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001484 }
1485 }
1486
1487 /*
1488 * The following loop implements the mount retries. When the mount
1489 * times out, and the "bg" option is set, we background ourself
1490 * and continue trying.
1491 *
1492 * The case where the mount point is not present and the "bg"
1493 * option is set, is treated as a timeout. This is done to
1494 * support nested mounts.
1495 *
1496 * The "retry" count specified by the user is the number of
1497 * minutes to retry before giving up.
1498 */
1499 {
1500 struct timeval total_timeout;
1501 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001502 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001503 time_t t;
1504 time_t prevt;
1505 time_t timeout;
1506
1507 retry_timeout.tv_sec = 3;
1508 retry_timeout.tv_usec = 0;
1509 total_timeout.tv_sec = 20;
1510 total_timeout.tv_usec = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001511/* FIXME: use monotonic()? */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001512 timeout = time(NULL) + 60 * retry;
1513 prevt = 0;
1514 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001515 retry:
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001516 /* Be careful not to use too many CPU cycles */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001517 if (t - prevt < 30)
1518 sleep(30);
1519
Denis Vlasenkob9256052007-09-28 10:29:17 +00001520 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001521 mountprog,
1522 mountvers,
1523 proto,
1524 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001525 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001526
1527 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001528 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001529 msock = RPC_ANYSOCK;
1530
Denis Vlasenkob9256052007-09-28 10:29:17 +00001531 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001532 case IPPROTO_UDP:
1533 mclient = clntudp_create(&mount_server_addr,
Tanguy Pruvot823694d2012-11-18 13:20:29 +01001534 pm_mnt.pm_prog,
1535 pm_mnt.pm_vers,
1536 retry_timeout,
1537 &msock);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001538 if (mclient)
1539 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001540 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001541 msock = RPC_ANYSOCK;
1542 case IPPROTO_TCP:
1543 mclient = clnttcp_create(&mount_server_addr,
Tanguy Pruvot823694d2012-11-18 13:20:29 +01001544 pm_mnt.pm_prog,
1545 pm_mnt.pm_vers,
1546 &msock, 0, 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001547 break;
1548 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001549 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001550 }
1551 if (!mclient) {
1552 if (!daemonized && prevt == 0)
1553 error_msg_rpc(clnt_spcreateerror(" "));
1554 } else {
1555 enum clnt_stat clnt_stat;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001556
1557 /* Try to mount hostname:pathname */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001558 mclient->cl_auth = authunix_create_default();
1559
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001560 /* Make pointers in xdr_mountres3 NULL so
Denis Vlasenko25098f72006-09-14 15:46:33 +00001561 * that xdr_array allocates memory for us
1562 */
1563 memset(&status, 0, sizeof(status));
1564
Denis Vlasenkob9256052007-09-28 10:29:17 +00001565 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001566 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
Tanguy Pruvot823694d2012-11-18 13:20:29 +01001567 (xdrproc_t) xdr_dirpath,
1568 (caddr_t) &pathname,
1569 (xdrproc_t) xdr_mountres3,
1570 (caddr_t) &status,
1571 total_timeout);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001572 else
1573 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
Tanguy Pruvot823694d2012-11-18 13:20:29 +01001574 (xdrproc_t) xdr_dirpath,
1575 (caddr_t) &pathname,
1576 (xdrproc_t) xdr_fhstatus,
1577 (caddr_t) &status,
1578 total_timeout);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001579
1580 if (clnt_stat == RPC_SUCCESS)
1581 goto prepare_kernel_data; /* we're done */
1582 if (errno != ECONNREFUSED) {
1583 error_msg_rpc(clnt_sperror(mclient, " "));
1584 goto fail; /* don't retry */
1585 }
1586 /* Connection refused */
1587 if (!daemonized && prevt == 0) /* print just once */
1588 error_msg_rpc(clnt_sperror(mclient, " "));
1589 auth_destroy(mclient->cl_auth);
1590 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001591 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001592 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001593 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001594 }
1595
1596 /* Timeout. We are going to retry... maybe */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001597 if (!bg)
1598 goto fail;
1599 if (!daemonized) {
1600 daemonized = daemonize();
1601 if (daemonized <= 0) { /* parent or error */
1602 retval = -daemonized;
1603 goto ret;
1604 }
1605 }
1606 prevt = t;
1607 t = time(NULL);
1608 if (t >= timeout)
1609 /* TODO error message */
1610 goto fail;
1611
1612 goto retry;
1613 }
1614
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001615 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001616
1617 if (nfsvers == 2) {
1618 if (status.nfsv2.fhs_status != 0) {
1619 bb_error_msg("%s:%s failed, reason given by server: %s",
1620 hostname, pathname,
1621 nfs_strerror(status.nfsv2.fhs_status));
1622 goto fail;
1623 }
1624 memcpy(data.root.data,
1625 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1626 NFS_FHSIZE);
1627 data.root.size = NFS_FHSIZE;
1628 memcpy(data.old_root.data,
1629 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1630 NFS_FHSIZE);
1631 } else {
1632 fhandle3 *my_fhandle;
1633 if (status.nfsv3.fhs_status != 0) {
1634 bb_error_msg("%s:%s failed, reason given by server: %s",
1635 hostname, pathname,
1636 nfs_strerror(status.nfsv3.fhs_status));
1637 goto fail;
1638 }
1639 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1640 memset(data.old_root.data, 0, NFS_FHSIZE);
1641 memset(&data.root, 0, sizeof(data.root));
1642 data.root.size = my_fhandle->fhandle3_len;
1643 memcpy(data.root.data,
1644 (char *) my_fhandle->fhandle3_val,
1645 my_fhandle->fhandle3_len);
1646
1647 data.flags |= NFS_MOUNT_VER3;
1648 }
1649
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001650 /* Create nfs socket for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001651 if (tcp) {
1652 if (nfs_mount_version < 3) {
1653 bb_error_msg("NFS over TCP is not supported");
1654 goto fail;
1655 }
1656 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1657 } else
1658 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1659 if (fsock < 0) {
1660 bb_perror_msg("nfs socket");
1661 goto fail;
1662 }
1663 if (bindresvport(fsock, 0) < 0) {
1664 bb_perror_msg("nfs bindresvport");
1665 goto fail;
1666 }
1667 if (port == 0) {
1668 server_addr.sin_port = PMAPPORT;
1669 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1670 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1671 if (port == 0)
1672 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001673 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001674 server_addr.sin_port = htons(port);
1675
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001676 /* Prepare data structure for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001677 data.fd = fsock;
1678 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1679 strncpy(data.hostname, hostname, sizeof(data.hostname));
1680
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001681 /* Clean up */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001682 auth_destroy(mclient->cl_auth);
1683 clnt_destroy(mclient);
1684 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001685 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001686
1687 if (bg) {
1688 /* We must wait until mount directory is available */
1689 struct stat statbuf;
1690 int delay = 1;
1691 while (stat(mp->mnt_dir, &statbuf) == -1) {
1692 if (!daemonized) {
1693 daemonized = daemonize();
1694 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001695/* FIXME: parent doesn't close fsock - ??! */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001696 retval = -daemonized;
1697 goto ret;
1698 }
1699 }
1700 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1701 delay *= 2;
1702 if (delay > 30)
1703 delay = 30;
1704 }
1705 }
1706
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001707 /* Perform actual mount */
1708 do_mount:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001709 retval = mount_it_now(mp, vfsflags, (char*)&data);
1710 goto ret;
1711
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001712 /* Abort */
1713 fail:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001714 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001715 if (mclient) {
1716 auth_destroy(mclient->cl_auth);
1717 clnt_destroy(mclient);
1718 }
1719 close(msock);
1720 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001721 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001722 close(fsock);
1723
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001724 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001725 free(hostname);
1726 free(mounthost);
1727 free(filteropts);
1728 return retval;
1729}
1730
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001731#else // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001732
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001733/* Linux 2.6.23+ supports nfs mounts with options passed as a string.
1734 * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
1735 * (However, note that then you lose any chances that NFS over IPv6 would work).
1736 */
Tanguy Pruvot823694d2012-11-18 13:20:29 +01001737static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001738{
1739 len_and_sockaddr *lsa;
1740 char *opts;
1741 char *end;
1742 char *dotted;
1743 int ret;
1744
1745# if ENABLE_FEATURE_IPV6
1746 end = strchr(mp->mnt_fsname, ']');
1747 if (end && end[1] == ':')
1748 end++;
1749 else
1750# endif
1751 /* mount_main() guarantees that ':' is there */
1752 end = strchr(mp->mnt_fsname, ':');
1753
1754 *end = '\0';
1755 lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
1756 *end = ':';
1757 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1758 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1759 opts = xasprintf("%s%saddr=%s",
1760 filteropts ? filteropts : "",
1761 filteropts ? "," : "",
1762 dotted
1763 );
1764 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1765 ret = mount_it_now(mp, vfsflags, opts);
1766 if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1767
1768 return ret;
1769}
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001770
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001771#endif // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001772
1773// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1774// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001775// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001776static int singlemount(struct mntent *mp, int ignore_busy)
1777{
Denis Vlasenkob4133682008-02-18 13:05:38 +00001778 int rc = -1;
Tanguy Pruvot823694d2012-11-18 13:20:29 +01001779 unsigned long vfsflags;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001780 char *loopFile = NULL, *filteropts = NULL;
maxwen27116ba2015-08-14 21:41:28 +02001781 char *detected_fstype = NULL;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001782 llist_t *fl = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001783 struct stat st;
1784
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001785 errno = 0;
1786
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001787 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1788
maxwen27116ba2015-08-14 21:41:28 +02001789 if (user_fstype) {
1790 // Treat fstype "auto" as unspecified
1791 if (mp->mnt_type && !strcmp(mp->mnt_type, "auto"))
1792 mp->mnt_type = NULL;
1793 } else if (mp->mnt_type) {
1794 // If user didn't specify an fstype and blkid disagrees or the
1795 // fstype is "auto", trust blkid's determination of the fstype.
1796 detected_fstype = get_fstype_from_devname(mp->mnt_fsname);
1797
1798 if (!strcmp(mp->mnt_type, "auto") ||
1799 (detected_fstype && strcmp(detected_fstype, mp->mnt_type)))
1800 mp->mnt_type = detected_fstype;
1801 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001802
Denis Vlasenko2535f122007-09-15 13:28:30 +00001803 // Might this be a virtual filesystem?
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001804 if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1805 char *args[35];
1806 char *s;
1807 int n;
1808 // fsname: "cmd#arg1#arg2..."
1809 // WARNING: allows execution of arbitrary commands!
1810 // Try "mount 'sh#-c#sh' bogus_dir".
1811 // It is safe ONLY because non-root
1812 // cannot use two-argument mount command
1813 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1814 // "mount: can't find sh#-c#sh in /etc/fstab"
1815 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1816
1817 s = mp->mnt_fsname;
1818 n = 0;
1819 args[n++] = s;
1820 while (*s && n < 35 - 2) {
1821 if (*s++ == '#' && *s != '#') {
1822 s[-1] = '\0';
1823 args[n++] = s;
Denis Vlasenko2535f122007-09-15 13:28:30 +00001824 }
1825 }
Denis Vlasenko2535f122007-09-15 13:28:30 +00001826 args[n++] = mp->mnt_dir;
1827 args[n] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +01001828 rc = spawn_and_wait(args);
Denis Vlasenko2535f122007-09-15 13:28:30 +00001829 goto report_error;
1830 }
1831
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001832 // Might this be an CIFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001833 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001834 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1835 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1836 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001837 ) {
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001838 int len;
1839 char c;
Tanguy Pruvot823694d2012-11-18 13:20:29 +01001840 char *hostname, *share;
1841 char *dotted, *ip;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001842 len_and_sockaddr *lsa;
Tanguy Pruvot823694d2012-11-18 13:20:29 +01001843
1844 // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001845
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001846 hostname = mp->mnt_fsname + 2;
1847 len = strcspn(hostname, "/\\");
Tanguy Pruvot823694d2012-11-18 13:20:29 +01001848 share = hostname + len + 1;
1849 if (len == 0 // 3rd char is a [back]slash (IOW: empty hostname)
1850 || share[-1] == '\0' // no [back]slash after hostname
1851 || share[0] == '\0' // empty share name
1852 ) {
Denis Vlasenko5c329932009-04-12 12:16:21 +00001853 goto report_error;
Tanguy Pruvot823694d2012-11-18 13:20:29 +01001854 }
1855 c = share[-1];
1856 share[-1] = '\0';
1857 len = strcspn(share, "/\\");
1858
1859 // "unc=\\hostname\share" option is mandatory
1860 // after CIFS option parsing was rewritten in Linux 3.4.
1861 // Must use backslashes.
1862 // If /dir1/dir2 is present, also add "prefixpath=dir1/dir2"
1863 {
1864 char *unc = xasprintf(
1865 share[len] != '\0' /* "/dir1/dir2" exists? */
1866 ? "unc=\\\\%s\\%.*s,prefixpath=%s"
1867 : "unc=\\\\%s\\%.*s",
1868 hostname,
1869 len, share,
1870 share + len + 1 /* "dir1/dir2" */
1871 );
maxwen27116ba2015-08-14 21:41:28 +02001872 parse_mount_options(unc, &filteropts);
1873 if (ENABLE_FEATURE_CLEAN_UP) free(unc);
Tanguy Pruvot823694d2012-11-18 13:20:29 +01001874 }
1875
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001876 lsa = host2sockaddr(hostname, 0);
Tanguy Pruvot823694d2012-11-18 13:20:29 +01001877 share[-1] = c;
Denis Vlasenko5c329932009-04-12 12:16:21 +00001878 if (!lsa)
1879 goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001880
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001881 // Insert "ip=..." option into options
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001882 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001883 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001884 ip = xasprintf("ip=%s", dotted);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001885 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001886 parse_mount_options(ip, &filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001887 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001888
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001889 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001890 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001891
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001892 goto report_error;
1893 }
1894
1895 // Might this be an NFS filesystem?
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001896 if ((!mp->mnt_type || strncmp(mp->mnt_type, "nfs", 3) == 0)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001897 && strchr(mp->mnt_fsname, ':') != NULL
1898 ) {
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001899 if (!mp->mnt_type)
1900 mp->mnt_type = (char*)"nfs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001901 rc = nfsmount(mp, vfsflags, filteropts);
1902 goto report_error;
1903 }
1904
1905 // Look at the file. (Not found isn't a failure for remount, or for
1906 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001907 // (We use stat, not lstat, in order to allow
1908 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001909 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001910 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1911 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001912 // Do we need to allocate a loopback device for it?
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001913 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1914 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001915 mp->mnt_fsname = NULL; // will receive malloced loop dev name
Tanguy Pruvot823694d2012-11-18 13:20:29 +01001916 if (set_loop(&mp->mnt_fsname, loopFile, 0, /*ro:*/ (vfsflags & MS_RDONLY)) < 0) {
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001917 if (errno == EPERM || errno == EACCES)
Tanguy Pruvot8aeb3712011-06-30 08:59:26 +02001918 bb_error_msg("%s", bb_msg_perm_denied_are_you_root);
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001919 else
Denys Vlasenko6331cf02009-11-13 09:08:27 +01001920 bb_perror_msg("can't setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001921 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001922 }
1923
1924 // Autodetect bind mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001925 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1926 vfsflags |= MS_BIND;
1927 }
1928
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001929 // If we know the fstype (or don't need to), jump straight
1930 // to the actual mount.
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001931 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001932 char *next;
1933 for (;;) {
1934 next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
1935 if (next)
1936 *next = '\0';
1937 rc = mount_it_now(mp, vfsflags, filteropts);
1938 if (rc == 0 || !next)
1939 break;
1940 mp->mnt_type = next + 1;
1941 }
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001942 } else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001943 // Loop through filesystem types until mount succeeds
1944 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001945
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001946 // Initialize list of block backed filesystems.
1947 // This has to be done here so that during "mount -a",
1948 // mounts after /proc shows up can autodetect.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001949 if (!fslist) {
1950 fslist = get_block_backed_filesystems();
1951 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1952 atexit(delete_block_backed_filesystems);
1953 }
1954
1955 for (fl = fslist; fl; fl = fl->link) {
1956 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001957 rc = mount_it_now(mp, vfsflags, filteropts);
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001958 if (rc == 0)
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001959 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001960 }
1961 }
1962
1963 // If mount failed, clean up loop file (if any).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001964 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1965 del_loop(mp->mnt_fsname);
1966 if (ENABLE_FEATURE_CLEAN_UP) {
1967 free(loopFile);
1968 free(mp->mnt_fsname);
1969 }
1970 }
1971
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001972 report_error:
1973 if (ENABLE_FEATURE_CLEAN_UP)
1974 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001975
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001976 if (errno == EBUSY && ignore_busy)
1977 return 0;
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001978 if (rc != 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001979 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001980 return rc;
1981}
1982
Michael Abbott6b5accb2009-12-04 03:33:07 +01001983// -O support
1984// -O interprets a list of filter options which select whether a mount
1985// point will be mounted: only mounts with options matching *all* filtering
1986// options will be selected.
1987// By default each -O filter option must be present in the list of mount
1988// options, but if it is prefixed by "no" then it must be absent.
1989// For example,
1990// -O a,nob,c matches -o a,c but fails to match -o a,b,c
1991// (and also fails to match -o a because -o c is absent).
1992//
1993// It is different from -t in that each option is matched exactly; a leading
1994// "no" at the beginning of one option does not negate the rest.
1995static int match_opt(const char *fs_opt_in, const char *O_opt)
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001996{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001997 if (!O_opt)
Michael Abbott6b5accb2009-12-04 03:33:07 +01001998 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001999
Michael Abbott6b5accb2009-12-04 03:33:07 +01002000 while (*O_opt) {
2001 const char *fs_opt = fs_opt_in;
2002 int O_len;
2003 int match;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002004
Michael Abbott6b5accb2009-12-04 03:33:07 +01002005 // If option begins with "no" then treat as an inverted match:
2006 // matching is a failure
2007 match = 0;
2008 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
2009 match = 1;
2010 O_opt += 2;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002011 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01002012 // Isolate the current O option
2013 O_len = strchrnul(O_opt, ',') - O_opt;
2014 // Check for a match against existing options
2015 while (1) {
2016 if (strncmp(fs_opt, O_opt, O_len) == 0
2017 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
2018 ) {
2019 if (match)
2020 return 0; // "no" prefix, but option found
2021 match = 1; // current O option found, go check next one
2022 break;
2023 }
2024 fs_opt = strchr(fs_opt, ',');
2025 if (!fs_opt)
2026 break;
2027 fs_opt++;
2028 }
2029 if (match == 0)
2030 return 0; // match wanted but not found
2031 if (O_opt[O_len] == '\0') // end?
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002032 break;
Michael Abbott6b5accb2009-12-04 03:33:07 +01002033 // Step to the next O option
2034 O_opt += O_len + 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002035 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01002036 // If we get here then everything matched
2037 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002038}
2039
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002040// Parse options, if necessary parse fstab/mtab, and call singlemount for
2041// each directory to be mounted.
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00002042int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002043int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002044{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002045 char *cmdopts = xzalloc(1);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002046 char *fstype = NULL;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002047 char *O_optmatch = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002048 char *storage_path;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00002049 llist_t *lst_o = NULL;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00002050 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002051 FILE *fstab;
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002052 int i, j;
2053 int rc = EXIT_SUCCESS;
Tanguy Pruvot823694d2012-11-18 13:20:29 +01002054 unsigned long cmdopt_flags;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00002055 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002056 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002057 IF_NOT_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002058
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002059 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00002060
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02002061 INIT_G();
2062
Denis Vlasenkof732e962008-02-18 12:07:49 +00002063 // Parse long options, like --bind and --move. Note that -o option
2064 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002065 for (i = j = 1; argv[i]; i++) {
2066 if (argv[i][0] == '-' && argv[i][1] == '-')
2067 append_mount_options(&cmdopts, argv[i] + 2);
2068 else
2069 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002070 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002071 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002072
2073 // Parse remaining options
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002074 // Max 2 params; -o is a list, -v is a counter
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002075 opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002076 opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002077 IF_FEATURE_MOUNT_VERBOSE(, &verbose));
maxwen27116ba2015-08-14 21:41:28 +02002078
2079 if (opt & OPT_t) user_fstype = 1;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00002080 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00002081 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2082 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00002083 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002084
2085 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002086 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00002087 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002088 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2089
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002090 if (!mountTable)
2091 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002092
Denis Vlasenko2535f122007-09-15 13:28:30 +00002093 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002094 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002095 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002096 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00002097 // util-linux 2.12a happily shows rootfs...
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002098 //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002099
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002100 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002101 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2102 mtpair->mnt_dir, mtpair->mnt_type,
2103 mtpair->mnt_opts);
2104 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002105 if (ENABLE_FEATURE_CLEAN_UP)
2106 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002107 return EXIT_SUCCESS;
2108 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002109 storage_path = NULL;
2110 } else {
2111 // When we have two arguments, the second is the directory and we can
2112 // skip looking at fstab entirely. We can always abspath() the directory
2113 // argument when we get it.
2114 if (argv[1]) {
2115 if (nonroot)
Tanguy Pruvot8aeb3712011-06-30 08:59:26 +02002116 bb_error_msg_and_die("%s", bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002117 mtpair->mnt_fsname = argv[0];
2118 mtpair->mnt_dir = argv[1];
2119 mtpair->mnt_type = fstype;
2120 mtpair->mnt_opts = cmdopts;
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002121 resolve_mount_spec(&mtpair->mnt_fsname);
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002122 rc = singlemount(mtpair, /*ignore_busy:*/ 0);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002123 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00002124 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002125 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002126 }
2127
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002128 // Past this point, we are handling either "mount -a [opts]"
2129 // or "mount [opts] single_param"
2130
Tanguy Pruvot823694d2012-11-18 13:20:29 +01002131 cmdopt_flags = parse_mount_options(cmdopts, NULL);
2132 if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
Tanguy Pruvot8aeb3712011-06-30 08:59:26 +02002133 bb_error_msg_and_die("%s", bb_msg_you_must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00002134
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002135 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002136 if (ENABLE_FEATURE_MOUNT_FLAGS
Tanguy Pruvot823694d2012-11-18 13:20:29 +01002137 && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002138 ) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002139 // verbose_mount(source, target, type, flags, data)
Tanguy Pruvot823694d2012-11-18 13:20:29 +01002140 rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002141 if (rc)
2142 bb_simple_perror_msg_and_die(argv[0]);
2143 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002144 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00002145
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002146 // Open either fstab or mtab
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002147 fstabname = "/etc/fstab";
Tanguy Pruvot823694d2012-11-18 13:20:29 +01002148 if (cmdopt_flags & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002149 // WARNING. I am not sure this matches util-linux's
2150 // behavior. It's possible util-linux does not
2151 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002152 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002153 }
2154 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00002155 if (!fstab)
Denys Vlasenko651a2692010-03-23 16:25:17 +01002156 bb_perror_msg_and_die("can't read '%s'", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002157
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002158 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00002159 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002160 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002161 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002162
2163 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00002164 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002165 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002166 GETMNTENT_BUFSIZE/2)
2167 ) { // End of fstab/mtab is reached
2168 mtcur = mtother; // the thing we found last time
2169 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002170 }
2171
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002172 // If we're trying to mount something specific and this isn't it,
2173 // skip it. Note we must match the exact text in fstab (ala
2174 // "proc") or a full path from root
2175 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002176
2177 // Is this what we're looking for?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002178 if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2179 && strcmp(storage_path, mtcur->mnt_fsname) != 0
2180 && strcmp(argv[0], mtcur->mnt_dir) != 0
2181 && strcmp(storage_path, mtcur->mnt_dir) != 0
2182 ) {
2183 continue; // no
2184 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002185
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002186 // Remember this entry. Something later may have
2187 // overmounted it, and we want the _last_ match.
2188 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002189
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002190 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002191 } else {
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002192 struct mntent *mp;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002193 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002194 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002195 if (nonroot)
Tanguy Pruvot8aeb3712011-06-30 08:59:26 +02002196 bb_error_msg_and_die("%s", bb_msg_you_must_be_root);
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002197
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002198 // Does type match? (NULL matches always)
2199 if (!match_fstype(mtcur, fstype))
2200 continue;
2201
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002202 // Skip noauto and swap anyway
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002203 if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2204 // swap is bogus "fstype", parse_mount_options can't check fstypes
2205 || strcasecmp(mtcur->mnt_type, "swap") == 0
2206 ) {
2207 continue;
2208 }
2209
2210 // Does (at least one) option match?
2211 // (NULL matches always)
2212 if (!match_opt(mtcur->mnt_opts, O_optmatch))
2213 continue;
2214
2215 resolve_mount_spec(&mtcur->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002216
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002217 // NFS mounts want this to be xrealloc-able
2218 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002219
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002220 // If nothing is mounted on this directory...
2221 // (otherwise repeated "mount -a" mounts everything again)
2222 mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2223 // We do not check fsname match of found mount point -
2224 // "/" may have fsname of "/dev/root" while fstab
2225 // says "/dev/something_else".
2226 if (mp) {
Denys Vlasenko86566762009-12-10 21:32:28 +01002227 if (verbose) {
2228 bb_error_msg("according to %s, "
2229 "%s is already mounted on %s",
2230 bb_path_mtab_file,
2231 mp->mnt_fsname, mp->mnt_dir);
2232 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002233 } else {
2234 // ...mount this thing
2235 if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2236 // Count number of failed mounts
2237 rc++;
2238 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002239 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002240 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002241 }
2242 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002243
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002244 // End of fstab/mtab is reached.
2245 // Were we looking for something specific?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002246 if (argv[0]) { // yes
Tanguy Pruvot823694d2012-11-18 13:20:29 +01002247 unsigned long l;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002248
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002249 // If we didn't find anything, complain
2250 if (!mtcur->mnt_fsname)
2251 bb_error_msg_and_die("can't find %s in %s",
2252 argv[0], fstabname);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002253
2254 // What happens when we try to "mount swap_partition"?
2255 // (fstab containts "swap_partition swap swap defaults 0 0")
2256 // util-linux-ng 2.13.1 does this:
2257 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2258 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2259 // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory)
2260 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2261 // exit_group(32) = ?
2262#if 0
2263 // In case we want to simply skip swap partitions:
2264 l = parse_mount_options(mtcur->mnt_opts, NULL);
2265 if ((l & MOUNT_SWAP)
2266 // swap is bogus "fstype", parse_mount_options can't check fstypes
2267 || strcasecmp(mtcur->mnt_type, "swap") == 0
2268 ) {
2269 goto ret;
2270 }
2271#endif
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002272 if (nonroot) {
2273 // fstab must have "users" or "user"
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002274 l = parse_mount_options(mtcur->mnt_opts, NULL);
2275 if (!(l & MOUNT_USERS))
Tanguy Pruvot8aeb3712011-06-30 08:59:26 +02002276 bb_error_msg_and_die("%s", bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002277 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002278
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002279 //util-linux-2.12 does not do this check.
2280 //// If nothing is mounted on this directory...
2281 //// (otherwise repeated "mount FOO" mounts FOO again)
2282 //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2283 //if (mp) {
2284 // bb_error_msg("according to %s, "
2285 // "%s is already mounted on %s",
2286 // bb_path_mtab_file,
2287 // mp->mnt_fsname, mp->mnt_dir);
2288 //} else {
2289 // ...mount the last thing we found
2290 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2291 append_mount_options(&(mtcur->mnt_opts), cmdopts);
2292 resolve_mount_spec(&mtpair->mnt_fsname);
maxwen27116ba2015-08-14 21:41:28 +02002293 if (user_fstype)
2294 mtcur->mnt_type = fstype;
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002295 rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2296 if (ENABLE_FEATURE_CLEAN_UP)
2297 free(mtcur->mnt_opts);
2298 //}
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002299 }
2300
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002301 //ret:
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002302 if (ENABLE_FEATURE_CLEAN_UP)
2303 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002304 if (ENABLE_FEATURE_CLEAN_UP) {
2305 free(storage_path);
2306 free(cmdopts);
2307 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002308
2309//TODO: exitcode should be ORed mask of (from "man mount"):
2310// 0 success
2311// 1 incorrect invocation or permissions
2312// 2 system error (out of memory, cannot fork, no more loop devices)
2313// 4 internal mount bug or missing nfs support in mount
2314// 8 user interrupt
2315//16 problems writing or locking /etc/mtab
2316//32 mount failure
2317//64 some mount succeeded
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002318 return rc;
2319}