blob: 525fdcce9cbc60a53bc9e4e4ca3fdf6cad06f54a [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: )
39//usage: "\n -r Read-only mount"
40//usage: "\n -w Read-write mount (default)"
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +010041//usage: "\n -t FSTYPE[,...] Filesystem type(s)"
Pere Orga5bc8c002011-04-11 03:29:49 +020042//usage: "\n -O OPT Mount only filesystems with option OPT (-a only)"
43//usage: "\n-o OPT:"
44//usage: IF_FEATURE_MOUNT_LOOP(
45//usage: "\n loop Ignored (loop devices are autodetected)"
46//usage: )
47//usage: IF_FEATURE_MOUNT_FLAGS(
48//usage: "\n [a]sync Writes are [a]synchronous"
49//usage: "\n [no]atime Disable/enable updates to inode access times"
50//usage: "\n [no]diratime Disable/enable atime updates to directories"
51//usage: "\n [no]relatime Disable/enable atime updates relative to modification time"
52//usage: "\n [no]dev (Dis)allow use of special device files"
53//usage: "\n [no]exec (Dis)allow use of executable files"
54//usage: "\n [no]suid (Dis)allow set-user-id-root programs"
55//usage: "\n [r]shared Convert [recursively] to a shared subtree"
56//usage: "\n [r]slave Convert [recursively] to a slave subtree"
57//usage: "\n [r]private Convert [recursively] to a private subtree"
58//usage: "\n [un]bindable Make mount point [un]able to be bind mounted"
59//usage: "\n [r]bind Bind a file or directory [recursively] to another location"
60//usage: "\n move Relocate an existing mount point"
61//usage: )
62//usage: "\n remount Remount a mounted filesystem, changing flags"
63//usage: "\n ro/rw Same as -r/-w"
64//usage: "\n"
65//usage: "\nThere are filesystem-specific -o flags."
66//usage:
67//usage:#define mount_example_usage
68//usage: "$ mount\n"
69//usage: "/dev/hda3 on / type minix (rw)\n"
70//usage: "proc on /proc type proc (rw)\n"
71//usage: "devpts on /dev/pts type devpts (rw)\n"
72//usage: "$ mount /dev/fd0 /mnt -t msdos -o ro\n"
73//usage: "$ mount /tmp/diskimage /opt -t ext2 -o loop\n"
74//usage: "$ mount cd_image.iso mydir\n"
75//usage:#define mount_notes_usage
76//usage: "Returns 0 for success, number of failed mounts for -a, or errno for one mount."
77
Eric Andersencc8ed391999-10-05 16:24:54 +000078#include <mntent.h>
Bernhard Reutner-Fischerf4701962008-01-27 12:50:12 +000079#include <syslog.h>
Denys Vlasenkoda49f582009-07-08 02:58:38 +020080#include <sys/mount.h>
Denys Vlasenko102ff762009-11-21 17:14:08 +010081// Grab more as needed from util-linux's mount/mount_constants.h
82#ifndef MS_DIRSYNC
83# define MS_DIRSYNC (1 << 7) // Directory modifications are synchronous
84#endif
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +020085#ifndef MS_UNION
86# define MS_UNION (1 << 8)
87#endif
Denys Vlasenkoda49f582009-07-08 02:58:38 +020088#ifndef MS_BIND
89# define MS_BIND (1 << 12)
90#endif
91#ifndef MS_MOVE
92# define MS_MOVE (1 << 13)
93#endif
94#ifndef MS_RECURSIVE
95# define MS_RECURSIVE (1 << 14)
96#endif
97#ifndef MS_SILENT
98# define MS_SILENT (1 << 15)
99#endif
Denys Vlasenko102ff762009-11-21 17:14:08 +0100100// The shared subtree stuff, which went in around 2.6.15
Denys Vlasenkoda49f582009-07-08 02:58:38 +0200101#ifndef MS_UNBINDABLE
102# define MS_UNBINDABLE (1 << 17)
103#endif
104#ifndef MS_PRIVATE
105# define MS_PRIVATE (1 << 18)
106#endif
107#ifndef MS_SLAVE
108# define MS_SLAVE (1 << 19)
109#endif
110#ifndef MS_SHARED
111# define MS_SHARED (1 << 20)
112#endif
113#ifndef MS_RELATIME
114# define MS_RELATIME (1 << 21)
115#endif
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200116#ifndef MS_STRICTATIME
117# define MS_STRICTATIME (1 << 24)
118#endif
119
120/* Any ~MS_FOO value has this bit set: */
121#define BB_MS_INVERTED_VALUE (1u << 31)
Eric Andersenbd22ed82000-07-08 18:55:24 +0000122
Denys Vlasenko102ff762009-11-21 17:14:08 +0100123#include "libbb.h"
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000124#if ENABLE_FEATURE_MOUNT_LABEL
Natanael Copa9aff2992009-09-20 04:28:22 +0200125# include "volume_id.h"
126#else
127# define resolve_mount_spec(fsname) ((void)0)
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000128#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000129
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000130// Needed for nfs support only
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000131#include <sys/utsname.h>
132#undef TRUE
133#undef FALSE
Denys Vlasenkocc428142009-12-16 02:06:56 +0100134#if ENABLE_FEATURE_MOUNT_NFS
135/* This is just a warning of a common mistake. Possibly this should be a
136 * uclibc faq entry rather than in busybox... */
137# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
138# error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
139# endif
140# include <rpc/rpc.h>
141# include <rpc/pmap_prot.h>
142# include <rpc/pmap_clnt.h>
143#endif
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000144
145
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000146#if defined(__dietlibc__)
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000147// 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
148// dietlibc-0.30 does not have implementation of getmntent_r()
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +0000149static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000150 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000151{
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000152 struct mntent* ment = getmntent(stream);
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +0000153 return memcpy(result, ment, sizeof(*ment));
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000154}
155#endif
156
157
Rob Landleydc0955b2006-03-14 18:16:25 +0000158// Not real flags, but we want to be able to check for this.
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000159enum {
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000160 MOUNT_USERS = (1 << 28) * ENABLE_DESKTOP,
161 MOUNT_NOAUTO = (1 << 29),
162 MOUNT_SWAP = (1 << 30),
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000163};
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000164
165
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +0000166#define OPTION_STR "o:t:rwanfvsiO:"
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000167enum {
168 OPT_o = (1 << 0),
169 OPT_t = (1 << 1),
170 OPT_r = (1 << 2),
171 OPT_w = (1 << 3),
172 OPT_a = (1 << 4),
173 OPT_n = (1 << 5),
174 OPT_f = (1 << 6),
175 OPT_v = (1 << 7),
176 OPT_s = (1 << 8),
177 OPT_i = (1 << 9),
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +0000178 OPT_O = (1 << 10),
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000179};
180
181#if ENABLE_FEATURE_MTAB_SUPPORT
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200182#define USE_MTAB (!(option_mask32 & OPT_n))
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000183#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200184#define USE_MTAB 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000185#endif
186
187#if ENABLE_FEATURE_MOUNT_FAKE
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200188#define FAKE_IT (option_mask32 & OPT_f)
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000189#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200190#define FAKE_IT 0
191#endif
192
193#if ENABLE_FEATURE_MOUNT_HELPERS
194#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
195#else
196#define HELPERS_ALLOWED 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000197#endif
198
199
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000200// TODO: more "user" flag compatibility.
201// "user" option (from mount manpage):
202// Only the user that mounted a filesystem can unmount it again.
203// If any user should be able to unmount, then use users instead of user
204// in the fstab line. The owner option is similar to the user option,
205// with the restriction that the user must be the owner of the special file.
206// This may be useful e.g. for /dev/fd if a login script makes
207// the console user owner of this device.
Rob Landley3ba7bd12006-08-09 19:51:13 +0000208
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000209// Standard mount options (from -o options or --options),
210// with corresponding flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000211static const int32_t mount_options[] = {
Rob Landleye3781b72006-08-08 01:39:49 +0000212 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
Rob Landleydc0955b2006-03-14 18:16:25 +0000213
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000214 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000215 /* "loop" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000216 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000217
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000218 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000219 /* "defaults" */ 0,
220 /* "quiet" 0 - do not filter out, vfat wants to see it */
221 /* "noauto" */ MOUNT_NOAUTO,
222 /* "sw" */ MOUNT_SWAP,
223 /* "swap" */ MOUNT_SWAP,
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000224 IF_DESKTOP(/* "user" */ MOUNT_USERS,)
225 IF_DESKTOP(/* "users" */ MOUNT_USERS,)
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000226 /* "_netdev" */ 0,
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200227 IF_DESKTOP(/* "comment" */ 0,) /* systemd uses this in fstab */
Rob Landleye3781b72006-08-08 01:39:49 +0000228 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000229
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000230 IF_FEATURE_MOUNT_FLAGS(
Rob Landleye3781b72006-08-08 01:39:49 +0000231 // vfs flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000232 /* "nosuid" */ MS_NOSUID,
233 /* "suid" */ ~MS_NOSUID,
234 /* "dev" */ ~MS_NODEV,
235 /* "nodev" */ MS_NODEV,
236 /* "exec" */ ~MS_NOEXEC,
237 /* "noexec" */ MS_NOEXEC,
238 /* "sync" */ MS_SYNCHRONOUS,
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +0000239 /* "dirsync" */ MS_DIRSYNC,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000240 /* "async" */ ~MS_SYNCHRONOUS,
241 /* "atime" */ ~MS_NOATIME,
242 /* "noatime" */ MS_NOATIME,
243 /* "diratime" */ ~MS_NODIRATIME,
244 /* "nodiratime" */ MS_NODIRATIME,
Denis Vlasenko580ce2d2008-07-08 02:56:53 +0000245 /* "mand" */ MS_MANDLOCK,
246 /* "nomand" */ ~MS_MANDLOCK,
Bernhard Reutner-Fischerfb5902c2008-08-06 18:14:38 +0000247 /* "relatime" */ MS_RELATIME,
248 /* "norelatime" */ ~MS_RELATIME,
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200249 /* "strictatime" */ MS_STRICTATIME,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000250 /* "loud" */ ~MS_SILENT,
Roman Borisov19311bf2011-03-24 15:08:43 +0300251 /* "rbind" */ MS_BIND|MS_RECURSIVE,
Eric Andersen9601a1c2006-03-20 18:07:50 +0000252
Rob Landleye3781b72006-08-08 01:39:49 +0000253 // action flags
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200254 /* "union" */ MS_UNION,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000255 /* "bind" */ MS_BIND,
256 /* "move" */ MS_MOVE,
257 /* "shared" */ MS_SHARED,
258 /* "slave" */ MS_SLAVE,
259 /* "private" */ MS_PRIVATE,
260 /* "unbindable" */ MS_UNBINDABLE,
261 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
262 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
Roman Borisovd3679d22011-03-23 11:20:25 +0300263 /* "rprivate" */ MS_PRIVATE|MS_RECURSIVE,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000264 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
Rob Landleye3781b72006-08-08 01:39:49 +0000265 )
266
267 // Always understood.
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000268 /* "ro" */ MS_RDONLY, // vfs flag
269 /* "rw" */ ~MS_RDONLY, // vfs flag
270 /* "remount" */ MS_REMOUNT // action flag
Eric Andersencc8ed391999-10-05 16:24:54 +0000271};
272
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000273static const char mount_option_str[] =
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000274 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000275 "loop\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000276 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000277 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000278 "defaults\0"
279 // "quiet\0" - do not filter out, vfat wants to see it
280 "noauto\0"
281 "sw\0"
282 "swap\0"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000283 IF_DESKTOP("user\0")
284 IF_DESKTOP("users\0")
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000285 "_netdev\0"
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200286 IF_DESKTOP("comment\0") /* systemd uses this in fstab */
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000287 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000288 IF_FEATURE_MOUNT_FLAGS(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000289 // vfs flags
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000290 "nosuid\0"
291 "suid\0"
292 "dev\0"
293 "nodev\0"
294 "exec\0"
295 "noexec\0"
296 "sync\0"
297 "dirsync\0"
298 "async\0"
299 "atime\0"
300 "noatime\0"
301 "diratime\0"
302 "nodiratime\0"
303 "mand\0"
304 "nomand\0"
305 "relatime\0"
306 "norelatime\0"
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200307 "strictatime\0"
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000308 "loud\0"
Roman Borisov19311bf2011-03-24 15:08:43 +0300309 "rbind\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000310
311 // action flags
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200312 "union\0"
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000313 "bind\0"
314 "move\0"
Roman Borisov945fd172011-02-25 14:50:39 +0300315 "make-shared\0"
316 "make-slave\0"
317 "make-private\0"
318 "make-unbindable\0"
319 "make-rshared\0"
320 "make-rslave\0"
321 "make-rprivate\0"
322 "make-runbindable\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000323 )
324
325 // Always understood.
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000326 "ro\0" // vfs flag
327 "rw\0" // vfs flag
328 "remount\0" // action flag
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000329;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000330
Denis Vlasenkof732e962008-02-18 12:07:49 +0000331
332struct globals {
333#if ENABLE_FEATURE_MOUNT_NFS
334 smalluint nfs_mount_version;
335#endif
336#if ENABLE_FEATURE_MOUNT_VERBOSE
337 unsigned verbose;
338#endif
339 llist_t *fslist;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000340 char getmntent_buf[1];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100341} FIX_ALIASING;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000342enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
Denis Vlasenkof732e962008-02-18 12:07:49 +0000343#define G (*(struct globals*)&bb_common_bufsiz1)
344#define nfs_mount_version (G.nfs_mount_version)
Denis Vlasenkob4133682008-02-18 13:05:38 +0000345#if ENABLE_FEATURE_MOUNT_VERBOSE
Denis Vlasenkof732e962008-02-18 12:07:49 +0000346#define verbose (G.verbose )
Denis Vlasenkob4133682008-02-18 13:05:38 +0000347#else
348#define verbose 0
349#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000350#define fslist (G.fslist )
351#define getmntent_buf (G.getmntent_buf )
Denys Vlasenko16714242011-09-21 01:59:15 +0200352#define INIT_G() do { } while (0)
Denis Vlasenkof732e962008-02-18 12:07:49 +0000353
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100354#if ENABLE_FEATURE_MTAB_SUPPORT
355/*
356 * update_mtab_entry_on_move() is used to update entry in case of mount --move.
357 * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
358 * input mntent and replace it by new one.
359 */
360static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
361{
362 struct mntent *entries, *m;
363 int i, count;
364 FILE *mountTable;
365
366 mountTable = setmntent(bb_path_mtab_file, "r");
367 if (!mountTable) {
368 bb_perror_msg(bb_path_mtab_file);
369 return;
370 }
371
372 entries = NULL;
373 count = 0;
374 while ((m = getmntent(mountTable)) != NULL) {
375 entries = xrealloc_vector(entries, 3, count);
376 entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
377 entries[count].mnt_dir = xstrdup(m->mnt_dir);
378 entries[count].mnt_type = xstrdup(m->mnt_type);
379 entries[count].mnt_opts = xstrdup(m->mnt_opts);
380 entries[count].mnt_freq = m->mnt_freq;
381 entries[count].mnt_passno = m->mnt_passno;
382 count++;
383 }
384 endmntent(mountTable);
385
386 mountTable = setmntent(bb_path_mtab_file, "w");
387 if (mountTable) {
388 for (i = 0; i < count; i++) {
389 if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
390 addmntent(mountTable, &entries[i]);
391 else
392 addmntent(mountTable, mp);
393 }
394 endmntent(mountTable);
395 } else if (errno != EROFS)
396 bb_perror_msg(bb_path_mtab_file);
397
398 if (ENABLE_FEATURE_CLEAN_UP) {
399 for (i = 0; i < count; i++) {
400 free(entries[i].mnt_fsname);
401 free(entries[i].mnt_dir);
402 free(entries[i].mnt_type);
403 free(entries[i].mnt_opts);
404 }
405 free(entries);
406 }
407}
408#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000409
410#if ENABLE_FEATURE_MOUNT_VERBOSE
411static int verbose_mount(const char *source, const char *target,
412 const char *filesystemtype,
413 unsigned long mountflags, const void *data)
414{
415 int rc;
416
417 errno = 0;
418 rc = mount(source, target, filesystemtype, mountflags, data);
419 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000420 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000421 source, target, filesystemtype,
422 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000423 return rc;
424}
425#else
426#define verbose_mount(...) mount(__VA_ARGS__)
427#endif
428
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000429// Append mount options to string
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000430static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000431{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000432 if (*oldopts && **oldopts) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000433 // Do not insert options which are already there
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000434 while (newopts[0]) {
435 char *p;
436 int len = strlen(newopts);
437 p = strchr(newopts, ',');
438 if (p) len = p - newopts;
439 p = *oldopts;
440 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000441 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000442 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000443 goto skip;
444 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000445 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000446 p++;
447 }
448 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
449 free(*oldopts);
450 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000451 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000452 newopts += len;
453 while (newopts[0] == ',') newopts++;
454 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000455 } else {
456 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000457 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000458 }
459}
460
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000461// Use the mount_options list to parse options into flags.
Alexander Shishkin77650952010-10-28 06:10:03 +0200462// Also update list of unrecognized options if unrecognized != NULL
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200463static unsigned long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000464{
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200465 unsigned long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000466
Rob Landley6a6798b2005-08-10 20:35:54 +0000467 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000468 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000469 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000470 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000471 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000472
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000473 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000474
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000475// FIXME: use hasmntopt()
Rob Landley6a6798b2005-08-10 20:35:54 +0000476 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000477 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200478 /* We support "option=" match for "comment=" thingy */
479 unsigned opt_len = strlen(option_str);
480 if (strncasecmp(option_str, options, opt_len) == 0
481 && (options[opt_len] == '\0' || options[opt_len] == '=')
482 ) {
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200483 unsigned long fl = mount_options[i];
Denys Vlasenko9ad89792012-06-26 16:09:00 +0200484 if (fl & BB_MS_INVERTED_VALUE)
Alexander Shishkin77650952010-10-28 06:10:03 +0200485 flags &= fl;
486 else
487 flags |= fl;
488 goto found;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000489 }
Denys Vlasenko6ebb2b62012-06-22 15:17:18 +0200490 option_str += opt_len + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000491 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200492 // We did not recognize this option.
493 // If "unrecognized" is not NULL, append option there.
494 // Note that we should not append *empty* option -
495 // in this case we want to pass NULL, not "", to "data"
496 // parameter of mount(2) syscall.
497 // This is crucial for filesystems that don't accept
498 // any arbitrary mount options, like cgroup fs:
499 // "mount -t cgroup none /mnt"
500 if (options[0] && unrecognized) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000501 // Add it to strflags, to pass on to kernel
Alexander Shishkin77650952010-10-28 06:10:03 +0200502 char *p = *unrecognized;
503 unsigned len = p ? strlen(p) : 0;
504 *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000505
Rob Landley6a6798b2005-08-10 20:35:54 +0000506 // Comma separated if it's not the first one
Alexander Shishkin77650952010-10-28 06:10:03 +0200507 if (len) p[len++] = ',';
508 strcpy(p + len, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000509 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200510 found:
Denis Vlasenko2535f122007-09-15 13:28:30 +0000511 if (!comma)
512 break;
513 // Advance to next option
514 *comma = ',';
515 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000516 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000517
Rob Landleydc0955b2006-03-14 18:16:25 +0000518 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000519}
520
Rob Landleydc0955b2006-03-14 18:16:25 +0000521// Return a list of all block device backed filesystems
Rob Landleydc0955b2006-03-14 18:16:25 +0000522static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000523{
Denis Vlasenko87468852007-04-13 23:22:00 +0000524 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000525 "/etc/filesystems",
526 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000527 };
528 char *fs, *buf;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200529 llist_t *list = NULL;
Rob Landleydc0955b2006-03-14 18:16:25 +0000530 int i;
531 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000532
Denis Vlasenko87468852007-04-13 23:22:00 +0000533 for (i = 0; i < 2; i++) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000534 f = fopen_for_read(filesystems[i]);
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000535 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000536
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000537 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200538 if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100539 goto next;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000540 fs = skip_whitespace(buf);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200541 if (*fs == '#' || *fs == '*' || !*fs)
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100542 goto next;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000543
Denis Vlasenko372686b2006-10-12 22:42:33 +0000544 llist_add_to_end(&list, xstrdup(fs));
Denys Vlasenkof85554c2011-11-03 09:54:53 +0100545 next:
Denis Vlasenko372686b2006-10-12 22:42:33 +0000546 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000547 }
548 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
549 }
550
551 return list;
552}
553
Rob Landleydc0955b2006-03-14 18:16:25 +0000554#if ENABLE_FEATURE_CLEAN_UP
555static void delete_block_backed_filesystems(void)
556{
Rob Landleya6b5b602006-05-08 19:03:07 +0000557 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000558}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000559#else
560void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000561#endif
562
Rob Landleydc0955b2006-03-14 18:16:25 +0000563// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000564// NB: mp->xxx fields may be trashed on exit
Denys Vlasenko9ee42662012-06-21 12:08:56 +0200565static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000566{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000567 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000568
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200569 if (FAKE_IT) {
Denis Vlasenkob4133682008-02-18 13:05:38 +0000570 if (verbose >= 2)
571 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
572 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
573 vfsflags, filteropts);
574 goto mtab;
575 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000576
Rob Landleydc0955b2006-03-14 18:16:25 +0000577 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000578 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000579 errno = 0;
580 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000581 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000582
583 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000584 // helper program mount.<mnt_type>
Denys Vlasenkoba986032009-09-15 23:00:09 +0200585 if (HELPERS_ALLOWED && rc && mp->mnt_type) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200586 char *args[8];
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000587 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000588 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000589 rc = 1;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200590 if (FAKE_IT)
591 args[rc++] = (char *)"-f";
592 if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
593 args[rc++] = (char *)"-n";
Denis Vlasenko5c329932009-04-12 12:16:21 +0000594 args[rc++] = mp->mnt_fsname;
595 args[rc++] = mp->mnt_dir;
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000596 if (filteropts) {
597 args[rc++] = (char *)"-o";
598 args[rc++] = filteropts;
599 }
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000600 args[rc] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +0100601 rc = spawn_and_wait(args);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000602 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000603 if (!rc)
604 break;
605 errno = errno_save;
606 }
607
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000608 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000609 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000610 if (!(vfsflags & MS_SILENT))
611 bb_error_msg("%s is write-protected, mounting read-only",
612 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000613 vfsflags |= MS_RDONLY;
614 }
615
Rob Landleydc0955b2006-03-14 18:16:25 +0000616 // Abort entirely if permission denied.
617
618 if (rc && errno == EPERM)
619 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
620
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000621 // If the mount was successful, and we're maintaining an old-style
622 // mtab file by hand, add the new entry to it now.
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000623 mtab:
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200624 if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000625 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000626 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000627 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000628 int i;
629
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000630 if (!mountTable) {
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100631 bb_perror_msg(bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000632 goto ret;
633 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000634
635 // Add vfs string flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000636 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
637 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
638 append_mount_options(&(mp->mnt_opts), option_str);
639 option_str += strlen(option_str) + 1;
640 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000641
642 // Remove trailing / (if any) from directory we mounted on
Denis Vlasenko727ef942006-09-14 13:19:19 +0000643 i = strlen(mp->mnt_dir) - 1;
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100644 while (i > 0 && mp->mnt_dir[i] == '/')
Denys Vlasenkoc6450c92011-02-28 11:09:49 +0100645 mp->mnt_dir[i--] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000646
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000647 // Convert to canonical pathnames as needed
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000648 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100649 fsname = NULL;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000650 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000651 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000652 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000653 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000654 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000655
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100656 // Write and close
657#if ENABLE_FEATURE_MTAB_SUPPORT
658 if (vfsflags & MS_MOVE)
659 update_mtab_entry_on_move(mp);
660 else
661#endif
662 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000663 endmntent(mountTable);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100664
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000665 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000666 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000667 free(fsname);
668 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000669 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000670 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000671 return rc;
672}
673
Denis Vlasenko25098f72006-09-14 15:46:33 +0000674#if ENABLE_FEATURE_MOUNT_NFS
675
676/*
677 * Linux NFS mount
678 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
679 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +0200680 * Licensed under GPLv2, see file LICENSE in this source tree.
Denis Vlasenko25098f72006-09-14 15:46:33 +0000681 *
682 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
683 * numbers to be specified on the command line.
684 *
685 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
686 * Omit the call to connect() for Linux version 1.3.11 or later.
687 *
688 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
689 * Implemented the "bg", "fg" and "retry" mount options for NFS.
690 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000691 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000692 * - added Native Language Support
693 *
694 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
695 * plus NFSv3 stuff.
696 */
697
Denis Vlasenko25098f72006-09-14 15:46:33 +0000698#define MOUNTPORT 635
699#define MNTPATHLEN 1024
700#define MNTNAMLEN 255
701#define FHSIZE 32
702#define FHSIZE3 64
703
704typedef char fhandle[FHSIZE];
705
706typedef struct {
707 unsigned int fhandle3_len;
708 char *fhandle3_val;
709} fhandle3;
710
711enum mountstat3 {
712 MNT_OK = 0,
713 MNT3ERR_PERM = 1,
714 MNT3ERR_NOENT = 2,
715 MNT3ERR_IO = 5,
716 MNT3ERR_ACCES = 13,
717 MNT3ERR_NOTDIR = 20,
718 MNT3ERR_INVAL = 22,
719 MNT3ERR_NAMETOOLONG = 63,
720 MNT3ERR_NOTSUPP = 10004,
721 MNT3ERR_SERVERFAULT = 10006,
722};
723typedef enum mountstat3 mountstat3;
724
725struct fhstatus {
726 unsigned int fhs_status;
727 union {
728 fhandle fhs_fhandle;
729 } fhstatus_u;
730};
731typedef struct fhstatus fhstatus;
732
733struct mountres3_ok {
734 fhandle3 fhandle;
735 struct {
736 unsigned int auth_flavours_len;
737 char *auth_flavours_val;
738 } auth_flavours;
739};
740typedef struct mountres3_ok mountres3_ok;
741
742struct mountres3 {
743 mountstat3 fhs_status;
744 union {
745 mountres3_ok mountinfo;
746 } mountres3_u;
747};
748typedef struct mountres3 mountres3;
749
750typedef char *dirpath;
751
752typedef char *name;
753
754typedef struct mountbody *mountlist;
755
756struct mountbody {
757 name ml_hostname;
758 dirpath ml_directory;
759 mountlist ml_next;
760};
761typedef struct mountbody mountbody;
762
763typedef struct groupnode *groups;
764
765struct groupnode {
766 name gr_name;
767 groups gr_next;
768};
769typedef struct groupnode groupnode;
770
771typedef struct exportnode *exports;
772
773struct exportnode {
774 dirpath ex_dir;
775 groups ex_groups;
776 exports ex_next;
777};
778typedef struct exportnode exportnode;
779
780struct ppathcnf {
781 int pc_link_max;
782 short pc_max_canon;
783 short pc_max_input;
784 short pc_name_max;
785 short pc_path_max;
786 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000787 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000788 char pc_xxx;
789 short pc_mask[2];
790};
791typedef struct ppathcnf ppathcnf;
792
793#define MOUNTPROG 100005
794#define MOUNTVERS 1
795
796#define MOUNTPROC_NULL 0
797#define MOUNTPROC_MNT 1
798#define MOUNTPROC_DUMP 2
799#define MOUNTPROC_UMNT 3
800#define MOUNTPROC_UMNTALL 4
801#define MOUNTPROC_EXPORT 5
802#define MOUNTPROC_EXPORTALL 6
803
804#define MOUNTVERS_POSIX 2
805
806#define MOUNTPROC_PATHCONF 7
807
808#define MOUNT_V3 3
809
810#define MOUNTPROC3_NULL 0
811#define MOUNTPROC3_MNT 1
812#define MOUNTPROC3_DUMP 2
813#define MOUNTPROC3_UMNT 3
814#define MOUNTPROC3_UMNTALL 4
815#define MOUNTPROC3_EXPORT 5
816
817enum {
818#ifndef NFS_FHSIZE
819 NFS_FHSIZE = 32,
820#endif
821#ifndef NFS_PORT
822 NFS_PORT = 2049
823#endif
824};
825
Denis Vlasenko25098f72006-09-14 15:46:33 +0000826/*
827 * We want to be able to compile mount on old kernels in such a way
828 * that the binary will work well on more recent kernels.
829 * Thus, if necessary we teach nfsmount.c the structure of new fields
830 * that will come later.
831 *
832 * Moreover, the new kernel includes conflict with glibc includes
833 * so it is easiest to ignore the kernel altogether (at compile time).
834 */
835
836struct nfs2_fh {
Dave Lovefae473c2011-11-10 15:19:25 +0100837 char data[32];
Denis Vlasenko25098f72006-09-14 15:46:33 +0000838};
839struct nfs3_fh {
Dave Lovefae473c2011-11-10 15:19:25 +0100840 unsigned short size;
841 unsigned char data[64];
Denis Vlasenko25098f72006-09-14 15:46:33 +0000842};
843
844struct nfs_mount_data {
Dave Lovefae473c2011-11-10 15:19:25 +0100845 int version; /* 1 */
846 int fd; /* 1 */
847 struct nfs2_fh old_root; /* 1 */
848 int flags; /* 1 */
849 int rsize; /* 1 */
850 int wsize; /* 1 */
851 int timeo; /* 1 */
852 int retrans; /* 1 */
853 int acregmin; /* 1 */
854 int acregmax; /* 1 */
855 int acdirmin; /* 1 */
856 int acdirmax; /* 1 */
857 struct sockaddr_in addr; /* 1 */
858 char hostname[256]; /* 1 */
859 int namlen; /* 2 */
860 unsigned int bsize; /* 3 */
861 struct nfs3_fh root; /* 4 */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000862};
863
864/* bits in the flags field */
865enum {
866 NFS_MOUNT_SOFT = 0x0001, /* 1 */
867 NFS_MOUNT_INTR = 0x0002, /* 1 */
868 NFS_MOUNT_SECURE = 0x0004, /* 1 */
869 NFS_MOUNT_POSIX = 0x0008, /* 1 */
870 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
871 NFS_MOUNT_NOAC = 0x0020, /* 1 */
872 NFS_MOUNT_TCP = 0x0040, /* 2 */
873 NFS_MOUNT_VER3 = 0x0080, /* 3 */
874 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000875 NFS_MOUNT_NONLM = 0x0200, /* 3 */
Dave Lovefae473c2011-11-10 15:19:25 +0100876 NFS_MOUNT_NOACL = 0x0800, /* 4 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000877 NFS_MOUNT_NORDIRPLUS = 0x4000
Denis Vlasenko25098f72006-09-14 15:46:33 +0000878};
879
880
881/*
882 * We need to translate between nfs status return values and
883 * the local errno values which may not be the same.
884 *
885 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
886 * "after #include <errno.h> the symbol errno is reserved for any use,
887 * it cannot even be used as a struct tag or field name".
888 */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000889#ifndef EDQUOT
Denys Vlasenkocc428142009-12-16 02:06:56 +0100890# define EDQUOT ENOSPC
Denis Vlasenko25098f72006-09-14 15:46:33 +0000891#endif
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000892/* Convert each NFSERR_BLAH into EBLAH */
Denys Vlasenkocc428142009-12-16 02:06:56 +0100893static const uint8_t nfs_err_stat[] = {
894 1, 2, 5, 6, 13, 17,
895 19, 20, 21, 22, 27, 28,
896 30, 63, 66, 69, 70, 71
897};
Denys Vlasenkodc1fd2e2010-05-19 17:01:29 +0200898#if ( \
899 EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \
900 ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \
901 EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
902typedef uint8_t nfs_err_type;
903#else
904typedef uint16_t nfs_err_type;
905#endif
906static const nfs_err_type nfs_err_errnum[] = {
Denys Vlasenkocc428142009-12-16 02:06:56 +0100907 EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST,
908 ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC,
909 EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
Denis Vlasenko25098f72006-09-14 15:46:33 +0000910};
Denis Vlasenko25098f72006-09-14 15:46:33 +0000911static char *nfs_strerror(int status)
912{
913 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000914
Denys Vlasenkocc428142009-12-16 02:06:56 +0100915 for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
916 if (nfs_err_stat[i] == status)
917 return strerror(nfs_err_errnum[i]);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000918 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000919 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000920}
921
922static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
923{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200924 return xdr_opaque(xdrs, objp, FHSIZE);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000925}
926
927static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
928{
929 if (!xdr_u_int(xdrs, &objp->fhs_status))
930 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200931 if (objp->fhs_status == 0)
932 return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000933 return TRUE;
934}
935
936static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
937{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200938 return xdr_string(xdrs, objp, MNTPATHLEN);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000939}
940
941static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
942{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200943 return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
944 (unsigned int *) &objp->fhandle3_len,
945 FHSIZE3);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000946}
947
948static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
949{
950 if (!xdr_fhandle3(xdrs, &objp->fhandle))
951 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200952 return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
953 &(objp->auth_flavours.auth_flavours_len),
954 ~0,
955 sizeof(int),
956 (xdrproc_t) xdr_int);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000957}
958
959static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
960{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200961 return xdr_enum(xdrs, (enum_t *) objp);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000962}
963
964static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
965{
966 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
967 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200968 if (objp->fhs_status == MNT_OK)
969 return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000970 return TRUE;
971}
972
973#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
974
Denis Vlasenko25098f72006-09-14 15:46:33 +0000975/*
976 * Unfortunately, the kernel prints annoying console messages
977 * in case of an unexpected nfs mount version (instead of
978 * just returning some error). Therefore we'll have to try
979 * and figure out what version the kernel expects.
980 *
981 * Variables:
982 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
983 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
984 * nfs_mount_version: version this source and running kernel can handle
985 */
986static void
987find_kernel_nfs_mount_version(void)
988{
Denis Vlasenkob9256052007-09-28 10:29:17 +0000989 int kernel_version;
990
991 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000992 return;
993
994 nfs_mount_version = 4; /* default */
995
996 kernel_version = get_linux_version_code();
997 if (kernel_version) {
Denys Vlasenkocc428142009-12-16 02:06:56 +0100998 if (kernel_version < KERNEL_VERSION(2,2,18))
Denis Vlasenko25098f72006-09-14 15:46:33 +0000999 nfs_mount_version = 3;
1000 /* else v4 since 2.3.99pre4 */
1001 }
1002}
1003
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +00001004static void
Denis Vlasenkob9256052007-09-28 10:29:17 +00001005get_mountport(struct pmap *pm_mnt,
1006 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001007 long unsigned prog,
1008 long unsigned version,
1009 long unsigned proto,
1010 long unsigned port)
1011{
1012 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001013
1014 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001015/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
1016 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001017 pmap = pmap_getmaps(server_addr);
1018
1019 if (version > MAX_NFSPROT)
1020 version = MAX_NFSPROT;
1021 if (!prog)
1022 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001023 pm_mnt->pm_prog = prog;
1024 pm_mnt->pm_vers = version;
1025 pm_mnt->pm_prot = proto;
1026 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001027
Denis Vlasenko25098f72006-09-14 15:46:33 +00001028 while (pmap) {
1029 if (pmap->pml_map.pm_prog != prog)
1030 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001031 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001032 goto next;
1033 if (version > 2 && pmap->pml_map.pm_vers != version)
1034 goto next;
1035 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1036 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001037 if (pmap->pml_map.pm_vers > MAX_NFSPROT
1038 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1039 || (port && pmap->pml_map.pm_port != port)
1040 ) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001041 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001042 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001043 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1044 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001045 pmap = pmap->pml_next;
1046 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001047 if (!pm_mnt->pm_vers)
1048 pm_mnt->pm_vers = MOUNTVERS;
1049 if (!pm_mnt->pm_port)
1050 pm_mnt->pm_port = MOUNTPORT;
1051 if (!pm_mnt->pm_prot)
1052 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001053}
1054
Denis Vlasenkof0000652007-09-04 18:30:26 +00001055#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001056static int daemonize(void)
1057{
Denis Vlasenko25098f72006-09-14 15:46:33 +00001058 int pid = fork();
1059 if (pid < 0) /* error */
1060 return -errno;
1061 if (pid > 0) /* parent */
1062 return 0;
1063 /* child */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001064 close(0);
1065 xopen(bb_dev_null, O_RDWR);
1066 xdup2(0, 1);
1067 xdup2(0, 2);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001068 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +00001069 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001070 logmode = LOGMODE_SYSLOG;
1071 return 1;
1072}
Denis Vlasenkof0000652007-09-04 18:30:26 +00001073#else
1074static inline int daemonize(void) { return -ENOSYS; }
1075#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001076
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001077/* TODO */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001078static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001079{
1080 return 0;
1081}
1082
1083/* RPC strerror analogs are terminally idiotic:
1084 * *mandatory* prefix and \n at end.
1085 * This hopefully helps. Usage:
1086 * error_msg_rpc(clnt_*error*(" ")) */
1087static void error_msg_rpc(const char *msg)
1088{
Denis Vlasenko23514fe2006-09-19 14:07:52 +00001089 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001090 while (msg[0] == ' ' || msg[0] == ':') msg++;
1091 len = strlen(msg);
1092 while (len && msg[len-1] == '\n') len--;
1093 bb_error_msg("%.*s", len, msg);
1094}
1095
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001096/* NB: mp->xxx fields may be trashed on exit */
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001097static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001098{
1099 CLIENT *mclient;
1100 char *hostname;
1101 char *pathname;
1102 char *mounthost;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001103 /* prior to 2.6.23, kernel took NFS options in a form of this struct
1104 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1105 * then data pointer is interpreted as a string. */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001106 struct nfs_mount_data data;
1107 char *opt;
1108 struct hostent *hp;
1109 struct sockaddr_in server_addr;
1110 struct sockaddr_in mount_server_addr;
1111 int msock, fsock;
1112 union {
1113 struct fhstatus nfsv2;
1114 struct mountres3 nfsv3;
1115 } status;
1116 int daemonized;
1117 char *s;
1118 int port;
1119 int mountport;
1120 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001121#if BB_MMU
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001122 smallint bg = 0;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001123#else
1124 enum { bg = 0 };
1125#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001126 int retry;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001127 int mountprog;
1128 int mountvers;
1129 int nfsprog;
1130 int nfsvers;
1131 int retval;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001132 /* these all are one-bit really. gcc 4.3.1 likes this combination: */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001133 smallint tcp;
1134 smallint soft;
1135 int intr;
1136 int posix;
1137 int nocto;
1138 int noac;
1139 int nordirplus;
1140 int nolock;
Dave Lovefae473c2011-11-10 15:19:25 +01001141 int noacl;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001142
1143 find_kernel_nfs_mount_version();
1144
1145 daemonized = 0;
1146 mounthost = NULL;
1147 retval = ETIMEDOUT;
1148 msock = fsock = -1;
1149 mclient = NULL;
1150
1151 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1152
1153 filteropts = xstrdup(filteropts); /* going to trash it later... */
1154
1155 hostname = xstrdup(mp->mnt_fsname);
1156 /* mount_main() guarantees that ':' is there */
1157 s = strchr(hostname, ':');
1158 pathname = s + 1;
1159 *s = '\0';
1160 /* Ignore all but first hostname in replicated mounts
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001161 * until they can be fully supported. (mack@sgi.com) */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001162 s = strchr(hostname, ',');
1163 if (s) {
1164 *s = '\0';
1165 bb_error_msg("warning: multiple hostnames not supported");
1166 }
1167
1168 server_addr.sin_family = AF_INET;
1169 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1170 hp = gethostbyname(hostname);
1171 if (hp == NULL) {
1172 bb_herror_msg("%s", hostname);
1173 goto fail;
1174 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001175 if (hp->h_length != (int)sizeof(struct in_addr)) {
1176 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001177 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001178 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001179 }
1180
1181 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1182
1183 /* add IP address to mtab options for use when unmounting */
1184
1185 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1186 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1187 } else {
1188 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1189 mp->mnt_opts[0] ? "," : "",
1190 inet_ntoa(server_addr.sin_addr));
1191 free(mp->mnt_opts);
1192 mp->mnt_opts = tmp;
1193 }
1194
1195 /* Set default options.
1196 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1197 * let the kernel decide.
1198 * timeo is filled in after we know whether it'll be TCP or UDP. */
1199 memset(&data, 0, sizeof(data));
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001200 data.retrans = 3;
1201 data.acregmin = 3;
1202 data.acregmax = 60;
1203 data.acdirmin = 30;
1204 data.acdirmax = 60;
1205 data.namlen = NAME_MAX;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001206
Denis Vlasenko25098f72006-09-14 15:46:33 +00001207 soft = 0;
1208 intr = 0;
1209 posix = 0;
1210 nocto = 0;
1211 nolock = 0;
1212 noac = 0;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001213 nordirplus = 0;
Dave Lovefae473c2011-11-10 15:19:25 +01001214 noacl = 0;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001215 retry = 10000; /* 10000 minutes ~ 1 week */
Bernhard Reutner-Fischer88206292011-05-04 19:03:30 +02001216 tcp = 1; /* nfs-utils uses tcp per default */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001217
1218 mountprog = MOUNTPROG;
1219 mountvers = 0;
1220 port = 0;
1221 mountport = 0;
1222 nfsprog = 100003;
1223 nfsvers = 0;
1224
1225 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001226 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001227 char *opteq = strchr(opt, '=');
1228 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001229 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001230 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001231 /* 0 */ "rsize\0"
1232 /* 1 */ "wsize\0"
1233 /* 2 */ "timeo\0"
1234 /* 3 */ "retrans\0"
1235 /* 4 */ "acregmin\0"
1236 /* 5 */ "acregmax\0"
1237 /* 6 */ "acdirmin\0"
1238 /* 7 */ "acdirmax\0"
1239 /* 8 */ "actimeo\0"
1240 /* 9 */ "retry\0"
1241 /* 10 */ "port\0"
1242 /* 11 */ "mountport\0"
1243 /* 12 */ "mounthost\0"
1244 /* 13 */ "mountprog\0"
1245 /* 14 */ "mountvers\0"
1246 /* 15 */ "nfsprog\0"
1247 /* 16 */ "nfsvers\0"
1248 /* 17 */ "vers\0"
1249 /* 18 */ "proto\0"
1250 /* 19 */ "namlen\0"
1251 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001252
1253 *opteq++ = '\0';
1254 idx = index_in_strings(options, opt);
1255 switch (idx) {
1256 case 12: // "mounthost"
1257 mounthost = xstrndup(opteq,
1258 strcspn(opteq, " \t\n\r,"));
1259 continue;
1260 case 18: // "proto"
1261 if (!strncmp(opteq, "tcp", 3))
1262 tcp = 1;
1263 else if (!strncmp(opteq, "udp", 3))
1264 tcp = 0;
1265 else
1266 bb_error_msg("warning: unrecognized proto= option");
1267 continue;
1268 case 20: // "addr" - ignore
1269 continue;
Peter Korsgaard301fe502011-02-21 17:52:13 +01001270 case -1: // unknown
1271 if (vfsflags & MS_REMOUNT)
1272 continue;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001273 }
1274
Denys Vlasenko77832482010-08-12 14:14:45 +02001275 val = xatoi_positive(opteq);
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001276 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001277 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001278 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001279 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001280 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001281 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001282 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001283 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001284 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001285 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001286 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001287 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001288 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001289 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001290 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001291 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001292 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001293 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001294 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001295 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001296 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001297 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001298 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001299 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001300 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001301 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001302 data.acregmin = val;
1303 data.acregmax = val;
1304 data.acdirmin = val;
1305 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001306 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001307 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001308 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001309 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001310 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001311 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001312 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001313 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001314 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001315 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001316 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001317 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001318 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001319 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001320 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001321 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001322 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001323 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001324 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001325 case 16: // "nfsvers"
1326 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001327 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001328 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001329 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001330 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001331 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001332 //else
1333 // bb_error_msg("warning: option namlen is not supported\n");
1334 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001335 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001336 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1337 goto fail;
1338 }
1339 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001340 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001341 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001342 "bg\0"
1343 "fg\0"
1344 "soft\0"
1345 "hard\0"
1346 "intr\0"
1347 "posix\0"
1348 "cto\0"
1349 "ac\0"
1350 "tcp\0"
1351 "udp\0"
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001352 "lock\0"
Dave Lovefae473c2011-11-10 15:19:25 +01001353 "rdirplus\0"
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001354 "acl\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001355 int val = 1;
1356 if (!strncmp(opt, "no", 2)) {
1357 val = 0;
1358 opt += 2;
1359 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001360 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001361 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001362#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001363 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001364#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001365 break;
1366 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001367#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001368 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001369#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001370 break;
1371 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001372 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001373 break;
1374 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001375 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001376 break;
1377 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001378 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001379 break;
1380 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001381 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001382 break;
1383 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001384 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001385 break;
1386 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001387 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001388 break;
1389 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001390 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001391 break;
1392 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001393 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001394 break;
1395 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001396 if (nfs_mount_version >= 3)
1397 nolock = !val;
1398 else
1399 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001400 break;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001401 case 11: //rdirplus
1402 nordirplus = !val;
1403 break;
Dave Lovefae473c2011-11-10 15:19:25 +01001404 case 12: // acl
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001405 noacl = !val;
Dave Lovefae473c2011-11-10 15:19:25 +01001406 break;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001407 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001408 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1409 goto fail;
1410 }
1411 }
1412 }
1413 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1414
1415 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1416 | (intr ? NFS_MOUNT_INTR : 0)
1417 | (posix ? NFS_MOUNT_POSIX : 0)
1418 | (nocto ? NFS_MOUNT_NOCTO : 0)
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001419 | (noac ? NFS_MOUNT_NOAC : 0)
Dave Lovefae473c2011-11-10 15:19:25 +01001420 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
Denys Vlasenko56443cd2012-04-20 15:07:22 +02001421 | (noacl ? NFS_MOUNT_NOACL : 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001422 if (nfs_mount_version >= 2)
1423 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1424 if (nfs_mount_version >= 3)
1425 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1426 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1427 bb_error_msg("NFSv%d not supported", nfsvers);
1428 goto fail;
1429 }
1430 if (nfsvers && !mountvers)
1431 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1432 if (nfsvers && nfsvers < mountvers) {
1433 mountvers = nfsvers;
1434 }
1435
1436 /* Adjust options if none specified */
1437 if (!data.timeo)
1438 data.timeo = tcp ? 70 : 7;
1439
Denis Vlasenko25098f72006-09-14 15:46:33 +00001440 data.version = nfs_mount_version;
1441
1442 if (vfsflags & MS_REMOUNT)
1443 goto do_mount;
1444
1445 /*
1446 * If the previous mount operation on the same host was
1447 * backgrounded, and the "bg" for this mount is also set,
1448 * give up immediately, to avoid the initial timeout.
1449 */
1450 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001451 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001452 if (daemonized <= 0) { /* parent or error */
1453 retval = -daemonized;
1454 goto ret;
1455 }
1456 }
1457
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001458 /* Create mount daemon client */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001459 /* See if the nfs host = mount host. */
1460 if (mounthost) {
1461 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1462 mount_server_addr.sin_family = AF_INET;
1463 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1464 } else {
1465 hp = gethostbyname(mounthost);
1466 if (hp == NULL) {
1467 bb_herror_msg("%s", mounthost);
1468 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001469 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001470 if (hp->h_length != (int)sizeof(struct in_addr)) {
1471 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001472 }
1473 mount_server_addr.sin_family = AF_INET;
Denys Vlasenko99069332010-02-27 19:38:19 +01001474 memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001475 }
1476 }
1477
1478 /*
1479 * The following loop implements the mount retries. When the mount
1480 * times out, and the "bg" option is set, we background ourself
1481 * and continue trying.
1482 *
1483 * The case where the mount point is not present and the "bg"
1484 * option is set, is treated as a timeout. This is done to
1485 * support nested mounts.
1486 *
1487 * The "retry" count specified by the user is the number of
1488 * minutes to retry before giving up.
1489 */
1490 {
1491 struct timeval total_timeout;
1492 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001493 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001494 time_t t;
1495 time_t prevt;
1496 time_t timeout;
1497
1498 retry_timeout.tv_sec = 3;
1499 retry_timeout.tv_usec = 0;
1500 total_timeout.tv_sec = 20;
1501 total_timeout.tv_usec = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001502/* FIXME: use monotonic()? */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001503 timeout = time(NULL) + 60 * retry;
1504 prevt = 0;
1505 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001506 retry:
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001507 /* Be careful not to use too many CPU cycles */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001508 if (t - prevt < 30)
1509 sleep(30);
1510
Denis Vlasenkob9256052007-09-28 10:29:17 +00001511 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001512 mountprog,
1513 mountvers,
1514 proto,
1515 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001516 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001517
1518 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001519 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001520 msock = RPC_ANYSOCK;
1521
Denis Vlasenkob9256052007-09-28 10:29:17 +00001522 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001523 case IPPROTO_UDP:
1524 mclient = clntudp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001525 pm_mnt.pm_prog,
1526 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001527 retry_timeout,
1528 &msock);
1529 if (mclient)
1530 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001531 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001532 msock = RPC_ANYSOCK;
1533 case IPPROTO_TCP:
1534 mclient = clnttcp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001535 pm_mnt.pm_prog,
1536 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001537 &msock, 0, 0);
1538 break;
1539 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001540 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001541 }
1542 if (!mclient) {
1543 if (!daemonized && prevt == 0)
1544 error_msg_rpc(clnt_spcreateerror(" "));
1545 } else {
1546 enum clnt_stat clnt_stat;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001547
1548 /* Try to mount hostname:pathname */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001549 mclient->cl_auth = authunix_create_default();
1550
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001551 /* Make pointers in xdr_mountres3 NULL so
Denis Vlasenko25098f72006-09-14 15:46:33 +00001552 * that xdr_array allocates memory for us
1553 */
1554 memset(&status, 0, sizeof(status));
1555
Denis Vlasenkob9256052007-09-28 10:29:17 +00001556 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001557 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1558 (xdrproc_t) xdr_dirpath,
1559 (caddr_t) &pathname,
1560 (xdrproc_t) xdr_mountres3,
1561 (caddr_t) &status,
1562 total_timeout);
1563 else
1564 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1565 (xdrproc_t) xdr_dirpath,
1566 (caddr_t) &pathname,
1567 (xdrproc_t) xdr_fhstatus,
1568 (caddr_t) &status,
1569 total_timeout);
1570
1571 if (clnt_stat == RPC_SUCCESS)
1572 goto prepare_kernel_data; /* we're done */
1573 if (errno != ECONNREFUSED) {
1574 error_msg_rpc(clnt_sperror(mclient, " "));
1575 goto fail; /* don't retry */
1576 }
1577 /* Connection refused */
1578 if (!daemonized && prevt == 0) /* print just once */
1579 error_msg_rpc(clnt_sperror(mclient, " "));
1580 auth_destroy(mclient->cl_auth);
1581 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001582 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001583 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001584 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001585 }
1586
1587 /* Timeout. We are going to retry... maybe */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001588 if (!bg)
1589 goto fail;
1590 if (!daemonized) {
1591 daemonized = daemonize();
1592 if (daemonized <= 0) { /* parent or error */
1593 retval = -daemonized;
1594 goto ret;
1595 }
1596 }
1597 prevt = t;
1598 t = time(NULL);
1599 if (t >= timeout)
1600 /* TODO error message */
1601 goto fail;
1602
1603 goto retry;
1604 }
1605
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001606 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001607
1608 if (nfsvers == 2) {
1609 if (status.nfsv2.fhs_status != 0) {
1610 bb_error_msg("%s:%s failed, reason given by server: %s",
1611 hostname, pathname,
1612 nfs_strerror(status.nfsv2.fhs_status));
1613 goto fail;
1614 }
1615 memcpy(data.root.data,
1616 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1617 NFS_FHSIZE);
1618 data.root.size = NFS_FHSIZE;
1619 memcpy(data.old_root.data,
1620 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1621 NFS_FHSIZE);
1622 } else {
1623 fhandle3 *my_fhandle;
1624 if (status.nfsv3.fhs_status != 0) {
1625 bb_error_msg("%s:%s failed, reason given by server: %s",
1626 hostname, pathname,
1627 nfs_strerror(status.nfsv3.fhs_status));
1628 goto fail;
1629 }
1630 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1631 memset(data.old_root.data, 0, NFS_FHSIZE);
1632 memset(&data.root, 0, sizeof(data.root));
1633 data.root.size = my_fhandle->fhandle3_len;
1634 memcpy(data.root.data,
1635 (char *) my_fhandle->fhandle3_val,
1636 my_fhandle->fhandle3_len);
1637
1638 data.flags |= NFS_MOUNT_VER3;
1639 }
1640
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001641 /* Create nfs socket for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001642 if (tcp) {
1643 if (nfs_mount_version < 3) {
1644 bb_error_msg("NFS over TCP is not supported");
1645 goto fail;
1646 }
1647 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1648 } else
1649 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1650 if (fsock < 0) {
1651 bb_perror_msg("nfs socket");
1652 goto fail;
1653 }
1654 if (bindresvport(fsock, 0) < 0) {
1655 bb_perror_msg("nfs bindresvport");
1656 goto fail;
1657 }
1658 if (port == 0) {
1659 server_addr.sin_port = PMAPPORT;
1660 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1661 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1662 if (port == 0)
1663 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001664 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001665 server_addr.sin_port = htons(port);
1666
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001667 /* Prepare data structure for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001668 data.fd = fsock;
1669 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1670 strncpy(data.hostname, hostname, sizeof(data.hostname));
1671
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001672 /* Clean up */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001673 auth_destroy(mclient->cl_auth);
1674 clnt_destroy(mclient);
1675 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001676 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001677
1678 if (bg) {
1679 /* We must wait until mount directory is available */
1680 struct stat statbuf;
1681 int delay = 1;
1682 while (stat(mp->mnt_dir, &statbuf) == -1) {
1683 if (!daemonized) {
1684 daemonized = daemonize();
1685 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001686/* FIXME: parent doesn't close fsock - ??! */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001687 retval = -daemonized;
1688 goto ret;
1689 }
1690 }
1691 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1692 delay *= 2;
1693 if (delay > 30)
1694 delay = 30;
1695 }
1696 }
1697
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001698 /* Perform actual mount */
1699 do_mount:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001700 retval = mount_it_now(mp, vfsflags, (char*)&data);
1701 goto ret;
1702
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001703 /* Abort */
1704 fail:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001705 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001706 if (mclient) {
1707 auth_destroy(mclient->cl_auth);
1708 clnt_destroy(mclient);
1709 }
1710 close(msock);
1711 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001712 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001713 close(fsock);
1714
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001715 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001716 free(hostname);
1717 free(mounthost);
1718 free(filteropts);
1719 return retval;
1720}
1721
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001722#else // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001723
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001724/* Linux 2.6.23+ supports nfs mounts with options passed as a string.
1725 * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
1726 * (However, note that then you lose any chances that NFS over IPv6 would work).
1727 */
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001728static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001729{
1730 len_and_sockaddr *lsa;
1731 char *opts;
1732 char *end;
1733 char *dotted;
1734 int ret;
1735
1736# if ENABLE_FEATURE_IPV6
1737 end = strchr(mp->mnt_fsname, ']');
1738 if (end && end[1] == ':')
1739 end++;
1740 else
1741# endif
1742 /* mount_main() guarantees that ':' is there */
1743 end = strchr(mp->mnt_fsname, ':');
1744
1745 *end = '\0';
Denys Vlasenko39b23312011-11-10 17:01:39 +01001746 lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001747 *end = ':';
1748 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1749 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1750 opts = xasprintf("%s%saddr=%s",
1751 filteropts ? filteropts : "",
1752 filteropts ? "," : "",
1753 dotted
1754 );
1755 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1756 ret = mount_it_now(mp, vfsflags, opts);
1757 if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1758
1759 return ret;
1760}
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001761
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001762#endif // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001763
1764// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1765// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001766// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001767static int singlemount(struct mntent *mp, int ignore_busy)
1768{
Denis Vlasenkob4133682008-02-18 13:05:38 +00001769 int rc = -1;
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001770 unsigned long vfsflags;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001771 char *loopFile = NULL, *filteropts = NULL;
1772 llist_t *fl = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001773 struct stat st;
1774
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001775 errno = 0;
1776
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001777 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1778
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001779 // Treat fstype "auto" as unspecified
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001780 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1781 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001782
Denis Vlasenko2535f122007-09-15 13:28:30 +00001783 // Might this be a virtual filesystem?
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001784 if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1785 char *args[35];
1786 char *s;
1787 int n;
1788 // fsname: "cmd#arg1#arg2..."
1789 // WARNING: allows execution of arbitrary commands!
1790 // Try "mount 'sh#-c#sh' bogus_dir".
1791 // It is safe ONLY because non-root
1792 // cannot use two-argument mount command
1793 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1794 // "mount: can't find sh#-c#sh in /etc/fstab"
1795 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1796
1797 s = mp->mnt_fsname;
1798 n = 0;
1799 args[n++] = s;
1800 while (*s && n < 35 - 2) {
1801 if (*s++ == '#' && *s != '#') {
1802 s[-1] = '\0';
1803 args[n++] = s;
Denis Vlasenko2535f122007-09-15 13:28:30 +00001804 }
1805 }
Denis Vlasenko2535f122007-09-15 13:28:30 +00001806 args[n++] = mp->mnt_dir;
1807 args[n] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +01001808 rc = spawn_and_wait(args);
Denis Vlasenko2535f122007-09-15 13:28:30 +00001809 goto report_error;
1810 }
1811
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001812 // Might this be an CIFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001813 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001814 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1815 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1816 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001817 ) {
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001818 int len;
1819 char c;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001820 len_and_sockaddr *lsa;
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001821 char *hostname, *dotted, *ip;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001822
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001823 hostname = mp->mnt_fsname + 2;
1824 len = strcspn(hostname, "/\\");
1825 if (len == 0 || hostname[len] == '\0')
Denis Vlasenko5c329932009-04-12 12:16:21 +00001826 goto report_error;
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001827 c = hostname[len];
1828 hostname[len] = '\0';
1829 lsa = host2sockaddr(hostname, 0);
1830 hostname[len] = c;
Denis Vlasenko5c329932009-04-12 12:16:21 +00001831 if (!lsa)
1832 goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001833
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001834 // Insert "ip=..." option into options
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001835 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001836 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001837 ip = xasprintf("ip=%s", dotted);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001838 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001839 parse_mount_options(ip, &filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001840 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001841
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001842 // "-o mand" is required [why?]
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001843 vfsflags |= MS_MANDLOCK;
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001844 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001845 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001846
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001847 goto report_error;
1848 }
1849
1850 // Might this be an NFS filesystem?
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001851 if ((!mp->mnt_type || strncmp(mp->mnt_type, "nfs", 3) == 0)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001852 && strchr(mp->mnt_fsname, ':') != NULL
1853 ) {
Denys Vlasenkoa86e0242011-11-10 16:53:35 +01001854 if (!mp->mnt_type)
1855 mp->mnt_type = (char*)"nfs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001856 rc = nfsmount(mp, vfsflags, filteropts);
1857 goto report_error;
1858 }
1859
1860 // Look at the file. (Not found isn't a failure for remount, or for
1861 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001862 // (We use stat, not lstat, in order to allow
1863 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001864 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001865 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1866 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001867 // Do we need to allocate a loopback device for it?
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001868 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1869 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001870 mp->mnt_fsname = NULL; // will receive malloced loop dev name
Denys Vlasenko9ee42662012-06-21 12:08:56 +02001871 if (set_loop(&mp->mnt_fsname, loopFile, 0, /*ro:*/ (vfsflags & MS_RDONLY)) < 0) {
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001872 if (errno == EPERM || errno == EACCES)
1873 bb_error_msg(bb_msg_perm_denied_are_you_root);
1874 else
Denys Vlasenko6331cf02009-11-13 09:08:27 +01001875 bb_perror_msg("can't setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001876 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001877 }
1878
1879 // Autodetect bind mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001880 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1881 vfsflags |= MS_BIND;
1882 }
1883
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001884 // If we know the fstype (or don't need to), jump straight
1885 // to the actual mount.
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001886 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +01001887 char *next;
1888 for (;;) {
1889 next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
1890 if (next)
1891 *next = '\0';
1892 rc = mount_it_now(mp, vfsflags, filteropts);
1893 if (rc == 0 || !next)
1894 break;
1895 mp->mnt_type = next + 1;
1896 }
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001897 } else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001898 // Loop through filesystem types until mount succeeds
1899 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001900
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001901 // Initialize list of block backed filesystems.
1902 // This has to be done here so that during "mount -a",
1903 // mounts after /proc shows up can autodetect.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001904 if (!fslist) {
1905 fslist = get_block_backed_filesystems();
1906 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1907 atexit(delete_block_backed_filesystems);
1908 }
1909
1910 for (fl = fslist; fl; fl = fl->link) {
1911 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001912 rc = mount_it_now(mp, vfsflags, filteropts);
Karol Lewandowskib5ebe5f2011-11-03 10:02:31 +01001913 if (rc == 0)
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001914 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001915 }
1916 }
1917
1918 // If mount failed, clean up loop file (if any).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001919 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1920 del_loop(mp->mnt_fsname);
1921 if (ENABLE_FEATURE_CLEAN_UP) {
1922 free(loopFile);
1923 free(mp->mnt_fsname);
1924 }
1925 }
1926
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001927 report_error:
1928 if (ENABLE_FEATURE_CLEAN_UP)
1929 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001930
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001931 if (errno == EBUSY && ignore_busy)
1932 return 0;
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001933 if (rc != 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001934 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001935 return rc;
1936}
1937
Michael Abbott6b5accb2009-12-04 03:33:07 +01001938// -O support
1939// -O interprets a list of filter options which select whether a mount
1940// point will be mounted: only mounts with options matching *all* filtering
1941// options will be selected.
1942// By default each -O filter option must be present in the list of mount
1943// options, but if it is prefixed by "no" then it must be absent.
1944// For example,
1945// -O a,nob,c matches -o a,c but fails to match -o a,b,c
1946// (and also fails to match -o a because -o c is absent).
1947//
1948// It is different from -t in that each option is matched exactly; a leading
1949// "no" at the beginning of one option does not negate the rest.
1950static int match_opt(const char *fs_opt_in, const char *O_opt)
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001951{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001952 if (!O_opt)
Michael Abbott6b5accb2009-12-04 03:33:07 +01001953 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001954
Michael Abbott6b5accb2009-12-04 03:33:07 +01001955 while (*O_opt) {
1956 const char *fs_opt = fs_opt_in;
1957 int O_len;
1958 int match;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001959
Michael Abbott6b5accb2009-12-04 03:33:07 +01001960 // If option begins with "no" then treat as an inverted match:
1961 // matching is a failure
1962 match = 0;
1963 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
1964 match = 1;
1965 O_opt += 2;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001966 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01001967 // Isolate the current O option
1968 O_len = strchrnul(O_opt, ',') - O_opt;
1969 // Check for a match against existing options
1970 while (1) {
1971 if (strncmp(fs_opt, O_opt, O_len) == 0
1972 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
1973 ) {
1974 if (match)
1975 return 0; // "no" prefix, but option found
1976 match = 1; // current O option found, go check next one
1977 break;
1978 }
1979 fs_opt = strchr(fs_opt, ',');
1980 if (!fs_opt)
1981 break;
1982 fs_opt++;
1983 }
1984 if (match == 0)
1985 return 0; // match wanted but not found
1986 if (O_opt[O_len] == '\0') // end?
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001987 break;
Michael Abbott6b5accb2009-12-04 03:33:07 +01001988 // Step to the next O option
1989 O_opt += O_len + 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001990 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01001991 // If we get here then everything matched
1992 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001993}
1994
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001995// Parse options, if necessary parse fstab/mtab, and call singlemount for
1996// each directory to be mounted.
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001997int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001998int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001999{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002000 char *cmdopts = xzalloc(1);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002001 char *fstype = NULL;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002002 char *O_optmatch = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002003 char *storage_path;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00002004 llist_t *lst_o = NULL;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00002005 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002006 FILE *fstab;
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002007 int i, j;
2008 int rc = EXIT_SUCCESS;
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002009 unsigned long cmdopt_flags;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00002010 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002011 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002012 IF_NOT_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002013
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002014 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00002015
Denys Vlasenko16714242011-09-21 01:59:15 +02002016 INIT_G();
2017
Denis Vlasenkof732e962008-02-18 12:07:49 +00002018 // Parse long options, like --bind and --move. Note that -o option
2019 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002020 for (i = j = 1; argv[i]; i++) {
2021 if (argv[i][0] == '-' && argv[i][1] == '-')
2022 append_mount_options(&cmdopts, argv[i] + 2);
2023 else
2024 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002025 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002026 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002027
2028 // Parse remaining options
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002029 // Max 2 params; -o is a list, -v is a counter
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002030 opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002031 opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002032 IF_FEATURE_MOUNT_VERBOSE(, &verbose));
Denis Vlasenkof9dde912008-10-18 19:15:57 +00002033 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00002034 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2035 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00002036 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002037
2038 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002039 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00002040 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002041 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2042
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002043 if (!mountTable)
2044 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002045
Denis Vlasenko2535f122007-09-15 13:28:30 +00002046 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002047 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002048 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002049 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00002050 // util-linux 2.12a happily shows rootfs...
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002051 //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002052
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002053 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002054 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2055 mtpair->mnt_dir, mtpair->mnt_type,
2056 mtpair->mnt_opts);
2057 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002058 if (ENABLE_FEATURE_CLEAN_UP)
2059 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002060 return EXIT_SUCCESS;
2061 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002062 storage_path = NULL;
2063 } else {
2064 // When we have two arguments, the second is the directory and we can
2065 // skip looking at fstab entirely. We can always abspath() the directory
2066 // argument when we get it.
2067 if (argv[1]) {
2068 if (nonroot)
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002069 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002070 mtpair->mnt_fsname = argv[0];
2071 mtpair->mnt_dir = argv[1];
2072 mtpair->mnt_type = fstype;
2073 mtpair->mnt_opts = cmdopts;
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002074 resolve_mount_spec(&mtpair->mnt_fsname);
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002075 rc = singlemount(mtpair, /*ignore_busy:*/ 0);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002076 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00002077 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002078 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002079 }
2080
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002081 // Past this point, we are handling either "mount -a [opts]"
2082 // or "mount [opts] single_param"
2083
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002084 cmdopt_flags = parse_mount_options(cmdopts, NULL);
2085 if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002086 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00002087
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002088 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002089 if (ENABLE_FEATURE_MOUNT_FLAGS
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002090 && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002091 ) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002092 // verbose_mount(source, target, type, flags, data)
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002093 rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002094 if (rc)
2095 bb_simple_perror_msg_and_die(argv[0]);
2096 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002097 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00002098
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002099 // Open either fstab or mtab
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002100 fstabname = "/etc/fstab";
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002101 if (cmdopt_flags & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002102 // WARNING. I am not sure this matches util-linux's
2103 // behavior. It's possible util-linux does not
2104 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002105 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002106 }
2107 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00002108 if (!fstab)
Denys Vlasenko651a2692010-03-23 16:25:17 +01002109 bb_perror_msg_and_die("can't read '%s'", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002110
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002111 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00002112 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002113 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002114 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002115
2116 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00002117 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002118 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002119 GETMNTENT_BUFSIZE/2)
2120 ) { // End of fstab/mtab is reached
2121 mtcur = mtother; // the thing we found last time
2122 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002123 }
2124
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002125 // If we're trying to mount something specific and this isn't it,
2126 // skip it. Note we must match the exact text in fstab (ala
2127 // "proc") or a full path from root
2128 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002129
2130 // Is this what we're looking for?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002131 if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2132 && strcmp(storage_path, mtcur->mnt_fsname) != 0
2133 && strcmp(argv[0], mtcur->mnt_dir) != 0
2134 && strcmp(storage_path, mtcur->mnt_dir) != 0
2135 ) {
2136 continue; // no
2137 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002138
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002139 // Remember this entry. Something later may have
2140 // overmounted it, and we want the _last_ match.
2141 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002142
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002143 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002144 } else {
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002145 struct mntent *mp;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002146 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002147 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002148 if (nonroot)
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002149 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002150
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002151 // Does type match? (NULL matches always)
2152 if (!match_fstype(mtcur, fstype))
2153 continue;
2154
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002155 // Skip noauto and swap anyway
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002156 if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2157 // swap is bogus "fstype", parse_mount_options can't check fstypes
2158 || strcasecmp(mtcur->mnt_type, "swap") == 0
2159 ) {
2160 continue;
2161 }
2162
2163 // Does (at least one) option match?
2164 // (NULL matches always)
2165 if (!match_opt(mtcur->mnt_opts, O_optmatch))
2166 continue;
2167
2168 resolve_mount_spec(&mtcur->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002169
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002170 // NFS mounts want this to be xrealloc-able
2171 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002172
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002173 // If nothing is mounted on this directory...
2174 // (otherwise repeated "mount -a" mounts everything again)
2175 mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2176 // We do not check fsname match of found mount point -
2177 // "/" may have fsname of "/dev/root" while fstab
2178 // says "/dev/something_else".
2179 if (mp) {
Denys Vlasenko86566762009-12-10 21:32:28 +01002180 if (verbose) {
2181 bb_error_msg("according to %s, "
2182 "%s is already mounted on %s",
2183 bb_path_mtab_file,
2184 mp->mnt_fsname, mp->mnt_dir);
2185 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002186 } else {
2187 // ...mount this thing
2188 if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2189 // Count number of failed mounts
2190 rc++;
2191 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002192 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002193 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002194 }
2195 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002196
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002197 // End of fstab/mtab is reached.
2198 // Were we looking for something specific?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002199 if (argv[0]) { // yes
Denys Vlasenko9ee42662012-06-21 12:08:56 +02002200 unsigned long l;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002201
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002202 // If we didn't find anything, complain
2203 if (!mtcur->mnt_fsname)
2204 bb_error_msg_and_die("can't find %s in %s",
2205 argv[0], fstabname);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002206
2207 // What happens when we try to "mount swap_partition"?
2208 // (fstab containts "swap_partition swap swap defaults 0 0")
2209 // util-linux-ng 2.13.1 does this:
2210 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2211 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2212 // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory)
2213 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2214 // exit_group(32) = ?
2215#if 0
2216 // In case we want to simply skip swap partitions:
2217 l = parse_mount_options(mtcur->mnt_opts, NULL);
2218 if ((l & MOUNT_SWAP)
2219 // swap is bogus "fstype", parse_mount_options can't check fstypes
2220 || strcasecmp(mtcur->mnt_type, "swap") == 0
2221 ) {
2222 goto ret;
2223 }
2224#endif
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002225 if (nonroot) {
2226 // fstab must have "users" or "user"
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002227 l = parse_mount_options(mtcur->mnt_opts, NULL);
2228 if (!(l & MOUNT_USERS))
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002229 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002230 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002231
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002232 //util-linux-2.12 does not do this check.
2233 //// If nothing is mounted on this directory...
2234 //// (otherwise repeated "mount FOO" mounts FOO again)
2235 //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2236 //if (mp) {
2237 // bb_error_msg("according to %s, "
2238 // "%s is already mounted on %s",
2239 // bb_path_mtab_file,
2240 // mp->mnt_fsname, mp->mnt_dir);
2241 //} else {
2242 // ...mount the last thing we found
2243 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2244 append_mount_options(&(mtcur->mnt_opts), cmdopts);
2245 resolve_mount_spec(&mtpair->mnt_fsname);
2246 rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2247 if (ENABLE_FEATURE_CLEAN_UP)
2248 free(mtcur->mnt_opts);
2249 //}
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002250 }
2251
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002252 //ret:
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002253 if (ENABLE_FEATURE_CLEAN_UP)
2254 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002255 if (ENABLE_FEATURE_CLEAN_UP) {
2256 free(storage_path);
2257 free(cmdopts);
2258 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002259
2260//TODO: exitcode should be ORed mask of (from "man mount"):
2261// 0 success
2262// 1 incorrect invocation or permissions
2263// 2 system error (out of memory, cannot fork, no more loop devices)
2264// 4 internal mount bug or missing nfs support in mount
2265// 8 user interrupt
2266//16 problems writing or locking /etc/mtab
2267//32 mount failure
2268//64 some mount succeeded
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002269 return rc;
2270}