blob: 62fd41fd75a1de05c6e92e7eac153295061207e0 [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: )
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +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)"
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +010045//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
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200120#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,
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +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,
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200253 /* "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"
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +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"
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200311 "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;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000344 char getmntent_buf[1];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100345} FIX_ALIASING;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000346enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
Denis Vlasenkof732e962008-02-18 12:07:49 +0000347#define G (*(struct globals*)&bb_common_bufsiz1)
348#define nfs_mount_version (G.nfs_mount_version)
Denis Vlasenkob4133682008-02-18 13:05:38 +0000349#if ENABLE_FEATURE_MOUNT_VERBOSE
Denis Vlasenkof732e962008-02-18 12:07:49 +0000350#define verbose (G.verbose )
Denis Vlasenkob4133682008-02-18 13:05:38 +0000351#else
352#define verbose 0
353#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000354#define fslist (G.fslist )
355#define getmntent_buf (G.getmntent_buf )
Denys Vlasenko16714242011-09-21 01:59:15 +0200356#define INIT_G() do { } while (0)
Denis Vlasenkof732e962008-02-18 12:07:49 +0000357
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100358#if ENABLE_FEATURE_MTAB_SUPPORT
359/*
360 * update_mtab_entry_on_move() is used to update entry in case of mount --move.
361 * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
362 * input mntent and replace it by new one.
363 */
364static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
365{
366 struct mntent *entries, *m;
367 int i, count;
368 FILE *mountTable;
369
370 mountTable = setmntent(bb_path_mtab_file, "r");
371 if (!mountTable) {
372 bb_perror_msg(bb_path_mtab_file);
373 return;
374 }
375
376 entries = NULL;
377 count = 0;
378 while ((m = getmntent(mountTable)) != NULL) {
379 entries = xrealloc_vector(entries, 3, count);
380 entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
381 entries[count].mnt_dir = xstrdup(m->mnt_dir);
382 entries[count].mnt_type = xstrdup(m->mnt_type);
383 entries[count].mnt_opts = xstrdup(m->mnt_opts);
384 entries[count].mnt_freq = m->mnt_freq;
385 entries[count].mnt_passno = m->mnt_passno;
386 count++;
387 }
388 endmntent(mountTable);
389
390 mountTable = setmntent(bb_path_mtab_file, "w");
391 if (mountTable) {
392 for (i = 0; i < count; i++) {
393 if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
394 addmntent(mountTable, &entries[i]);
395 else
396 addmntent(mountTable, mp);
397 }
398 endmntent(mountTable);
399 } else if (errno != EROFS)
400 bb_perror_msg(bb_path_mtab_file);
401
402 if (ENABLE_FEATURE_CLEAN_UP) {
403 for (i = 0; i < count; i++) {
404 free(entries[i].mnt_fsname);
405 free(entries[i].mnt_dir);
406 free(entries[i].mnt_type);
407 free(entries[i].mnt_opts);
408 }
409 free(entries);
410 }
411}
412#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000413
414#if ENABLE_FEATURE_MOUNT_VERBOSE
415static int verbose_mount(const char *source, const char *target,
416 const char *filesystemtype,
417 unsigned long mountflags, const void *data)
418{
419 int rc;
420
421 errno = 0;
422 rc = mount(source, target, filesystemtype, mountflags, data);
423 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000424 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000425 source, target, filesystemtype,
426 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000427 return rc;
428}
429#else
430#define verbose_mount(...) mount(__VA_ARGS__)
431#endif
432
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000433// Append mount options to string
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000434static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000435{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000436 if (*oldopts && **oldopts) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000437 // Do not insert options which are already there
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000438 while (newopts[0]) {
439 char *p;
440 int len = strlen(newopts);
441 p = strchr(newopts, ',');
442 if (p) len = p - newopts;
443 p = *oldopts;
444 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000445 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000446 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000447 goto skip;
448 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000449 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000450 p++;
451 }
452 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
453 free(*oldopts);
454 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000455 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000456 newopts += len;
457 while (newopts[0] == ',') newopts++;
458 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000459 } else {
460 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000461 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000462 }
463}
464
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000465// Use the mount_options list to parse options into flags.
Alexander Shishkin77650952010-10-28 06:10:03 +0200466// Also update list of unrecognized options if unrecognized != NULL
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200467static unsigned long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000468{
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200469 unsigned long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000470
Rob Landley6a6798b2005-08-10 20:35:54 +0000471 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000472 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000473 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000474 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000475 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000476
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000477 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000478
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000479// FIXME: use hasmntopt()
Rob Landley6a6798b2005-08-10 20:35:54 +0000480 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000481 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200482 unsigned opt_len = strlen(option_str);
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100483
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200484 if (strncasecmp(option_str, options, opt_len) == 0
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +0100485 && (options[opt_len] == '\0'
486 /* or is it "comment=" thingy in fstab? */
487 IF_FEATURE_MOUNT_FSTAB(IF_DESKTOP( || option_str[opt_len-1] == '=' ))
488 )
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200489 ) {
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200490 unsigned long fl = mount_options[i];
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200491 if (fl & BB_MS_INVERTED_VALUE)
Alexander Shishkin77650952010-10-28 06:10:03 +0200492 flags &= fl;
493 else
494 flags |= fl;
495 goto found;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000496 }
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200497 option_str += opt_len + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000498 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200499 // We did not recognize this option.
500 // If "unrecognized" is not NULL, append option there.
501 // Note that we should not append *empty* option -
502 // in this case we want to pass NULL, not "", to "data"
503 // parameter of mount(2) syscall.
504 // This is crucial for filesystems that don't accept
505 // any arbitrary mount options, like cgroup fs:
506 // "mount -t cgroup none /mnt"
507 if (options[0] && unrecognized) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000508 // Add it to strflags, to pass on to kernel
Alexander Shishkin77650952010-10-28 06:10:03 +0200509 char *p = *unrecognized;
510 unsigned len = p ? strlen(p) : 0;
511 *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000512
Rob Landley6a6798b2005-08-10 20:35:54 +0000513 // Comma separated if it's not the first one
Alexander Shishkin77650952010-10-28 06:10:03 +0200514 if (len) p[len++] = ',';
515 strcpy(p + len, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000516 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200517 found:
Denis Vlasenko2535f122007-09-15 13:28:30 +0000518 if (!comma)
519 break;
520 // Advance to next option
521 *comma = ',';
522 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000523 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000524
Rob Landleydc0955b2006-03-14 18:16:25 +0000525 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000526}
527
Rob Landleydc0955b2006-03-14 18:16:25 +0000528// Return a list of all block device backed filesystems
Rob Landleydc0955b2006-03-14 18:16:25 +0000529static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000530{
Denis Vlasenko87468852007-04-13 23:22:00 +0000531 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000532 "/etc/filesystems",
533 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000534 };
535 char *fs, *buf;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200536 llist_t *list = NULL;
Rob Landleydc0955b2006-03-14 18:16:25 +0000537 int i;
538 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000539
Denis Vlasenko87468852007-04-13 23:22:00 +0000540 for (i = 0; i < 2; i++) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000541 f = fopen_for_read(filesystems[i]);
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000542 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000543
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000544 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200545 if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100546 goto next;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000547 fs = skip_whitespace(buf);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200548 if (*fs == '#' || *fs == '*' || !*fs)
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100549 goto next;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000550
Denis Vlasenko372686b2006-10-12 22:42:33 +0000551 llist_add_to_end(&list, xstrdup(fs));
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100552 next:
Denis Vlasenko372686b2006-10-12 22:42:33 +0000553 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000554 }
555 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
556 }
557
558 return list;
559}
560
Rob Landleydc0955b2006-03-14 18:16:25 +0000561#if ENABLE_FEATURE_CLEAN_UP
562static void delete_block_backed_filesystems(void)
563{
Rob Landleya6b5b602006-05-08 19:03:07 +0000564 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000565}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000566#else
567void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000568#endif
569
Rob Landleydc0955b2006-03-14 18:16:25 +0000570// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000571// NB: mp->xxx fields may be trashed on exit
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200572static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000573{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000574 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000575
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200576 if (FAKE_IT) {
Denis Vlasenkob4133682008-02-18 13:05:38 +0000577 if (verbose >= 2)
578 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
579 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
580 vfsflags, filteropts);
581 goto mtab;
582 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000583
Rob Landleydc0955b2006-03-14 18:16:25 +0000584 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000585 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000586 errno = 0;
587 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000588 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000589
590 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000591 // helper program mount.<mnt_type>
Denys Vlasenkoba986032009-09-15 23:00:09 +0200592 if (HELPERS_ALLOWED && rc && mp->mnt_type) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200593 char *args[8];
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000594 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000595 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000596 rc = 1;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200597 if (FAKE_IT)
598 args[rc++] = (char *)"-f";
599 if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
600 args[rc++] = (char *)"-n";
Denis Vlasenko5c329932009-04-12 12:16:21 +0000601 args[rc++] = mp->mnt_fsname;
602 args[rc++] = mp->mnt_dir;
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000603 if (filteropts) {
604 args[rc++] = (char *)"-o";
605 args[rc++] = filteropts;
606 }
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000607 args[rc] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +0100608 rc = spawn_and_wait(args);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000609 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000610 if (!rc)
611 break;
612 errno = errno_save;
613 }
614
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000615 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000616 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000617 if (!(vfsflags & MS_SILENT))
618 bb_error_msg("%s is write-protected, mounting read-only",
619 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000620 vfsflags |= MS_RDONLY;
621 }
622
Rob Landleydc0955b2006-03-14 18:16:25 +0000623 // Abort entirely if permission denied.
624
625 if (rc && errno == EPERM)
626 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
627
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000628 // If the mount was successful, and we're maintaining an old-style
629 // mtab file by hand, add the new entry to it now.
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000630 mtab:
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200631 if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000632 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000633 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000634 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000635 int i;
636
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000637 if (!mountTable) {
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100638 bb_perror_msg(bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000639 goto ret;
640 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000641
642 // Add vfs string flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000643 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
644 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
645 append_mount_options(&(mp->mnt_opts), option_str);
646 option_str += strlen(option_str) + 1;
647 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000648
649 // Remove trailing / (if any) from directory we mounted on
Denis Vlasenko727ef942006-09-14 13:19:19 +0000650 i = strlen(mp->mnt_dir) - 1;
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100651 while (i > 0 && mp->mnt_dir[i] == '/')
Denys Vlasenkoc6450c92011-02-28 11:09:49 +0100652 mp->mnt_dir[i--] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000653
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000654 // Convert to canonical pathnames as needed
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000655 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100656 fsname = NULL;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000657 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000658 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000659 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000660 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000661 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000662
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100663 // Write and close
664#if ENABLE_FEATURE_MTAB_SUPPORT
665 if (vfsflags & MS_MOVE)
666 update_mtab_entry_on_move(mp);
667 else
668#endif
669 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000670 endmntent(mountTable);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100671
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000672 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000673 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000674 free(fsname);
675 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000676 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000677 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000678 return rc;
679}
680
Denis Vlasenko25098f72006-09-14 15:46:33 +0000681#if ENABLE_FEATURE_MOUNT_NFS
682
683/*
684 * Linux NFS mount
685 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
686 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +0200687 * Licensed under GPLv2, see file LICENSE in this source tree.
Denis Vlasenko25098f72006-09-14 15:46:33 +0000688 *
689 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
690 * numbers to be specified on the command line.
691 *
692 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
693 * Omit the call to connect() for Linux version 1.3.11 or later.
694 *
695 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
696 * Implemented the "bg", "fg" and "retry" mount options for NFS.
697 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000698 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000699 * - added Native Language Support
700 *
701 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
702 * plus NFSv3 stuff.
703 */
704
Denis Vlasenko25098f72006-09-14 15:46:33 +0000705#define MOUNTPORT 635
706#define MNTPATHLEN 1024
707#define MNTNAMLEN 255
708#define FHSIZE 32
709#define FHSIZE3 64
710
711typedef char fhandle[FHSIZE];
712
713typedef struct {
714 unsigned int fhandle3_len;
715 char *fhandle3_val;
716} fhandle3;
717
718enum mountstat3 {
719 MNT_OK = 0,
720 MNT3ERR_PERM = 1,
721 MNT3ERR_NOENT = 2,
722 MNT3ERR_IO = 5,
723 MNT3ERR_ACCES = 13,
724 MNT3ERR_NOTDIR = 20,
725 MNT3ERR_INVAL = 22,
726 MNT3ERR_NAMETOOLONG = 63,
727 MNT3ERR_NOTSUPP = 10004,
728 MNT3ERR_SERVERFAULT = 10006,
729};
730typedef enum mountstat3 mountstat3;
731
732struct fhstatus {
733 unsigned int fhs_status;
734 union {
735 fhandle fhs_fhandle;
736 } fhstatus_u;
737};
738typedef struct fhstatus fhstatus;
739
740struct mountres3_ok {
741 fhandle3 fhandle;
742 struct {
743 unsigned int auth_flavours_len;
744 char *auth_flavours_val;
745 } auth_flavours;
746};
747typedef struct mountres3_ok mountres3_ok;
748
749struct mountres3 {
750 mountstat3 fhs_status;
751 union {
752 mountres3_ok mountinfo;
753 } mountres3_u;
754};
755typedef struct mountres3 mountres3;
756
757typedef char *dirpath;
758
759typedef char *name;
760
761typedef struct mountbody *mountlist;
762
763struct mountbody {
764 name ml_hostname;
765 dirpath ml_directory;
766 mountlist ml_next;
767};
768typedef struct mountbody mountbody;
769
770typedef struct groupnode *groups;
771
772struct groupnode {
773 name gr_name;
774 groups gr_next;
775};
776typedef struct groupnode groupnode;
777
778typedef struct exportnode *exports;
779
780struct exportnode {
781 dirpath ex_dir;
782 groups ex_groups;
783 exports ex_next;
784};
785typedef struct exportnode exportnode;
786
787struct ppathcnf {
788 int pc_link_max;
789 short pc_max_canon;
790 short pc_max_input;
791 short pc_name_max;
792 short pc_path_max;
793 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000794 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000795 char pc_xxx;
796 short pc_mask[2];
797};
798typedef struct ppathcnf ppathcnf;
799
800#define MOUNTPROG 100005
801#define MOUNTVERS 1
802
803#define MOUNTPROC_NULL 0
804#define MOUNTPROC_MNT 1
805#define MOUNTPROC_DUMP 2
806#define MOUNTPROC_UMNT 3
807#define MOUNTPROC_UMNTALL 4
808#define MOUNTPROC_EXPORT 5
809#define MOUNTPROC_EXPORTALL 6
810
811#define MOUNTVERS_POSIX 2
812
813#define MOUNTPROC_PATHCONF 7
814
815#define MOUNT_V3 3
816
817#define MOUNTPROC3_NULL 0
818#define MOUNTPROC3_MNT 1
819#define MOUNTPROC3_DUMP 2
820#define MOUNTPROC3_UMNT 3
821#define MOUNTPROC3_UMNTALL 4
822#define MOUNTPROC3_EXPORT 5
823
824enum {
825#ifndef NFS_FHSIZE
826 NFS_FHSIZE = 32,
827#endif
828#ifndef NFS_PORT
829 NFS_PORT = 2049
830#endif
831};
832
Denis Vlasenko25098f72006-09-14 15:46:33 +0000833/*
834 * We want to be able to compile mount on old kernels in such a way
835 * that the binary will work well on more recent kernels.
836 * Thus, if necessary we teach nfsmount.c the structure of new fields
837 * that will come later.
838 *
839 * Moreover, the new kernel includes conflict with glibc includes
840 * so it is easiest to ignore the kernel altogether (at compile time).
841 */
842
843struct nfs2_fh {
Dave Lovefae473c2011-11-10 15:19:25 +0100844 char data[32];
Denis Vlasenko25098f72006-09-14 15:46:33 +0000845};
846struct nfs3_fh {
Dave Lovefae473c2011-11-10 15:19:25 +0100847 unsigned short size;
848 unsigned char data[64];
Denis Vlasenko25098f72006-09-14 15:46:33 +0000849};
850
851struct nfs_mount_data {
Dave Lovefae473c2011-11-10 15:19:25 +0100852 int version; /* 1 */
853 int fd; /* 1 */
854 struct nfs2_fh old_root; /* 1 */
855 int flags; /* 1 */
856 int rsize; /* 1 */
857 int wsize; /* 1 */
858 int timeo; /* 1 */
859 int retrans; /* 1 */
860 int acregmin; /* 1 */
861 int acregmax; /* 1 */
862 int acdirmin; /* 1 */
863 int acdirmax; /* 1 */
864 struct sockaddr_in addr; /* 1 */
865 char hostname[256]; /* 1 */
866 int namlen; /* 2 */
867 unsigned int bsize; /* 3 */
868 struct nfs3_fh root; /* 4 */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000869};
870
871/* bits in the flags field */
872enum {
873 NFS_MOUNT_SOFT = 0x0001, /* 1 */
874 NFS_MOUNT_INTR = 0x0002, /* 1 */
875 NFS_MOUNT_SECURE = 0x0004, /* 1 */
876 NFS_MOUNT_POSIX = 0x0008, /* 1 */
877 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
878 NFS_MOUNT_NOAC = 0x0020, /* 1 */
879 NFS_MOUNT_TCP = 0x0040, /* 2 */
880 NFS_MOUNT_VER3 = 0x0080, /* 3 */
881 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000882 NFS_MOUNT_NONLM = 0x0200, /* 3 */
Dave Lovefae473c2011-11-10 15:19:25 +0100883 NFS_MOUNT_NOACL = 0x0800, /* 4 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000884 NFS_MOUNT_NORDIRPLUS = 0x4000
Denis Vlasenko25098f72006-09-14 15:46:33 +0000885};
886
887
888/*
889 * We need to translate between nfs status return values and
890 * the local errno values which may not be the same.
891 *
892 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
893 * "after #include <errno.h> the symbol errno is reserved for any use,
894 * it cannot even be used as a struct tag or field name".
895 */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000896#ifndef EDQUOT
Denys Vlasenkocc428142009-12-16 02:06:56 +0100897# define EDQUOT ENOSPC
Denis Vlasenko25098f72006-09-14 15:46:33 +0000898#endif
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000899/* Convert each NFSERR_BLAH into EBLAH */
Denys Vlasenkocc428142009-12-16 02:06:56 +0100900static const uint8_t nfs_err_stat[] = {
901 1, 2, 5, 6, 13, 17,
902 19, 20, 21, 22, 27, 28,
903 30, 63, 66, 69, 70, 71
904};
Denys Vlasenkodc1fd2e2010-05-19 17:01:29 +0200905#if ( \
906 EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \
907 ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \
908 EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
909typedef uint8_t nfs_err_type;
910#else
911typedef uint16_t nfs_err_type;
912#endif
913static const nfs_err_type nfs_err_errnum[] = {
Denys Vlasenkocc428142009-12-16 02:06:56 +0100914 EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST,
915 ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC,
916 EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
Denis Vlasenko25098f72006-09-14 15:46:33 +0000917};
Denis Vlasenko25098f72006-09-14 15:46:33 +0000918static char *nfs_strerror(int status)
919{
920 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000921
Denys Vlasenkocc428142009-12-16 02:06:56 +0100922 for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
923 if (nfs_err_stat[i] == status)
924 return strerror(nfs_err_errnum[i]);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000925 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000926 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000927}
928
929static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
930{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200931 return xdr_opaque(xdrs, objp, FHSIZE);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000932}
933
934static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
935{
936 if (!xdr_u_int(xdrs, &objp->fhs_status))
Denys Vlasenko69675782013-01-14 01:34:48 +0100937 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200938 if (objp->fhs_status == 0)
939 return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000940 return TRUE;
941}
942
943static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
944{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200945 return xdr_string(xdrs, objp, MNTPATHLEN);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000946}
947
948static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
949{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200950 return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
Denys Vlasenko69675782013-01-14 01:34:48 +0100951 (unsigned int *) &objp->fhandle3_len,
952 FHSIZE3);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000953}
954
955static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
956{
957 if (!xdr_fhandle3(xdrs, &objp->fhandle))
958 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200959 return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
Denys Vlasenko69675782013-01-14 01:34:48 +0100960 &(objp->auth_flavours.auth_flavours_len),
961 ~0,
962 sizeof(int),
963 (xdrproc_t) xdr_int);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000964}
965
966static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
967{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200968 return xdr_enum(xdrs, (enum_t *) objp);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000969}
970
971static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
972{
973 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
974 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200975 if (objp->fhs_status == MNT_OK)
976 return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000977 return TRUE;
978}
979
980#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
981
Denis Vlasenko25098f72006-09-14 15:46:33 +0000982/*
983 * Unfortunately, the kernel prints annoying console messages
984 * in case of an unexpected nfs mount version (instead of
985 * just returning some error). Therefore we'll have to try
986 * and figure out what version the kernel expects.
987 *
988 * Variables:
989 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
990 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
991 * nfs_mount_version: version this source and running kernel can handle
992 */
993static void
994find_kernel_nfs_mount_version(void)
995{
Denis Vlasenkob9256052007-09-28 10:29:17 +0000996 int kernel_version;
997
998 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000999 return;
1000
1001 nfs_mount_version = 4; /* default */
1002
1003 kernel_version = get_linux_version_code();
1004 if (kernel_version) {
Denys Vlasenkocc428142009-12-16 02:06:56 +01001005 if (kernel_version < KERNEL_VERSION(2,2,18))
Denis Vlasenko25098f72006-09-14 15:46:33 +00001006 nfs_mount_version = 3;
1007 /* else v4 since 2.3.99pre4 */
1008 }
1009}
1010
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +00001011static void
Denis Vlasenkob9256052007-09-28 10:29:17 +00001012get_mountport(struct pmap *pm_mnt,
1013 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001014 long unsigned prog,
1015 long unsigned version,
1016 long unsigned proto,
1017 long unsigned port)
1018{
1019 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001020
1021 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001022/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
1023 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001024 pmap = pmap_getmaps(server_addr);
1025
1026 if (version > MAX_NFSPROT)
1027 version = MAX_NFSPROT;
1028 if (!prog)
1029 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001030 pm_mnt->pm_prog = prog;
1031 pm_mnt->pm_vers = version;
1032 pm_mnt->pm_prot = proto;
1033 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001034
Denis Vlasenko25098f72006-09-14 15:46:33 +00001035 while (pmap) {
1036 if (pmap->pml_map.pm_prog != prog)
1037 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001038 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001039 goto next;
1040 if (version > 2 && pmap->pml_map.pm_vers != version)
1041 goto next;
1042 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1043 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001044 if (pmap->pml_map.pm_vers > MAX_NFSPROT
1045 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1046 || (port && pmap->pml_map.pm_port != port)
1047 ) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001048 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001049 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001050 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1051 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001052 pmap = pmap->pml_next;
1053 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001054 if (!pm_mnt->pm_vers)
1055 pm_mnt->pm_vers = MOUNTVERS;
1056 if (!pm_mnt->pm_port)
1057 pm_mnt->pm_port = MOUNTPORT;
1058 if (!pm_mnt->pm_prot)
1059 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001060}
1061
Denis Vlasenkof0000652007-09-04 18:30:26 +00001062#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001063static int daemonize(void)
1064{
Denis Vlasenko25098f72006-09-14 15:46:33 +00001065 int pid = fork();
1066 if (pid < 0) /* error */
1067 return -errno;
1068 if (pid > 0) /* parent */
1069 return 0;
1070 /* child */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001071 close(0);
1072 xopen(bb_dev_null, O_RDWR);
1073 xdup2(0, 1);
1074 xdup2(0, 2);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001075 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +00001076 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001077 logmode = LOGMODE_SYSLOG;
1078 return 1;
1079}
Denis Vlasenkof0000652007-09-04 18:30:26 +00001080#else
1081static inline int daemonize(void) { return -ENOSYS; }
1082#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001083
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001084/* TODO */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001085static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001086{
1087 return 0;
1088}
1089
1090/* RPC strerror analogs are terminally idiotic:
1091 * *mandatory* prefix and \n at end.
1092 * This hopefully helps. Usage:
1093 * error_msg_rpc(clnt_*error*(" ")) */
1094static void error_msg_rpc(const char *msg)
1095{
Denis Vlasenko23514fe2006-09-19 14:07:52 +00001096 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001097 while (msg[0] == ' ' || msg[0] == ':') msg++;
1098 len = strlen(msg);
1099 while (len && msg[len-1] == '\n') len--;
1100 bb_error_msg("%.*s", len, msg);
1101}
1102
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001103/* NB: mp->xxx fields may be trashed on exit */
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001104static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001105{
1106 CLIENT *mclient;
1107 char *hostname;
1108 char *pathname;
1109 char *mounthost;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001110 /* prior to 2.6.23, kernel took NFS options in a form of this struct
1111 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1112 * then data pointer is interpreted as a string. */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001113 struct nfs_mount_data data;
1114 char *opt;
1115 struct hostent *hp;
1116 struct sockaddr_in server_addr;
1117 struct sockaddr_in mount_server_addr;
1118 int msock, fsock;
1119 union {
1120 struct fhstatus nfsv2;
1121 struct mountres3 nfsv3;
1122 } status;
1123 int daemonized;
1124 char *s;
1125 int port;
1126 int mountport;
1127 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001128#if BB_MMU
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001129 smallint bg = 0;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001130#else
1131 enum { bg = 0 };
1132#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001133 int retry;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001134 int mountprog;
1135 int mountvers;
1136 int nfsprog;
1137 int nfsvers;
1138 int retval;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001139 /* these all are one-bit really. gcc 4.3.1 likes this combination: */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001140 smallint tcp;
1141 smallint soft;
1142 int intr;
1143 int posix;
1144 int nocto;
1145 int noac;
1146 int nordirplus;
1147 int nolock;
Dave Lovefae473c2011-11-10 15:19:25 +01001148 int noacl;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001149
1150 find_kernel_nfs_mount_version();
1151
1152 daemonized = 0;
1153 mounthost = NULL;
1154 retval = ETIMEDOUT;
1155 msock = fsock = -1;
1156 mclient = NULL;
1157
1158 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1159
1160 filteropts = xstrdup(filteropts); /* going to trash it later... */
1161
1162 hostname = xstrdup(mp->mnt_fsname);
1163 /* mount_main() guarantees that ':' is there */
1164 s = strchr(hostname, ':');
1165 pathname = s + 1;
1166 *s = '\0';
1167 /* Ignore all but first hostname in replicated mounts
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001168 * until they can be fully supported. (mack@sgi.com) */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001169 s = strchr(hostname, ',');
1170 if (s) {
1171 *s = '\0';
1172 bb_error_msg("warning: multiple hostnames not supported");
1173 }
1174
1175 server_addr.sin_family = AF_INET;
1176 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1177 hp = gethostbyname(hostname);
1178 if (hp == NULL) {
1179 bb_herror_msg("%s", hostname);
1180 goto fail;
1181 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001182 if (hp->h_length != (int)sizeof(struct in_addr)) {
1183 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001184 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001185 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001186 }
1187
1188 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1189
1190 /* add IP address to mtab options for use when unmounting */
1191
1192 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1193 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1194 } else {
1195 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1196 mp->mnt_opts[0] ? "," : "",
1197 inet_ntoa(server_addr.sin_addr));
1198 free(mp->mnt_opts);
1199 mp->mnt_opts = tmp;
1200 }
1201
1202 /* Set default options.
1203 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1204 * let the kernel decide.
1205 * timeo is filled in after we know whether it'll be TCP or UDP. */
1206 memset(&data, 0, sizeof(data));
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001207 data.retrans = 3;
1208 data.acregmin = 3;
1209 data.acregmax = 60;
1210 data.acdirmin = 30;
1211 data.acdirmax = 60;
1212 data.namlen = NAME_MAX;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001213
Denis Vlasenko25098f72006-09-14 15:46:33 +00001214 soft = 0;
1215 intr = 0;
1216 posix = 0;
1217 nocto = 0;
1218 nolock = 0;
1219 noac = 0;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001220 nordirplus = 0;
Dave Lovefae473c2011-11-10 15:19:25 +01001221 noacl = 0;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001222 retry = 10000; /* 10000 minutes ~ 1 week */
Bernhard Reutner-Fischer88206292011-05-04 19:03:30 +02001223 tcp = 1; /* nfs-utils uses tcp per default */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001224
1225 mountprog = MOUNTPROG;
1226 mountvers = 0;
1227 port = 0;
1228 mountport = 0;
1229 nfsprog = 100003;
1230 nfsvers = 0;
1231
1232 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001233 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001234 char *opteq = strchr(opt, '=');
1235 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001236 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001237 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001238 /* 0 */ "rsize\0"
1239 /* 1 */ "wsize\0"
1240 /* 2 */ "timeo\0"
1241 /* 3 */ "retrans\0"
1242 /* 4 */ "acregmin\0"
1243 /* 5 */ "acregmax\0"
1244 /* 6 */ "acdirmin\0"
1245 /* 7 */ "acdirmax\0"
1246 /* 8 */ "actimeo\0"
1247 /* 9 */ "retry\0"
1248 /* 10 */ "port\0"
1249 /* 11 */ "mountport\0"
1250 /* 12 */ "mounthost\0"
1251 /* 13 */ "mountprog\0"
1252 /* 14 */ "mountvers\0"
1253 /* 15 */ "nfsprog\0"
1254 /* 16 */ "nfsvers\0"
1255 /* 17 */ "vers\0"
1256 /* 18 */ "proto\0"
1257 /* 19 */ "namlen\0"
1258 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001259
1260 *opteq++ = '\0';
1261 idx = index_in_strings(options, opt);
1262 switch (idx) {
1263 case 12: // "mounthost"
1264 mounthost = xstrndup(opteq,
1265 strcspn(opteq, " \t\n\r,"));
1266 continue;
1267 case 18: // "proto"
1268 if (!strncmp(opteq, "tcp", 3))
1269 tcp = 1;
1270 else if (!strncmp(opteq, "udp", 3))
1271 tcp = 0;
1272 else
1273 bb_error_msg("warning: unrecognized proto= option");
1274 continue;
1275 case 20: // "addr" - ignore
1276 continue;
Peter Korsgaard301fe502011-02-21 17:52:13 +01001277 case -1: // unknown
1278 if (vfsflags & MS_REMOUNT)
1279 continue;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001280 }
1281
Denys Vlasenko77832482010-08-12 14:14:45 +02001282 val = xatoi_positive(opteq);
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001283 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001284 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001285 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001286 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001287 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001288 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001289 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001290 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001291 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001292 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001293 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001294 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001295 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001296 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001297 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001298 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001299 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001300 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001301 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001302 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001303 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001304 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001305 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001306 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001307 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001308 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001309 data.acregmin = val;
1310 data.acregmax = val;
1311 data.acdirmin = val;
1312 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001313 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001314 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001315 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001316 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001317 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001318 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001319 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001320 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001321 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001322 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001323 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001324 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001325 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001326 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001327 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001328 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001329 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001330 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001331 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001332 case 16: // "nfsvers"
1333 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001334 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001335 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001336 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001337 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001338 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001339 //else
1340 // bb_error_msg("warning: option namlen is not supported\n");
1341 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001342 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001343 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1344 goto fail;
1345 }
1346 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001347 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001348 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001349 "bg\0"
1350 "fg\0"
1351 "soft\0"
1352 "hard\0"
1353 "intr\0"
1354 "posix\0"
1355 "cto\0"
1356 "ac\0"
1357 "tcp\0"
1358 "udp\0"
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001359 "lock\0"
Dave Lovefae473c2011-11-10 15:19:25 +01001360 "rdirplus\0"
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001361 "acl\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001362 int val = 1;
1363 if (!strncmp(opt, "no", 2)) {
1364 val = 0;
1365 opt += 2;
1366 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001367 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001368 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001369#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001370 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001371#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001372 break;
1373 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001374#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001375 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001376#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001377 break;
1378 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001379 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001380 break;
1381 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001382 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001383 break;
1384 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001385 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001386 break;
1387 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001388 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001389 break;
1390 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001391 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001392 break;
1393 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001394 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001395 break;
1396 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001397 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001398 break;
1399 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001400 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001401 break;
1402 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001403 if (nfs_mount_version >= 3)
1404 nolock = !val;
1405 else
1406 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001407 break;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001408 case 11: //rdirplus
1409 nordirplus = !val;
1410 break;
Dave Lovefae473c2011-11-10 15:19:25 +01001411 case 12: // acl
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001412 noacl = !val;
Dave Lovefae473c2011-11-10 15:19:25 +01001413 break;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001414 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001415 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1416 goto fail;
1417 }
1418 }
1419 }
1420 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1421
1422 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1423 | (intr ? NFS_MOUNT_INTR : 0)
1424 | (posix ? NFS_MOUNT_POSIX : 0)
1425 | (nocto ? NFS_MOUNT_NOCTO : 0)
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001426 | (noac ? NFS_MOUNT_NOAC : 0)
Dave Lovefae473c2011-11-10 15:19:25 +01001427 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001428 | (noacl ? NFS_MOUNT_NOACL : 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001429 if (nfs_mount_version >= 2)
1430 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1431 if (nfs_mount_version >= 3)
1432 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1433 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1434 bb_error_msg("NFSv%d not supported", nfsvers);
1435 goto fail;
1436 }
1437 if (nfsvers && !mountvers)
1438 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1439 if (nfsvers && nfsvers < mountvers) {
1440 mountvers = nfsvers;
1441 }
1442
1443 /* Adjust options if none specified */
1444 if (!data.timeo)
1445 data.timeo = tcp ? 70 : 7;
1446
Denis Vlasenko25098f72006-09-14 15:46:33 +00001447 data.version = nfs_mount_version;
1448
1449 if (vfsflags & MS_REMOUNT)
1450 goto do_mount;
1451
1452 /*
1453 * If the previous mount operation on the same host was
1454 * backgrounded, and the "bg" for this mount is also set,
1455 * give up immediately, to avoid the initial timeout.
1456 */
1457 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001458 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001459 if (daemonized <= 0) { /* parent or error */
1460 retval = -daemonized;
1461 goto ret;
1462 }
1463 }
1464
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001465 /* Create mount daemon client */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001466 /* See if the nfs host = mount host. */
1467 if (mounthost) {
1468 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1469 mount_server_addr.sin_family = AF_INET;
1470 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1471 } else {
1472 hp = gethostbyname(mounthost);
1473 if (hp == NULL) {
1474 bb_herror_msg("%s", mounthost);
1475 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001476 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001477 if (hp->h_length != (int)sizeof(struct in_addr)) {
1478 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001479 }
1480 mount_server_addr.sin_family = AF_INET;
Denys Vlasenko99069332010-02-27 19:38:19 +01001481 memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001482 }
1483 }
1484
1485 /*
1486 * The following loop implements the mount retries. When the mount
1487 * times out, and the "bg" option is set, we background ourself
1488 * and continue trying.
1489 *
1490 * The case where the mount point is not present and the "bg"
1491 * option is set, is treated as a timeout. This is done to
1492 * support nested mounts.
1493 *
1494 * The "retry" count specified by the user is the number of
1495 * minutes to retry before giving up.
1496 */
1497 {
1498 struct timeval total_timeout;
1499 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001500 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001501 time_t t;
1502 time_t prevt;
1503 time_t timeout;
1504
1505 retry_timeout.tv_sec = 3;
1506 retry_timeout.tv_usec = 0;
1507 total_timeout.tv_sec = 20;
1508 total_timeout.tv_usec = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001509/* FIXME: use monotonic()? */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001510 timeout = time(NULL) + 60 * retry;
1511 prevt = 0;
1512 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001513 retry:
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001514 /* Be careful not to use too many CPU cycles */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001515 if (t - prevt < 30)
1516 sleep(30);
1517
Denis Vlasenkob9256052007-09-28 10:29:17 +00001518 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001519 mountprog,
1520 mountvers,
1521 proto,
1522 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001523 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001524
1525 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001526 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001527 msock = RPC_ANYSOCK;
1528
Denis Vlasenkob9256052007-09-28 10:29:17 +00001529 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001530 case IPPROTO_UDP:
1531 mclient = clntudp_create(&mount_server_addr,
Denys Vlasenko69675782013-01-14 01:34:48 +01001532 pm_mnt.pm_prog,
1533 pm_mnt.pm_vers,
1534 retry_timeout,
1535 &msock);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001536 if (mclient)
1537 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001538 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001539 msock = RPC_ANYSOCK;
1540 case IPPROTO_TCP:
1541 mclient = clnttcp_create(&mount_server_addr,
Denys Vlasenko69675782013-01-14 01:34:48 +01001542 pm_mnt.pm_prog,
1543 pm_mnt.pm_vers,
1544 &msock, 0, 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001545 break;
1546 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001547 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001548 }
1549 if (!mclient) {
1550 if (!daemonized && prevt == 0)
1551 error_msg_rpc(clnt_spcreateerror(" "));
1552 } else {
1553 enum clnt_stat clnt_stat;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001554
1555 /* Try to mount hostname:pathname */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001556 mclient->cl_auth = authunix_create_default();
1557
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001558 /* Make pointers in xdr_mountres3 NULL so
Denis Vlasenko25098f72006-09-14 15:46:33 +00001559 * that xdr_array allocates memory for us
1560 */
1561 memset(&status, 0, sizeof(status));
1562
Denis Vlasenkob9256052007-09-28 10:29:17 +00001563 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001564 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
Denys Vlasenko69675782013-01-14 01:34:48 +01001565 (xdrproc_t) xdr_dirpath,
1566 (caddr_t) &pathname,
1567 (xdrproc_t) xdr_mountres3,
1568 (caddr_t) &status,
1569 total_timeout);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001570 else
1571 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
Denys Vlasenko69675782013-01-14 01:34:48 +01001572 (xdrproc_t) xdr_dirpath,
1573 (caddr_t) &pathname,
1574 (xdrproc_t) xdr_fhstatus,
1575 (caddr_t) &status,
1576 total_timeout);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001577
1578 if (clnt_stat == RPC_SUCCESS)
1579 goto prepare_kernel_data; /* we're done */
1580 if (errno != ECONNREFUSED) {
1581 error_msg_rpc(clnt_sperror(mclient, " "));
1582 goto fail; /* don't retry */
1583 }
1584 /* Connection refused */
1585 if (!daemonized && prevt == 0) /* print just once */
1586 error_msg_rpc(clnt_sperror(mclient, " "));
1587 auth_destroy(mclient->cl_auth);
1588 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001589 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001590 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001591 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001592 }
1593
1594 /* Timeout. We are going to retry... maybe */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001595 if (!bg)
1596 goto fail;
1597 if (!daemonized) {
1598 daemonized = daemonize();
1599 if (daemonized <= 0) { /* parent or error */
1600 retval = -daemonized;
1601 goto ret;
1602 }
1603 }
1604 prevt = t;
1605 t = time(NULL);
1606 if (t >= timeout)
1607 /* TODO error message */
1608 goto fail;
1609
1610 goto retry;
1611 }
1612
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001613 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001614
1615 if (nfsvers == 2) {
1616 if (status.nfsv2.fhs_status != 0) {
1617 bb_error_msg("%s:%s failed, reason given by server: %s",
1618 hostname, pathname,
1619 nfs_strerror(status.nfsv2.fhs_status));
1620 goto fail;
1621 }
1622 memcpy(data.root.data,
1623 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1624 NFS_FHSIZE);
1625 data.root.size = NFS_FHSIZE;
1626 memcpy(data.old_root.data,
1627 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1628 NFS_FHSIZE);
1629 } else {
1630 fhandle3 *my_fhandle;
1631 if (status.nfsv3.fhs_status != 0) {
1632 bb_error_msg("%s:%s failed, reason given by server: %s",
1633 hostname, pathname,
1634 nfs_strerror(status.nfsv3.fhs_status));
1635 goto fail;
1636 }
1637 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1638 memset(data.old_root.data, 0, NFS_FHSIZE);
1639 memset(&data.root, 0, sizeof(data.root));
1640 data.root.size = my_fhandle->fhandle3_len;
1641 memcpy(data.root.data,
1642 (char *) my_fhandle->fhandle3_val,
1643 my_fhandle->fhandle3_len);
1644
1645 data.flags |= NFS_MOUNT_VER3;
1646 }
1647
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001648 /* Create nfs socket for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001649 if (tcp) {
1650 if (nfs_mount_version < 3) {
1651 bb_error_msg("NFS over TCP is not supported");
1652 goto fail;
1653 }
1654 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1655 } else
1656 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1657 if (fsock < 0) {
1658 bb_perror_msg("nfs socket");
1659 goto fail;
1660 }
1661 if (bindresvport(fsock, 0) < 0) {
1662 bb_perror_msg("nfs bindresvport");
1663 goto fail;
1664 }
1665 if (port == 0) {
1666 server_addr.sin_port = PMAPPORT;
1667 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1668 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1669 if (port == 0)
1670 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001671 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001672 server_addr.sin_port = htons(port);
1673
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001674 /* Prepare data structure for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001675 data.fd = fsock;
1676 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1677 strncpy(data.hostname, hostname, sizeof(data.hostname));
1678
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001679 /* Clean up */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001680 auth_destroy(mclient->cl_auth);
1681 clnt_destroy(mclient);
1682 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001683 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001684
1685 if (bg) {
1686 /* We must wait until mount directory is available */
1687 struct stat statbuf;
1688 int delay = 1;
1689 while (stat(mp->mnt_dir, &statbuf) == -1) {
1690 if (!daemonized) {
1691 daemonized = daemonize();
1692 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001693/* FIXME: parent doesn't close fsock - ??! */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001694 retval = -daemonized;
1695 goto ret;
1696 }
1697 }
1698 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1699 delay *= 2;
1700 if (delay > 30)
1701 delay = 30;
1702 }
1703 }
1704
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001705 /* Perform actual mount */
1706 do_mount:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001707 retval = mount_it_now(mp, vfsflags, (char*)&data);
1708 goto ret;
1709
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001710 /* Abort */
1711 fail:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001712 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001713 if (mclient) {
1714 auth_destroy(mclient->cl_auth);
1715 clnt_destroy(mclient);
1716 }
1717 close(msock);
1718 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001719 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001720 close(fsock);
1721
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001722 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001723 free(hostname);
1724 free(mounthost);
1725 free(filteropts);
1726 return retval;
1727}
1728
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001729#else // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001730
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001731/* Linux 2.6.23+ supports nfs mounts with options passed as a string.
1732 * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
1733 * (However, note that then you lose any chances that NFS over IPv6 would work).
1734 */
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001735static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001736{
1737 len_and_sockaddr *lsa;
1738 char *opts;
1739 char *end;
1740 char *dotted;
1741 int ret;
1742
1743# if ENABLE_FEATURE_IPV6
1744 end = strchr(mp->mnt_fsname, ']');
1745 if (end && end[1] == ':')
1746 end++;
1747 else
1748# endif
1749 /* mount_main() guarantees that ':' is there */
1750 end = strchr(mp->mnt_fsname, ':');
1751
1752 *end = '\0';
Denys Vlasenko39b23312011-11-10 17:01:39 +01001753 lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001754 *end = ':';
1755 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1756 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1757 opts = xasprintf("%s%saddr=%s",
1758 filteropts ? filteropts : "",
1759 filteropts ? "," : "",
1760 dotted
1761 );
1762 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1763 ret = mount_it_now(mp, vfsflags, opts);
1764 if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1765
1766 return ret;
1767}
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001768
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001769#endif // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001770
1771// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1772// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001773// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001774static int singlemount(struct mntent *mp, int ignore_busy)
1775{
Denis Vlasenkob4133682008-02-18 13:05:38 +00001776 int rc = -1;
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001777 unsigned long vfsflags;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001778 char *loopFile = NULL, *filteropts = NULL;
1779 llist_t *fl = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001780 struct stat st;
1781
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001782 errno = 0;
1783
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001784 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1785
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001786 // Treat fstype "auto" as unspecified
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001787 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1788 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001789
Denis Vlasenko2535f122007-09-15 13:28:30 +00001790 // Might this be a virtual filesystem?
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001791 if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1792 char *args[35];
1793 char *s;
1794 int n;
1795 // fsname: "cmd#arg1#arg2..."
1796 // WARNING: allows execution of arbitrary commands!
1797 // Try "mount 'sh#-c#sh' bogus_dir".
1798 // It is safe ONLY because non-root
1799 // cannot use two-argument mount command
1800 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1801 // "mount: can't find sh#-c#sh in /etc/fstab"
1802 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1803
1804 s = mp->mnt_fsname;
1805 n = 0;
1806 args[n++] = s;
1807 while (*s && n < 35 - 2) {
1808 if (*s++ == '#' && *s != '#') {
1809 s[-1] = '\0';
1810 args[n++] = s;
Denis Vlasenko2535f122007-09-15 13:28:30 +00001811 }
1812 }
Denis Vlasenko2535f122007-09-15 13:28:30 +00001813 args[n++] = mp->mnt_dir;
1814 args[n] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +01001815 rc = spawn_and_wait(args);
Denis Vlasenko2535f122007-09-15 13:28:30 +00001816 goto report_error;
1817 }
1818
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001819 // Might this be an CIFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001820 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001821 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1822 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1823 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001824 ) {
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001825 int len;
1826 char c;
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001827 char *hostname, *share;
1828 char *dotted, *ip;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001829 len_and_sockaddr *lsa;
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001830
1831 // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001832
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001833 hostname = mp->mnt_fsname + 2;
1834 len = strcspn(hostname, "/\\");
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001835 share = hostname + len + 1;
1836 if (len == 0 // 3rd char is a [back]slash (IOW: empty hostname)
1837 || share[-1] == '\0' // no [back]slash after hostname
1838 || share[0] == '\0' // empty share name
Martin Santesson406ea152013-01-16 00:47:19 +01001839 ) {
Denis Vlasenko5c329932009-04-12 12:16:21 +00001840 goto report_error;
Martin Santesson406ea152013-01-16 00:47:19 +01001841 }
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001842 c = share[-1];
1843 share[-1] = '\0';
1844 len = strcspn(share, "/\\");
Martin Santesson406ea152013-01-16 00:47:19 +01001845
1846 // "unc=\\hostname\share" option is mandatory
1847 // after CIFS option parsing was rewritten in Linux 3.4.
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001848 // Must use backslashes.
1849 // If /dir1/dir2 is present, also add "prefixpath=dir1/dir2"
Martin Santesson406ea152013-01-16 00:47:19 +01001850 {
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001851 char *unc = xasprintf(
1852 share[len] != '\0' /* "/dir1/dir2" exists? */
1853 ? "unc=\\\\%s\\%.*s,prefixpath=%s"
1854 : "unc=\\\\%s\\%.*s",
1855 hostname,
1856 len, share,
1857 share + len + 1 /* "dir1/dir2" */
1858 );
Denys Vlasenko9b7ebfe2013-01-22 11:00:45 +01001859 parse_mount_options(unc, &filteropts);
1860 if (ENABLE_FEATURE_CLEAN_UP) free(unc);
Martin Santesson406ea152013-01-16 00:47:19 +01001861 }
1862
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001863 lsa = host2sockaddr(hostname, 0);
Bernhard Reutner-Fischer20c5e5a2013-01-17 02:30:35 +01001864 share[-1] = c;
Denis Vlasenko5c329932009-04-12 12:16:21 +00001865 if (!lsa)
1866 goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001867
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001868 // Insert "ip=..." option into options
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001869 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001870 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001871 ip = xasprintf("ip=%s", dotted);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001872 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001873 parse_mount_options(ip, &filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001874 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001875
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001876 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001877 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001878
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001879 goto report_error;
1880 }
1881
1882 // Might this be an NFS filesystem?
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001883 if ((!mp->mnt_type || strncmp(mp->mnt_type, "nfs", 3) == 0)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001884 && strchr(mp->mnt_fsname, ':') != NULL
1885 ) {
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001886 if (!mp->mnt_type)
1887 mp->mnt_type = (char*)"nfs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001888 rc = nfsmount(mp, vfsflags, filteropts);
1889 goto report_error;
1890 }
1891
1892 // Look at the file. (Not found isn't a failure for remount, or for
1893 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001894 // (We use stat, not lstat, in order to allow
1895 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001896 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001897 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1898 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001899 // Do we need to allocate a loopback device for it?
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001900 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1901 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001902 mp->mnt_fsname = NULL; // will receive malloced loop dev name
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001903 if (set_loop(&mp->mnt_fsname, loopFile, 0, /*ro:*/ (vfsflags & MS_RDONLY)) < 0) {
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001904 if (errno == EPERM || errno == EACCES)
1905 bb_error_msg(bb_msg_perm_denied_are_you_root);
1906 else
Denys Vlasenko6331cf02009-11-13 09:08:27 +01001907 bb_perror_msg("can't setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001908 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001909 }
1910
1911 // Autodetect bind mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001912 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1913 vfsflags |= MS_BIND;
1914 }
1915
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001916 // If we know the fstype (or don't need to), jump straight
1917 // to the actual mount.
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001918 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +01001919 char *next;
1920 for (;;) {
1921 next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
1922 if (next)
1923 *next = '\0';
1924 rc = mount_it_now(mp, vfsflags, filteropts);
1925 if (rc == 0 || !next)
1926 break;
1927 mp->mnt_type = next + 1;
1928 }
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001929 } else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001930 // Loop through filesystem types until mount succeeds
1931 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001932
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001933 // Initialize list of block backed filesystems.
1934 // This has to be done here so that during "mount -a",
1935 // mounts after /proc shows up can autodetect.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001936 if (!fslist) {
1937 fslist = get_block_backed_filesystems();
1938 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1939 atexit(delete_block_backed_filesystems);
1940 }
1941
1942 for (fl = fslist; fl; fl = fl->link) {
1943 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001944 rc = mount_it_now(mp, vfsflags, filteropts);
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +01001945 if (rc == 0)
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001946 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001947 }
1948 }
1949
1950 // If mount failed, clean up loop file (if any).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001951 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1952 del_loop(mp->mnt_fsname);
1953 if (ENABLE_FEATURE_CLEAN_UP) {
1954 free(loopFile);
1955 free(mp->mnt_fsname);
1956 }
1957 }
1958
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001959 report_error:
1960 if (ENABLE_FEATURE_CLEAN_UP)
1961 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001962
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001963 if (errno == EBUSY && ignore_busy)
1964 return 0;
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001965 if (rc != 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001966 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001967 return rc;
1968}
1969
Michael Abbott6b5accb2009-12-04 03:33:07 +01001970// -O support
1971// -O interprets a list of filter options which select whether a mount
1972// point will be mounted: only mounts with options matching *all* filtering
1973// options will be selected.
1974// By default each -O filter option must be present in the list of mount
1975// options, but if it is prefixed by "no" then it must be absent.
1976// For example,
1977// -O a,nob,c matches -o a,c but fails to match -o a,b,c
1978// (and also fails to match -o a because -o c is absent).
1979//
1980// It is different from -t in that each option is matched exactly; a leading
1981// "no" at the beginning of one option does not negate the rest.
1982static int match_opt(const char *fs_opt_in, const char *O_opt)
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001983{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001984 if (!O_opt)
Michael Abbott6b5accb2009-12-04 03:33:07 +01001985 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001986
Michael Abbott6b5accb2009-12-04 03:33:07 +01001987 while (*O_opt) {
1988 const char *fs_opt = fs_opt_in;
1989 int O_len;
1990 int match;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001991
Michael Abbott6b5accb2009-12-04 03:33:07 +01001992 // If option begins with "no" then treat as an inverted match:
1993 // matching is a failure
1994 match = 0;
1995 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
1996 match = 1;
1997 O_opt += 2;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001998 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01001999 // Isolate the current O option
2000 O_len = strchrnul(O_opt, ',') - O_opt;
2001 // Check for a match against existing options
2002 while (1) {
2003 if (strncmp(fs_opt, O_opt, O_len) == 0
2004 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
2005 ) {
2006 if (match)
2007 return 0; // "no" prefix, but option found
2008 match = 1; // current O option found, go check next one
2009 break;
2010 }
2011 fs_opt = strchr(fs_opt, ',');
2012 if (!fs_opt)
2013 break;
2014 fs_opt++;
2015 }
2016 if (match == 0)
2017 return 0; // match wanted but not found
2018 if (O_opt[O_len] == '\0') // end?
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002019 break;
Michael Abbott6b5accb2009-12-04 03:33:07 +01002020 // Step to the next O option
2021 O_opt += O_len + 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002022 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01002023 // If we get here then everything matched
2024 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002025}
2026
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002027// Parse options, if necessary parse fstab/mtab, and call singlemount for
2028// each directory to be mounted.
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00002029int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002030int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002031{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002032 char *cmdopts = xzalloc(1);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002033 char *fstype = NULL;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002034 char *O_optmatch = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002035 char *storage_path;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00002036 llist_t *lst_o = NULL;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00002037 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002038 FILE *fstab;
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002039 int i, j;
2040 int rc = EXIT_SUCCESS;
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002041 unsigned long cmdopt_flags;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00002042 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002043 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002044 IF_NOT_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002045
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002046 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00002047
Denys Vlasenko16714242011-09-21 01:59:15 +02002048 INIT_G();
2049
Denis Vlasenkof732e962008-02-18 12:07:49 +00002050 // Parse long options, like --bind and --move. Note that -o option
2051 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002052 for (i = j = 1; argv[i]; i++) {
2053 if (argv[i][0] == '-' && argv[i][1] == '-')
2054 append_mount_options(&cmdopts, argv[i] + 2);
2055 else
2056 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002057 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002058 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002059
2060 // Parse remaining options
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002061 // Max 2 params; -o is a list, -v is a counter
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002062 opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002063 opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002064 IF_FEATURE_MOUNT_VERBOSE(, &verbose));
Denis Vlasenkof9dde912008-10-18 19:15:57 +00002065 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00002066 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2067 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00002068 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002069
2070 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002071 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00002072 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002073 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2074
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002075 if (!mountTable)
2076 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002077
Denis Vlasenko2535f122007-09-15 13:28:30 +00002078 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002079 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002080 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002081 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00002082 // util-linux 2.12a happily shows rootfs...
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002083 //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002084
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002085 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002086 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2087 mtpair->mnt_dir, mtpair->mnt_type,
2088 mtpair->mnt_opts);
2089 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002090 if (ENABLE_FEATURE_CLEAN_UP)
2091 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002092 return EXIT_SUCCESS;
2093 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002094 storage_path = NULL;
2095 } else {
2096 // When we have two arguments, the second is the directory and we can
2097 // skip looking at fstab entirely. We can always abspath() the directory
2098 // argument when we get it.
2099 if (argv[1]) {
2100 if (nonroot)
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002101 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002102 mtpair->mnt_fsname = argv[0];
2103 mtpair->mnt_dir = argv[1];
2104 mtpair->mnt_type = fstype;
2105 mtpair->mnt_opts = cmdopts;
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002106 resolve_mount_spec(&mtpair->mnt_fsname);
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002107 rc = singlemount(mtpair, /*ignore_busy:*/ 0);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002108 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00002109 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002110 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002111 }
2112
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002113 // Past this point, we are handling either "mount -a [opts]"
2114 // or "mount [opts] single_param"
2115
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002116 cmdopt_flags = parse_mount_options(cmdopts, NULL);
2117 if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002118 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00002119
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002120 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002121 if (ENABLE_FEATURE_MOUNT_FLAGS
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002122 && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002123 ) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002124 // verbose_mount(source, target, type, flags, data)
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002125 rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002126 if (rc)
2127 bb_simple_perror_msg_and_die(argv[0]);
2128 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002129 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00002130
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002131 // Open either fstab or mtab
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002132 fstabname = "/etc/fstab";
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002133 if (cmdopt_flags & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002134 // WARNING. I am not sure this matches util-linux's
2135 // behavior. It's possible util-linux does not
2136 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002137 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002138 }
2139 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00002140 if (!fstab)
Denys Vlasenko651a2692010-03-23 16:25:17 +01002141 bb_perror_msg_and_die("can't read '%s'", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002142
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002143 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00002144 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002145 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002146 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002147
2148 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00002149 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002150 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002151 GETMNTENT_BUFSIZE/2)
2152 ) { // End of fstab/mtab is reached
2153 mtcur = mtother; // the thing we found last time
2154 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002155 }
2156
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002157 // If we're trying to mount something specific and this isn't it,
2158 // skip it. Note we must match the exact text in fstab (ala
2159 // "proc") or a full path from root
2160 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002161
2162 // Is this what we're looking for?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002163 if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2164 && strcmp(storage_path, mtcur->mnt_fsname) != 0
2165 && strcmp(argv[0], mtcur->mnt_dir) != 0
2166 && strcmp(storage_path, mtcur->mnt_dir) != 0
2167 ) {
2168 continue; // no
2169 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002170
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002171 // Remember this entry. Something later may have
2172 // overmounted it, and we want the _last_ match.
2173 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002174
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002175 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002176 } else {
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002177 struct mntent *mp;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002178 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002179 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002180 if (nonroot)
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002181 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002182
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002183 // Does type match? (NULL matches always)
2184 if (!match_fstype(mtcur, fstype))
2185 continue;
2186
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002187 // Skip noauto and swap anyway
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002188 if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2189 // swap is bogus "fstype", parse_mount_options can't check fstypes
2190 || strcasecmp(mtcur->mnt_type, "swap") == 0
2191 ) {
2192 continue;
2193 }
2194
2195 // Does (at least one) option match?
2196 // (NULL matches always)
2197 if (!match_opt(mtcur->mnt_opts, O_optmatch))
2198 continue;
2199
2200 resolve_mount_spec(&mtcur->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002201
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002202 // NFS mounts want this to be xrealloc-able
2203 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002204
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002205 // If nothing is mounted on this directory...
2206 // (otherwise repeated "mount -a" mounts everything again)
2207 mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2208 // We do not check fsname match of found mount point -
2209 // "/" may have fsname of "/dev/root" while fstab
2210 // says "/dev/something_else".
2211 if (mp) {
Denys Vlasenko86566762009-12-10 21:32:28 +01002212 if (verbose) {
2213 bb_error_msg("according to %s, "
2214 "%s is already mounted on %s",
2215 bb_path_mtab_file,
2216 mp->mnt_fsname, mp->mnt_dir);
2217 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002218 } else {
2219 // ...mount this thing
2220 if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2221 // Count number of failed mounts
2222 rc++;
2223 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002224 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002225 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002226 }
2227 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002228
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002229 // End of fstab/mtab is reached.
2230 // Were we looking for something specific?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002231 if (argv[0]) { // yes
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002232 unsigned long l;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002233
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002234 // If we didn't find anything, complain
2235 if (!mtcur->mnt_fsname)
2236 bb_error_msg_and_die("can't find %s in %s",
2237 argv[0], fstabname);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002238
2239 // What happens when we try to "mount swap_partition"?
2240 // (fstab containts "swap_partition swap swap defaults 0 0")
2241 // util-linux-ng 2.13.1 does this:
2242 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2243 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2244 // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory)
2245 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2246 // exit_group(32) = ?
2247#if 0
2248 // In case we want to simply skip swap partitions:
2249 l = parse_mount_options(mtcur->mnt_opts, NULL);
2250 if ((l & MOUNT_SWAP)
2251 // swap is bogus "fstype", parse_mount_options can't check fstypes
2252 || strcasecmp(mtcur->mnt_type, "swap") == 0
2253 ) {
2254 goto ret;
2255 }
2256#endif
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002257 if (nonroot) {
2258 // fstab must have "users" or "user"
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002259 l = parse_mount_options(mtcur->mnt_opts, NULL);
2260 if (!(l & MOUNT_USERS))
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002261 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002262 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002263
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002264 //util-linux-2.12 does not do this check.
2265 //// If nothing is mounted on this directory...
2266 //// (otherwise repeated "mount FOO" mounts FOO again)
2267 //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2268 //if (mp) {
2269 // bb_error_msg("according to %s, "
2270 // "%s is already mounted on %s",
2271 // bb_path_mtab_file,
2272 // mp->mnt_fsname, mp->mnt_dir);
2273 //} else {
2274 // ...mount the last thing we found
2275 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2276 append_mount_options(&(mtcur->mnt_opts), cmdopts);
2277 resolve_mount_spec(&mtpair->mnt_fsname);
2278 rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2279 if (ENABLE_FEATURE_CLEAN_UP)
2280 free(mtcur->mnt_opts);
2281 //}
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002282 }
2283
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002284 //ret:
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002285 if (ENABLE_FEATURE_CLEAN_UP)
2286 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002287 if (ENABLE_FEATURE_CLEAN_UP) {
2288 free(storage_path);
2289 free(cmdopts);
2290 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002291
2292//TODO: exitcode should be ORed mask of (from "man mount"):
2293// 0 success
2294// 1 incorrect invocation or permissions
2295// 2 system error (out of memory, cannot fork, no more loop devices)
2296// 4 internal mount bug or missing nfs support in mount
2297// 8 user interrupt
2298//16 problems writing or locking /etc/mtab
2299//32 mount failure
2300//64 some mount succeeded
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002301 return rc;
2302}