blob: ca04de79ded3bb52d5db69a1b81100af831f0d99 [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)"
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +020041//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
Eric Andersenbd22ed82000-07-08 18:55:24 +0000116
Denys Vlasenko102ff762009-11-21 17:14:08 +0100117#include "libbb.h"
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000118#if ENABLE_FEATURE_MOUNT_LABEL
Natanael Copa9aff2992009-09-20 04:28:22 +0200119# include "volume_id.h"
120#else
121# define resolve_mount_spec(fsname) ((void)0)
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000122#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000123
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000124// Needed for nfs support only
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000125#include <sys/utsname.h>
126#undef TRUE
127#undef FALSE
Denys Vlasenkocc428142009-12-16 02:06:56 +0100128#if ENABLE_FEATURE_MOUNT_NFS
129/* This is just a warning of a common mistake. Possibly this should be a
130 * uclibc faq entry rather than in busybox... */
131# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
132# error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
133# endif
134# include <rpc/rpc.h>
135# include <rpc/pmap_prot.h>
136# include <rpc/pmap_clnt.h>
137#endif
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000138
139
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000140#if defined(__dietlibc__)
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000141// 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
142// dietlibc-0.30 does not have implementation of getmntent_r()
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +0000143static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000144 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000145{
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000146 struct mntent* ment = getmntent(stream);
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +0000147 return memcpy(result, ment, sizeof(*ment));
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000148}
149#endif
150
151
Rob Landleydc0955b2006-03-14 18:16:25 +0000152// Not real flags, but we want to be able to check for this.
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000153enum {
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000154 MOUNT_USERS = (1 << 28) * ENABLE_DESKTOP,
155 MOUNT_NOAUTO = (1 << 29),
156 MOUNT_SWAP = (1 << 30),
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000157};
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000158
159
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +0000160#define OPTION_STR "o:t:rwanfvsiO:"
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000161enum {
162 OPT_o = (1 << 0),
163 OPT_t = (1 << 1),
164 OPT_r = (1 << 2),
165 OPT_w = (1 << 3),
166 OPT_a = (1 << 4),
167 OPT_n = (1 << 5),
168 OPT_f = (1 << 6),
169 OPT_v = (1 << 7),
170 OPT_s = (1 << 8),
171 OPT_i = (1 << 9),
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +0000172 OPT_O = (1 << 10),
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000173};
174
175#if ENABLE_FEATURE_MTAB_SUPPORT
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200176#define USE_MTAB (!(option_mask32 & OPT_n))
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000177#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200178#define USE_MTAB 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000179#endif
180
181#if ENABLE_FEATURE_MOUNT_FAKE
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200182#define FAKE_IT (option_mask32 & OPT_f)
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000183#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200184#define FAKE_IT 0
185#endif
186
187#if ENABLE_FEATURE_MOUNT_HELPERS
188#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
189#else
190#define HELPERS_ALLOWED 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000191#endif
192
193
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000194// TODO: more "user" flag compatibility.
195// "user" option (from mount manpage):
196// Only the user that mounted a filesystem can unmount it again.
197// If any user should be able to unmount, then use users instead of user
198// in the fstab line. The owner option is similar to the user option,
199// with the restriction that the user must be the owner of the special file.
200// This may be useful e.g. for /dev/fd if a login script makes
201// the console user owner of this device.
Rob Landley3ba7bd12006-08-09 19:51:13 +0000202
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000203// Standard mount options (from -o options or --options),
204// with corresponding flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000205static const int32_t mount_options[] = {
Rob Landleye3781b72006-08-08 01:39:49 +0000206 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
Rob Landleydc0955b2006-03-14 18:16:25 +0000207
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000208 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000209 /* "loop" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000210 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000211
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000212 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000213 /* "defaults" */ 0,
214 /* "quiet" 0 - do not filter out, vfat wants to see it */
215 /* "noauto" */ MOUNT_NOAUTO,
216 /* "sw" */ MOUNT_SWAP,
217 /* "swap" */ MOUNT_SWAP,
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000218 IF_DESKTOP(/* "user" */ MOUNT_USERS,)
219 IF_DESKTOP(/* "users" */ MOUNT_USERS,)
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000220 /* "_netdev" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000221 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000222
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000223 IF_FEATURE_MOUNT_FLAGS(
Rob Landleye3781b72006-08-08 01:39:49 +0000224 // vfs flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000225 /* "nosuid" */ MS_NOSUID,
226 /* "suid" */ ~MS_NOSUID,
227 /* "dev" */ ~MS_NODEV,
228 /* "nodev" */ MS_NODEV,
229 /* "exec" */ ~MS_NOEXEC,
230 /* "noexec" */ MS_NOEXEC,
231 /* "sync" */ MS_SYNCHRONOUS,
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +0000232 /* "dirsync" */ MS_DIRSYNC,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000233 /* "async" */ ~MS_SYNCHRONOUS,
234 /* "atime" */ ~MS_NOATIME,
235 /* "noatime" */ MS_NOATIME,
236 /* "diratime" */ ~MS_NODIRATIME,
237 /* "nodiratime" */ MS_NODIRATIME,
Denis Vlasenko580ce2d2008-07-08 02:56:53 +0000238 /* "mand" */ MS_MANDLOCK,
239 /* "nomand" */ ~MS_MANDLOCK,
Bernhard Reutner-Fischerfb5902c2008-08-06 18:14:38 +0000240 /* "relatime" */ MS_RELATIME,
241 /* "norelatime" */ ~MS_RELATIME,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000242 /* "loud" */ ~MS_SILENT,
Roman Borisov19311bf2011-03-24 15:08:43 +0300243 /* "rbind" */ MS_BIND|MS_RECURSIVE,
Eric Andersen9601a1c2006-03-20 18:07:50 +0000244
Rob Landleye3781b72006-08-08 01:39:49 +0000245 // action flags
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200246 /* "union" */ MS_UNION,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000247 /* "bind" */ MS_BIND,
248 /* "move" */ MS_MOVE,
249 /* "shared" */ MS_SHARED,
250 /* "slave" */ MS_SLAVE,
251 /* "private" */ MS_PRIVATE,
252 /* "unbindable" */ MS_UNBINDABLE,
253 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
254 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
Roman Borisovd3679d22011-03-23 11:20:25 +0300255 /* "rprivate" */ MS_PRIVATE|MS_RECURSIVE,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000256 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
Rob Landleye3781b72006-08-08 01:39:49 +0000257 )
258
259 // Always understood.
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000260 /* "ro" */ MS_RDONLY, // vfs flag
261 /* "rw" */ ~MS_RDONLY, // vfs flag
262 /* "remount" */ MS_REMOUNT // action flag
Eric Andersencc8ed391999-10-05 16:24:54 +0000263};
264
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000265static const char mount_option_str[] =
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000266 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000267 "loop\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000268 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000269 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000270 "defaults\0"
271 // "quiet\0" - do not filter out, vfat wants to see it
272 "noauto\0"
273 "sw\0"
274 "swap\0"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000275 IF_DESKTOP("user\0")
276 IF_DESKTOP("users\0")
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000277 "_netdev\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000278 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000279 IF_FEATURE_MOUNT_FLAGS(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000280 // vfs flags
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000281 "nosuid\0"
282 "suid\0"
283 "dev\0"
284 "nodev\0"
285 "exec\0"
286 "noexec\0"
287 "sync\0"
288 "dirsync\0"
289 "async\0"
290 "atime\0"
291 "noatime\0"
292 "diratime\0"
293 "nodiratime\0"
294 "mand\0"
295 "nomand\0"
296 "relatime\0"
297 "norelatime\0"
298 "loud\0"
Roman Borisov19311bf2011-03-24 15:08:43 +0300299 "rbind\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000300
301 // action flags
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200302 "union\0"
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000303 "bind\0"
304 "move\0"
Roman Borisov945fd172011-02-25 14:50:39 +0300305 "make-shared\0"
306 "make-slave\0"
307 "make-private\0"
308 "make-unbindable\0"
309 "make-rshared\0"
310 "make-rslave\0"
311 "make-rprivate\0"
312 "make-runbindable\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000313 )
314
315 // Always understood.
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000316 "ro\0" // vfs flag
317 "rw\0" // vfs flag
318 "remount\0" // action flag
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000319;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000320
Denis Vlasenkof732e962008-02-18 12:07:49 +0000321
322struct globals {
323#if ENABLE_FEATURE_MOUNT_NFS
324 smalluint nfs_mount_version;
325#endif
326#if ENABLE_FEATURE_MOUNT_VERBOSE
327 unsigned verbose;
328#endif
329 llist_t *fslist;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000330 char getmntent_buf[1];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100331} FIX_ALIASING;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000332enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
Denis Vlasenkof732e962008-02-18 12:07:49 +0000333#define G (*(struct globals*)&bb_common_bufsiz1)
334#define nfs_mount_version (G.nfs_mount_version)
Denis Vlasenkob4133682008-02-18 13:05:38 +0000335#if ENABLE_FEATURE_MOUNT_VERBOSE
Denis Vlasenkof732e962008-02-18 12:07:49 +0000336#define verbose (G.verbose )
Denis Vlasenkob4133682008-02-18 13:05:38 +0000337#else
338#define verbose 0
339#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000340#define fslist (G.fslist )
341#define getmntent_buf (G.getmntent_buf )
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200342#define INIT_G() do { } while (0)
Denis Vlasenkof732e962008-02-18 12:07:49 +0000343
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100344#if ENABLE_FEATURE_MTAB_SUPPORT
345/*
346 * update_mtab_entry_on_move() is used to update entry in case of mount --move.
347 * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
348 * input mntent and replace it by new one.
349 */
350static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
351{
352 struct mntent *entries, *m;
353 int i, count;
354 FILE *mountTable;
355
356 mountTable = setmntent(bb_path_mtab_file, "r");
357 if (!mountTable) {
358 bb_perror_msg(bb_path_mtab_file);
359 return;
360 }
361
362 entries = NULL;
363 count = 0;
364 while ((m = getmntent(mountTable)) != NULL) {
365 entries = xrealloc_vector(entries, 3, count);
366 entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
367 entries[count].mnt_dir = xstrdup(m->mnt_dir);
368 entries[count].mnt_type = xstrdup(m->mnt_type);
369 entries[count].mnt_opts = xstrdup(m->mnt_opts);
370 entries[count].mnt_freq = m->mnt_freq;
371 entries[count].mnt_passno = m->mnt_passno;
372 count++;
373 }
374 endmntent(mountTable);
375
376 mountTable = setmntent(bb_path_mtab_file, "w");
377 if (mountTable) {
378 for (i = 0; i < count; i++) {
379 if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
380 addmntent(mountTable, &entries[i]);
381 else
382 addmntent(mountTable, mp);
383 }
384 endmntent(mountTable);
385 } else if (errno != EROFS)
386 bb_perror_msg(bb_path_mtab_file);
387
388 if (ENABLE_FEATURE_CLEAN_UP) {
389 for (i = 0; i < count; i++) {
390 free(entries[i].mnt_fsname);
391 free(entries[i].mnt_dir);
392 free(entries[i].mnt_type);
393 free(entries[i].mnt_opts);
394 }
395 free(entries);
396 }
397}
398#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000399
400#if ENABLE_FEATURE_MOUNT_VERBOSE
401static int verbose_mount(const char *source, const char *target,
402 const char *filesystemtype,
403 unsigned long mountflags, const void *data)
404{
405 int rc;
406
407 errno = 0;
408 rc = mount(source, target, filesystemtype, mountflags, data);
409 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000410 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000411 source, target, filesystemtype,
412 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000413 return rc;
414}
415#else
416#define verbose_mount(...) mount(__VA_ARGS__)
417#endif
418
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000419// Append mount options to string
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000420static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000421{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000422 if (*oldopts && **oldopts) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000423 // Do not insert options which are already there
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000424 while (newopts[0]) {
425 char *p;
426 int len = strlen(newopts);
427 p = strchr(newopts, ',');
428 if (p) len = p - newopts;
429 p = *oldopts;
430 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000431 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000432 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000433 goto skip;
434 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000435 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000436 p++;
437 }
438 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
439 free(*oldopts);
440 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000441 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000442 newopts += len;
443 while (newopts[0] == ',') newopts++;
444 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000445 } else {
446 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000447 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000448 }
449}
450
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000451// Use the mount_options list to parse options into flags.
Alexander Shishkin77650952010-10-28 06:10:03 +0200452// Also update list of unrecognized options if unrecognized != NULL
Denis Vlasenkob4133682008-02-18 13:05:38 +0000453static long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000454{
Denis Vlasenkob4133682008-02-18 13:05:38 +0000455 long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000456
Rob Landley6a6798b2005-08-10 20:35:54 +0000457 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000458 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000459 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000460 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000461 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000462
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000463 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000464
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000465// FIXME: use hasmntopt()
Rob Landley6a6798b2005-08-10 20:35:54 +0000466 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000467 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Alexander Shishkin77650952010-10-28 06:10:03 +0200468 if (strcasecmp(option_str, options) == 0) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000469 long fl = mount_options[i];
Alexander Shishkin77650952010-10-28 06:10:03 +0200470 if (fl < 0)
471 flags &= fl;
472 else
473 flags |= fl;
474 goto found;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000475 }
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000476 option_str += strlen(option_str) + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000477 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200478 // We did not recognize this option.
479 // If "unrecognized" is not NULL, append option there.
480 // Note that we should not append *empty* option -
481 // in this case we want to pass NULL, not "", to "data"
482 // parameter of mount(2) syscall.
483 // This is crucial for filesystems that don't accept
484 // any arbitrary mount options, like cgroup fs:
485 // "mount -t cgroup none /mnt"
486 if (options[0] && unrecognized) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000487 // Add it to strflags, to pass on to kernel
Alexander Shishkin77650952010-10-28 06:10:03 +0200488 char *p = *unrecognized;
489 unsigned len = p ? strlen(p) : 0;
490 *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000491
Rob Landley6a6798b2005-08-10 20:35:54 +0000492 // Comma separated if it's not the first one
Alexander Shishkin77650952010-10-28 06:10:03 +0200493 if (len) p[len++] = ',';
494 strcpy(p + len, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000495 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200496 found:
Denis Vlasenko2535f122007-09-15 13:28:30 +0000497 if (!comma)
498 break;
499 // Advance to next option
500 *comma = ',';
501 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000502 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000503
Rob Landleydc0955b2006-03-14 18:16:25 +0000504 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000505}
506
Rob Landleydc0955b2006-03-14 18:16:25 +0000507// Return a list of all block device backed filesystems
Rob Landleydc0955b2006-03-14 18:16:25 +0000508static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000509{
Denis Vlasenko87468852007-04-13 23:22:00 +0000510 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000511 "/etc/filesystems",
512 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000513 };
514 char *fs, *buf;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200515 llist_t *list = NULL;
Rob Landleydc0955b2006-03-14 18:16:25 +0000516 int i;
517 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000518
Denis Vlasenko87468852007-04-13 23:22:00 +0000519 for (i = 0; i < 2; i++) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000520 f = fopen_for_read(filesystems[i]);
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000521 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000522
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000523 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200524 if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200525 goto next;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000526 fs = skip_whitespace(buf);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200527 if (*fs == '#' || *fs == '*' || !*fs)
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200528 goto next;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000529
Denis Vlasenko372686b2006-10-12 22:42:33 +0000530 llist_add_to_end(&list, xstrdup(fs));
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200531 next:
Denis Vlasenko372686b2006-10-12 22:42:33 +0000532 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000533 }
534 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
535 }
536
537 return list;
538}
539
Rob Landleydc0955b2006-03-14 18:16:25 +0000540#if ENABLE_FEATURE_CLEAN_UP
541static void delete_block_backed_filesystems(void)
542{
Rob Landleya6b5b602006-05-08 19:03:07 +0000543 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000544}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000545#else
546void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000547#endif
548
Rob Landleydc0955b2006-03-14 18:16:25 +0000549// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000550// NB: mp->xxx fields may be trashed on exit
Denis Vlasenkob4133682008-02-18 13:05:38 +0000551static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000552{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000553 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000554
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200555 if (FAKE_IT) {
Denis Vlasenkob4133682008-02-18 13:05:38 +0000556 if (verbose >= 2)
557 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
558 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
559 vfsflags, filteropts);
560 goto mtab;
561 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000562
Rob Landleydc0955b2006-03-14 18:16:25 +0000563 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000564 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000565 errno = 0;
566 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000567 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000568
569 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000570 // helper program mount.<mnt_type>
Denys Vlasenkoba986032009-09-15 23:00:09 +0200571 if (HELPERS_ALLOWED && rc && mp->mnt_type) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200572 char *args[8];
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000573 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000574 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000575 rc = 1;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200576 if (FAKE_IT)
577 args[rc++] = (char *)"-f";
578 if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
579 args[rc++] = (char *)"-n";
Denis Vlasenko5c329932009-04-12 12:16:21 +0000580 args[rc++] = mp->mnt_fsname;
581 args[rc++] = mp->mnt_dir;
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000582 if (filteropts) {
583 args[rc++] = (char *)"-o";
584 args[rc++] = filteropts;
585 }
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000586 args[rc] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +0100587 rc = spawn_and_wait(args);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000588 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000589 if (!rc)
590 break;
591 errno = errno_save;
592 }
593
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000594 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000595 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000596 if (!(vfsflags & MS_SILENT))
597 bb_error_msg("%s is write-protected, mounting read-only",
598 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000599 vfsflags |= MS_RDONLY;
600 }
601
Rob Landleydc0955b2006-03-14 18:16:25 +0000602 // Abort entirely if permission denied.
603
604 if (rc && errno == EPERM)
Tanguy Pruvot8aeb3712011-06-30 08:59:26 +0200605 bb_error_msg_and_die("%s", bb_msg_perm_denied_are_you_root);
Rob Landleydc0955b2006-03-14 18:16:25 +0000606
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000607 // If the mount was successful, and we're maintaining an old-style
608 // mtab file by hand, add the new entry to it now.
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000609 mtab:
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200610 if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000611 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000612 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000613 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000614 int i;
615
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000616 if (!mountTable) {
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100617 bb_perror_msg(bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000618 goto ret;
619 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000620
621 // Add vfs string flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000622 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
623 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
624 append_mount_options(&(mp->mnt_opts), option_str);
625 option_str += strlen(option_str) + 1;
626 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000627
628 // Remove trailing / (if any) from directory we mounted on
Denis Vlasenko727ef942006-09-14 13:19:19 +0000629 i = strlen(mp->mnt_dir) - 1;
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100630 while (i > 0 && mp->mnt_dir[i] == '/')
Denys Vlasenkoc6450c92011-02-28 11:09:49 +0100631 mp->mnt_dir[i--] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000632
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000633 // Convert to canonical pathnames as needed
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000634 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100635 fsname = NULL;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000636 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000637 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000638 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000639 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000640 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000641
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100642 // Write and close
643#if ENABLE_FEATURE_MTAB_SUPPORT
644 if (vfsflags & MS_MOVE)
645 update_mtab_entry_on_move(mp);
646 else
647#endif
648 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000649 endmntent(mountTable);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100650
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000651 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000652 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000653 free(fsname);
654 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000655 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000656 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000657 return rc;
658}
659
Denis Vlasenko25098f72006-09-14 15:46:33 +0000660#if ENABLE_FEATURE_MOUNT_NFS
661
662/*
663 * Linux NFS mount
664 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
665 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +0200666 * Licensed under GPLv2, see file LICENSE in this source tree.
Denis Vlasenko25098f72006-09-14 15:46:33 +0000667 *
668 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
669 * numbers to be specified on the command line.
670 *
671 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
672 * Omit the call to connect() for Linux version 1.3.11 or later.
673 *
674 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
675 * Implemented the "bg", "fg" and "retry" mount options for NFS.
676 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000677 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000678 * - added Native Language Support
679 *
680 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
681 * plus NFSv3 stuff.
682 */
683
Denis Vlasenko25098f72006-09-14 15:46:33 +0000684#define MOUNTPORT 635
685#define MNTPATHLEN 1024
686#define MNTNAMLEN 255
687#define FHSIZE 32
688#define FHSIZE3 64
689
690typedef char fhandle[FHSIZE];
691
692typedef struct {
693 unsigned int fhandle3_len;
694 char *fhandle3_val;
695} fhandle3;
696
697enum mountstat3 {
698 MNT_OK = 0,
699 MNT3ERR_PERM = 1,
700 MNT3ERR_NOENT = 2,
701 MNT3ERR_IO = 5,
702 MNT3ERR_ACCES = 13,
703 MNT3ERR_NOTDIR = 20,
704 MNT3ERR_INVAL = 22,
705 MNT3ERR_NAMETOOLONG = 63,
706 MNT3ERR_NOTSUPP = 10004,
707 MNT3ERR_SERVERFAULT = 10006,
708};
709typedef enum mountstat3 mountstat3;
710
711struct fhstatus {
712 unsigned int fhs_status;
713 union {
714 fhandle fhs_fhandle;
715 } fhstatus_u;
716};
717typedef struct fhstatus fhstatus;
718
719struct mountres3_ok {
720 fhandle3 fhandle;
721 struct {
722 unsigned int auth_flavours_len;
723 char *auth_flavours_val;
724 } auth_flavours;
725};
726typedef struct mountres3_ok mountres3_ok;
727
728struct mountres3 {
729 mountstat3 fhs_status;
730 union {
731 mountres3_ok mountinfo;
732 } mountres3_u;
733};
734typedef struct mountres3 mountres3;
735
736typedef char *dirpath;
737
738typedef char *name;
739
740typedef struct mountbody *mountlist;
741
742struct mountbody {
743 name ml_hostname;
744 dirpath ml_directory;
745 mountlist ml_next;
746};
747typedef struct mountbody mountbody;
748
749typedef struct groupnode *groups;
750
751struct groupnode {
752 name gr_name;
753 groups gr_next;
754};
755typedef struct groupnode groupnode;
756
757typedef struct exportnode *exports;
758
759struct exportnode {
760 dirpath ex_dir;
761 groups ex_groups;
762 exports ex_next;
763};
764typedef struct exportnode exportnode;
765
766struct ppathcnf {
767 int pc_link_max;
768 short pc_max_canon;
769 short pc_max_input;
770 short pc_name_max;
771 short pc_path_max;
772 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000773 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000774 char pc_xxx;
775 short pc_mask[2];
776};
777typedef struct ppathcnf ppathcnf;
778
779#define MOUNTPROG 100005
780#define MOUNTVERS 1
781
782#define MOUNTPROC_NULL 0
783#define MOUNTPROC_MNT 1
784#define MOUNTPROC_DUMP 2
785#define MOUNTPROC_UMNT 3
786#define MOUNTPROC_UMNTALL 4
787#define MOUNTPROC_EXPORT 5
788#define MOUNTPROC_EXPORTALL 6
789
790#define MOUNTVERS_POSIX 2
791
792#define MOUNTPROC_PATHCONF 7
793
794#define MOUNT_V3 3
795
796#define MOUNTPROC3_NULL 0
797#define MOUNTPROC3_MNT 1
798#define MOUNTPROC3_DUMP 2
799#define MOUNTPROC3_UMNT 3
800#define MOUNTPROC3_UMNTALL 4
801#define MOUNTPROC3_EXPORT 5
802
803enum {
804#ifndef NFS_FHSIZE
805 NFS_FHSIZE = 32,
806#endif
807#ifndef NFS_PORT
808 NFS_PORT = 2049
809#endif
810};
811
Denis Vlasenko25098f72006-09-14 15:46:33 +0000812/*
813 * We want to be able to compile mount on old kernels in such a way
814 * that the binary will work well on more recent kernels.
815 * Thus, if necessary we teach nfsmount.c the structure of new fields
816 * that will come later.
817 *
818 * Moreover, the new kernel includes conflict with glibc includes
819 * so it is easiest to ignore the kernel altogether (at compile time).
820 */
821
822struct nfs2_fh {
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200823 char data[32];
Denis Vlasenko25098f72006-09-14 15:46:33 +0000824};
825struct nfs3_fh {
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200826 unsigned short size;
827 unsigned char data[64];
Denis Vlasenko25098f72006-09-14 15:46:33 +0000828};
829
830struct nfs_mount_data {
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200831 int version; /* 1 */
832 int fd; /* 1 */
833 struct nfs2_fh old_root; /* 1 */
834 int flags; /* 1 */
835 int rsize; /* 1 */
836 int wsize; /* 1 */
837 int timeo; /* 1 */
838 int retrans; /* 1 */
839 int acregmin; /* 1 */
840 int acregmax; /* 1 */
841 int acdirmin; /* 1 */
842 int acdirmax; /* 1 */
843 struct sockaddr_in addr; /* 1 */
844 char hostname[256]; /* 1 */
845 int namlen; /* 2 */
846 unsigned int bsize; /* 3 */
847 struct nfs3_fh root; /* 4 */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000848};
849
850/* bits in the flags field */
851enum {
852 NFS_MOUNT_SOFT = 0x0001, /* 1 */
853 NFS_MOUNT_INTR = 0x0002, /* 1 */
854 NFS_MOUNT_SECURE = 0x0004, /* 1 */
855 NFS_MOUNT_POSIX = 0x0008, /* 1 */
856 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
857 NFS_MOUNT_NOAC = 0x0020, /* 1 */
858 NFS_MOUNT_TCP = 0x0040, /* 2 */
859 NFS_MOUNT_VER3 = 0x0080, /* 3 */
860 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000861 NFS_MOUNT_NONLM = 0x0200, /* 3 */
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200862 NFS_MOUNT_NOACL = 0x0800, /* 4 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000863 NFS_MOUNT_NORDIRPLUS = 0x4000
Denis Vlasenko25098f72006-09-14 15:46:33 +0000864};
865
866
867/*
868 * We need to translate between nfs status return values and
869 * the local errno values which may not be the same.
870 *
871 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
872 * "after #include <errno.h> the symbol errno is reserved for any use,
873 * it cannot even be used as a struct tag or field name".
874 */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000875#ifndef EDQUOT
Denys Vlasenkocc428142009-12-16 02:06:56 +0100876# define EDQUOT ENOSPC
Denis Vlasenko25098f72006-09-14 15:46:33 +0000877#endif
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000878/* Convert each NFSERR_BLAH into EBLAH */
Denys Vlasenkocc428142009-12-16 02:06:56 +0100879static const uint8_t nfs_err_stat[] = {
880 1, 2, 5, 6, 13, 17,
881 19, 20, 21, 22, 27, 28,
882 30, 63, 66, 69, 70, 71
883};
Denys Vlasenkodc1fd2e2010-05-19 17:01:29 +0200884#if ( \
885 EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \
886 ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \
887 EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
888typedef uint8_t nfs_err_type;
889#else
890typedef uint16_t nfs_err_type;
891#endif
892static const nfs_err_type nfs_err_errnum[] = {
Denys Vlasenkocc428142009-12-16 02:06:56 +0100893 EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST,
894 ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC,
895 EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
Denis Vlasenko25098f72006-09-14 15:46:33 +0000896};
Denis Vlasenko25098f72006-09-14 15:46:33 +0000897static char *nfs_strerror(int status)
898{
899 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000900
Denys Vlasenkocc428142009-12-16 02:06:56 +0100901 for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
902 if (nfs_err_stat[i] == status)
903 return strerror(nfs_err_errnum[i]);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000904 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000905 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000906}
907
908static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
909{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200910 return xdr_opaque(xdrs, objp, FHSIZE);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000911}
912
913static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
914{
915 if (!xdr_u_int(xdrs, &objp->fhs_status))
916 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200917 if (objp->fhs_status == 0)
918 return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000919 return TRUE;
920}
921
922static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
923{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200924 return xdr_string(xdrs, objp, MNTPATHLEN);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000925}
926
927static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
928{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200929 return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
930 (unsigned int *) &objp->fhandle3_len,
931 FHSIZE3);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000932}
933
934static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
935{
936 if (!xdr_fhandle3(xdrs, &objp->fhandle))
937 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200938 return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
939 &(objp->auth_flavours.auth_flavours_len),
940 ~0,
941 sizeof(int),
942 (xdrproc_t) xdr_int);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000943}
944
945static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
946{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200947 return xdr_enum(xdrs, (enum_t *) objp);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000948}
949
950static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
951{
952 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
953 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200954 if (objp->fhs_status == MNT_OK)
955 return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000956 return TRUE;
957}
958
959#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
960
Denis Vlasenko25098f72006-09-14 15:46:33 +0000961/*
962 * Unfortunately, the kernel prints annoying console messages
963 * in case of an unexpected nfs mount version (instead of
964 * just returning some error). Therefore we'll have to try
965 * and figure out what version the kernel expects.
966 *
967 * Variables:
968 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
969 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
970 * nfs_mount_version: version this source and running kernel can handle
971 */
972static void
973find_kernel_nfs_mount_version(void)
974{
Denis Vlasenkob9256052007-09-28 10:29:17 +0000975 int kernel_version;
976
977 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000978 return;
979
980 nfs_mount_version = 4; /* default */
981
982 kernel_version = get_linux_version_code();
983 if (kernel_version) {
Denys Vlasenkocc428142009-12-16 02:06:56 +0100984 if (kernel_version < KERNEL_VERSION(2,2,18))
Denis Vlasenko25098f72006-09-14 15:46:33 +0000985 nfs_mount_version = 3;
986 /* else v4 since 2.3.99pre4 */
987 }
988}
989
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +0000990static void
Denis Vlasenkob9256052007-09-28 10:29:17 +0000991get_mountport(struct pmap *pm_mnt,
992 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +0000993 long unsigned prog,
994 long unsigned version,
995 long unsigned proto,
996 long unsigned port)
997{
998 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000999
1000 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001001/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
1002 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001003 pmap = pmap_getmaps(server_addr);
1004
1005 if (version > MAX_NFSPROT)
1006 version = MAX_NFSPROT;
1007 if (!prog)
1008 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001009 pm_mnt->pm_prog = prog;
1010 pm_mnt->pm_vers = version;
1011 pm_mnt->pm_prot = proto;
1012 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001013
Denis Vlasenko25098f72006-09-14 15:46:33 +00001014 while (pmap) {
1015 if (pmap->pml_map.pm_prog != prog)
1016 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001017 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001018 goto next;
1019 if (version > 2 && pmap->pml_map.pm_vers != version)
1020 goto next;
1021 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1022 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001023 if (pmap->pml_map.pm_vers > MAX_NFSPROT
1024 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1025 || (port && pmap->pml_map.pm_port != port)
1026 ) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001027 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001028 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001029 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1030 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001031 pmap = pmap->pml_next;
1032 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001033 if (!pm_mnt->pm_vers)
1034 pm_mnt->pm_vers = MOUNTVERS;
1035 if (!pm_mnt->pm_port)
1036 pm_mnt->pm_port = MOUNTPORT;
1037 if (!pm_mnt->pm_prot)
1038 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001039}
1040
Denis Vlasenkof0000652007-09-04 18:30:26 +00001041#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001042static int daemonize(void)
1043{
Denis Vlasenko25098f72006-09-14 15:46:33 +00001044 int pid = fork();
1045 if (pid < 0) /* error */
1046 return -errno;
1047 if (pid > 0) /* parent */
1048 return 0;
1049 /* child */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001050 close(0);
1051 xopen(bb_dev_null, O_RDWR);
1052 xdup2(0, 1);
1053 xdup2(0, 2);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001054 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +00001055 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001056 logmode = LOGMODE_SYSLOG;
1057 return 1;
1058}
Denis Vlasenkof0000652007-09-04 18:30:26 +00001059#else
1060static inline int daemonize(void) { return -ENOSYS; }
1061#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001062
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001063/* TODO */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001064static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001065{
1066 return 0;
1067}
1068
1069/* RPC strerror analogs are terminally idiotic:
1070 * *mandatory* prefix and \n at end.
1071 * This hopefully helps. Usage:
1072 * error_msg_rpc(clnt_*error*(" ")) */
1073static void error_msg_rpc(const char *msg)
1074{
Denis Vlasenko23514fe2006-09-19 14:07:52 +00001075 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001076 while (msg[0] == ' ' || msg[0] == ':') msg++;
1077 len = strlen(msg);
1078 while (len && msg[len-1] == '\n') len--;
1079 bb_error_msg("%.*s", len, msg);
1080}
1081
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001082/* NB: mp->xxx fields may be trashed on exit */
Denys Vlasenko810b7162009-05-13 23:48:59 +02001083static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001084{
1085 CLIENT *mclient;
1086 char *hostname;
1087 char *pathname;
1088 char *mounthost;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001089 /* prior to 2.6.23, kernel took NFS options in a form of this struct
1090 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1091 * then data pointer is interpreted as a string. */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001092 struct nfs_mount_data data;
1093 char *opt;
1094 struct hostent *hp;
1095 struct sockaddr_in server_addr;
1096 struct sockaddr_in mount_server_addr;
1097 int msock, fsock;
1098 union {
1099 struct fhstatus nfsv2;
1100 struct mountres3 nfsv3;
1101 } status;
1102 int daemonized;
1103 char *s;
1104 int port;
1105 int mountport;
1106 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001107#if BB_MMU
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001108 smallint bg = 0;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001109#else
1110 enum { bg = 0 };
1111#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001112 int retry;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001113 int mountprog;
1114 int mountvers;
1115 int nfsprog;
1116 int nfsvers;
1117 int retval;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001118 /* these all are one-bit really. gcc 4.3.1 likes this combination: */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001119 smallint tcp;
1120 smallint soft;
1121 int intr;
1122 int posix;
1123 int nocto;
1124 int noac;
1125 int nordirplus;
1126 int nolock;
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001127 int noacl;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001128
1129 find_kernel_nfs_mount_version();
1130
1131 daemonized = 0;
1132 mounthost = NULL;
1133 retval = ETIMEDOUT;
1134 msock = fsock = -1;
1135 mclient = NULL;
1136
1137 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1138
1139 filteropts = xstrdup(filteropts); /* going to trash it later... */
1140
1141 hostname = xstrdup(mp->mnt_fsname);
1142 /* mount_main() guarantees that ':' is there */
1143 s = strchr(hostname, ':');
1144 pathname = s + 1;
1145 *s = '\0';
1146 /* Ignore all but first hostname in replicated mounts
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001147 * until they can be fully supported. (mack@sgi.com) */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001148 s = strchr(hostname, ',');
1149 if (s) {
1150 *s = '\0';
1151 bb_error_msg("warning: multiple hostnames not supported");
1152 }
1153
1154 server_addr.sin_family = AF_INET;
1155 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1156 hp = gethostbyname(hostname);
1157 if (hp == NULL) {
1158 bb_herror_msg("%s", hostname);
1159 goto fail;
1160 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001161 if (hp->h_length != (int)sizeof(struct in_addr)) {
1162 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001163 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001164 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001165 }
1166
1167 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1168
1169 /* add IP address to mtab options for use when unmounting */
1170
1171 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1172 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1173 } else {
1174 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1175 mp->mnt_opts[0] ? "," : "",
1176 inet_ntoa(server_addr.sin_addr));
1177 free(mp->mnt_opts);
1178 mp->mnt_opts = tmp;
1179 }
1180
1181 /* Set default options.
1182 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1183 * let the kernel decide.
1184 * timeo is filled in after we know whether it'll be TCP or UDP. */
1185 memset(&data, 0, sizeof(data));
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001186 data.retrans = 3;
1187 data.acregmin = 3;
1188 data.acregmax = 60;
1189 data.acdirmin = 30;
1190 data.acdirmax = 60;
1191 data.namlen = NAME_MAX;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001192
Denis Vlasenko25098f72006-09-14 15:46:33 +00001193 soft = 0;
1194 intr = 0;
1195 posix = 0;
1196 nocto = 0;
1197 nolock = 0;
1198 noac = 0;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001199 nordirplus = 0;
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001200 noacl = 0;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001201 retry = 10000; /* 10000 minutes ~ 1 week */
Bernhard Reutner-Fischer88206292011-05-04 19:03:30 +02001202 tcp = 1; /* nfs-utils uses tcp per default */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001203
1204 mountprog = MOUNTPROG;
1205 mountvers = 0;
1206 port = 0;
1207 mountport = 0;
1208 nfsprog = 100003;
1209 nfsvers = 0;
1210
1211 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001212 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001213 char *opteq = strchr(opt, '=');
1214 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001215 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001216 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001217 /* 0 */ "rsize\0"
1218 /* 1 */ "wsize\0"
1219 /* 2 */ "timeo\0"
1220 /* 3 */ "retrans\0"
1221 /* 4 */ "acregmin\0"
1222 /* 5 */ "acregmax\0"
1223 /* 6 */ "acdirmin\0"
1224 /* 7 */ "acdirmax\0"
1225 /* 8 */ "actimeo\0"
1226 /* 9 */ "retry\0"
1227 /* 10 */ "port\0"
1228 /* 11 */ "mountport\0"
1229 /* 12 */ "mounthost\0"
1230 /* 13 */ "mountprog\0"
1231 /* 14 */ "mountvers\0"
1232 /* 15 */ "nfsprog\0"
1233 /* 16 */ "nfsvers\0"
1234 /* 17 */ "vers\0"
1235 /* 18 */ "proto\0"
1236 /* 19 */ "namlen\0"
1237 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001238
1239 *opteq++ = '\0';
1240 idx = index_in_strings(options, opt);
1241 switch (idx) {
1242 case 12: // "mounthost"
1243 mounthost = xstrndup(opteq,
1244 strcspn(opteq, " \t\n\r,"));
1245 continue;
1246 case 18: // "proto"
1247 if (!strncmp(opteq, "tcp", 3))
1248 tcp = 1;
1249 else if (!strncmp(opteq, "udp", 3))
1250 tcp = 0;
1251 else
1252 bb_error_msg("warning: unrecognized proto= option");
1253 continue;
1254 case 20: // "addr" - ignore
1255 continue;
Peter Korsgaard301fe502011-02-21 17:52:13 +01001256 case -1: // unknown
1257 if (vfsflags & MS_REMOUNT)
1258 continue;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001259 }
1260
Denys Vlasenko77832482010-08-12 14:14:45 +02001261 val = xatoi_positive(opteq);
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001262 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001263 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001264 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001265 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001266 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001267 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001268 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001269 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001270 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001271 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001272 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001273 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001274 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001275 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001276 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001277 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001278 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001279 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001280 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001281 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001282 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001283 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001284 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001285 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001286 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001287 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001288 data.acregmin = val;
1289 data.acregmax = val;
1290 data.acdirmin = val;
1291 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001292 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001293 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001294 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001295 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001296 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001297 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001298 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001299 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001300 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001301 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001302 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001303 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001304 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001305 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001306 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001307 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001308 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001309 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001310 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001311 case 16: // "nfsvers"
1312 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001313 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001314 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001315 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001316 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001317 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001318 //else
1319 // bb_error_msg("warning: option namlen is not supported\n");
1320 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001321 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001322 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1323 goto fail;
1324 }
1325 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001326 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001327 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001328 "bg\0"
1329 "fg\0"
1330 "soft\0"
1331 "hard\0"
1332 "intr\0"
1333 "posix\0"
1334 "cto\0"
1335 "ac\0"
1336 "tcp\0"
1337 "udp\0"
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001338 "lock\0"
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001339 "rdirplus\0"
1340 "acl\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001341 int val = 1;
1342 if (!strncmp(opt, "no", 2)) {
1343 val = 0;
1344 opt += 2;
1345 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001346 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001347 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001348#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001349 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001350#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001351 break;
1352 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001353#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001354 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001355#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001356 break;
1357 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001358 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001359 break;
1360 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001361 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001362 break;
1363 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001364 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001365 break;
1366 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001367 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001368 break;
1369 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001370 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001371 break;
1372 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001373 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001374 break;
1375 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001376 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001377 break;
1378 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001379 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001380 break;
1381 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001382 if (nfs_mount_version >= 3)
1383 nolock = !val;
1384 else
1385 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001386 break;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001387 case 11: //rdirplus
1388 nordirplus = !val;
1389 break;
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001390 case 12: // acl
1391 noacl = !val;
1392 break;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001393 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001394 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1395 goto fail;
1396 }
1397 }
1398 }
1399 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1400
1401 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1402 | (intr ? NFS_MOUNT_INTR : 0)
1403 | (posix ? NFS_MOUNT_POSIX : 0)
1404 | (nocto ? NFS_MOUNT_NOCTO : 0)
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001405 | (noac ? NFS_MOUNT_NOAC : 0)
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001406 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
1407 | (noacl ? NFS_MOUNT_NOACL : 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001408 if (nfs_mount_version >= 2)
1409 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1410 if (nfs_mount_version >= 3)
1411 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1412 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1413 bb_error_msg("NFSv%d not supported", nfsvers);
1414 goto fail;
1415 }
1416 if (nfsvers && !mountvers)
1417 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1418 if (nfsvers && nfsvers < mountvers) {
1419 mountvers = nfsvers;
1420 }
1421
1422 /* Adjust options if none specified */
1423 if (!data.timeo)
1424 data.timeo = tcp ? 70 : 7;
1425
Denis Vlasenko25098f72006-09-14 15:46:33 +00001426 data.version = nfs_mount_version;
1427
1428 if (vfsflags & MS_REMOUNT)
1429 goto do_mount;
1430
1431 /*
1432 * If the previous mount operation on the same host was
1433 * backgrounded, and the "bg" for this mount is also set,
1434 * give up immediately, to avoid the initial timeout.
1435 */
1436 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001437 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001438 if (daemonized <= 0) { /* parent or error */
1439 retval = -daemonized;
1440 goto ret;
1441 }
1442 }
1443
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001444 /* Create mount daemon client */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001445 /* See if the nfs host = mount host. */
1446 if (mounthost) {
1447 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1448 mount_server_addr.sin_family = AF_INET;
1449 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1450 } else {
1451 hp = gethostbyname(mounthost);
1452 if (hp == NULL) {
1453 bb_herror_msg("%s", mounthost);
1454 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001455 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001456 if (hp->h_length != (int)sizeof(struct in_addr)) {
1457 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001458 }
1459 mount_server_addr.sin_family = AF_INET;
Denys Vlasenko99069332010-02-27 19:38:19 +01001460 memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001461 }
1462 }
1463
1464 /*
1465 * The following loop implements the mount retries. When the mount
1466 * times out, and the "bg" option is set, we background ourself
1467 * and continue trying.
1468 *
1469 * The case where the mount point is not present and the "bg"
1470 * option is set, is treated as a timeout. This is done to
1471 * support nested mounts.
1472 *
1473 * The "retry" count specified by the user is the number of
1474 * minutes to retry before giving up.
1475 */
1476 {
1477 struct timeval total_timeout;
1478 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001479 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001480 time_t t;
1481 time_t prevt;
1482 time_t timeout;
1483
1484 retry_timeout.tv_sec = 3;
1485 retry_timeout.tv_usec = 0;
1486 total_timeout.tv_sec = 20;
1487 total_timeout.tv_usec = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001488/* FIXME: use monotonic()? */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001489 timeout = time(NULL) + 60 * retry;
1490 prevt = 0;
1491 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001492 retry:
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001493 /* Be careful not to use too many CPU cycles */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001494 if (t - prevt < 30)
1495 sleep(30);
1496
Denis Vlasenkob9256052007-09-28 10:29:17 +00001497 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001498 mountprog,
1499 mountvers,
1500 proto,
1501 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001502 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001503
1504 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001505 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001506 msock = RPC_ANYSOCK;
1507
Denis Vlasenkob9256052007-09-28 10:29:17 +00001508 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001509 case IPPROTO_UDP:
1510 mclient = clntudp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001511 pm_mnt.pm_prog,
1512 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001513 retry_timeout,
1514 &msock);
1515 if (mclient)
1516 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001517 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001518 msock = RPC_ANYSOCK;
1519 case IPPROTO_TCP:
1520 mclient = clnttcp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001521 pm_mnt.pm_prog,
1522 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001523 &msock, 0, 0);
1524 break;
1525 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001526 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001527 }
1528 if (!mclient) {
1529 if (!daemonized && prevt == 0)
1530 error_msg_rpc(clnt_spcreateerror(" "));
1531 } else {
1532 enum clnt_stat clnt_stat;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001533
1534 /* Try to mount hostname:pathname */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001535 mclient->cl_auth = authunix_create_default();
1536
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001537 /* Make pointers in xdr_mountres3 NULL so
Denis Vlasenko25098f72006-09-14 15:46:33 +00001538 * that xdr_array allocates memory for us
1539 */
1540 memset(&status, 0, sizeof(status));
1541
Denis Vlasenkob9256052007-09-28 10:29:17 +00001542 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001543 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1544 (xdrproc_t) xdr_dirpath,
1545 (caddr_t) &pathname,
1546 (xdrproc_t) xdr_mountres3,
1547 (caddr_t) &status,
1548 total_timeout);
1549 else
1550 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1551 (xdrproc_t) xdr_dirpath,
1552 (caddr_t) &pathname,
1553 (xdrproc_t) xdr_fhstatus,
1554 (caddr_t) &status,
1555 total_timeout);
1556
1557 if (clnt_stat == RPC_SUCCESS)
1558 goto prepare_kernel_data; /* we're done */
1559 if (errno != ECONNREFUSED) {
1560 error_msg_rpc(clnt_sperror(mclient, " "));
1561 goto fail; /* don't retry */
1562 }
1563 /* Connection refused */
1564 if (!daemonized && prevt == 0) /* print just once */
1565 error_msg_rpc(clnt_sperror(mclient, " "));
1566 auth_destroy(mclient->cl_auth);
1567 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001568 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001569 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001570 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001571 }
1572
1573 /* Timeout. We are going to retry... maybe */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001574 if (!bg)
1575 goto fail;
1576 if (!daemonized) {
1577 daemonized = daemonize();
1578 if (daemonized <= 0) { /* parent or error */
1579 retval = -daemonized;
1580 goto ret;
1581 }
1582 }
1583 prevt = t;
1584 t = time(NULL);
1585 if (t >= timeout)
1586 /* TODO error message */
1587 goto fail;
1588
1589 goto retry;
1590 }
1591
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001592 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001593
1594 if (nfsvers == 2) {
1595 if (status.nfsv2.fhs_status != 0) {
1596 bb_error_msg("%s:%s failed, reason given by server: %s",
1597 hostname, pathname,
1598 nfs_strerror(status.nfsv2.fhs_status));
1599 goto fail;
1600 }
1601 memcpy(data.root.data,
1602 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1603 NFS_FHSIZE);
1604 data.root.size = NFS_FHSIZE;
1605 memcpy(data.old_root.data,
1606 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1607 NFS_FHSIZE);
1608 } else {
1609 fhandle3 *my_fhandle;
1610 if (status.nfsv3.fhs_status != 0) {
1611 bb_error_msg("%s:%s failed, reason given by server: %s",
1612 hostname, pathname,
1613 nfs_strerror(status.nfsv3.fhs_status));
1614 goto fail;
1615 }
1616 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1617 memset(data.old_root.data, 0, NFS_FHSIZE);
1618 memset(&data.root, 0, sizeof(data.root));
1619 data.root.size = my_fhandle->fhandle3_len;
1620 memcpy(data.root.data,
1621 (char *) my_fhandle->fhandle3_val,
1622 my_fhandle->fhandle3_len);
1623
1624 data.flags |= NFS_MOUNT_VER3;
1625 }
1626
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001627 /* Create nfs socket for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001628 if (tcp) {
1629 if (nfs_mount_version < 3) {
1630 bb_error_msg("NFS over TCP is not supported");
1631 goto fail;
1632 }
1633 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1634 } else
1635 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1636 if (fsock < 0) {
1637 bb_perror_msg("nfs socket");
1638 goto fail;
1639 }
1640 if (bindresvport(fsock, 0) < 0) {
1641 bb_perror_msg("nfs bindresvport");
1642 goto fail;
1643 }
1644 if (port == 0) {
1645 server_addr.sin_port = PMAPPORT;
1646 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1647 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1648 if (port == 0)
1649 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001650 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001651 server_addr.sin_port = htons(port);
1652
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001653 /* Prepare data structure for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001654 data.fd = fsock;
1655 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1656 strncpy(data.hostname, hostname, sizeof(data.hostname));
1657
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001658 /* Clean up */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001659 auth_destroy(mclient->cl_auth);
1660 clnt_destroy(mclient);
1661 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001662 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001663
1664 if (bg) {
1665 /* We must wait until mount directory is available */
1666 struct stat statbuf;
1667 int delay = 1;
1668 while (stat(mp->mnt_dir, &statbuf) == -1) {
1669 if (!daemonized) {
1670 daemonized = daemonize();
1671 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001672/* FIXME: parent doesn't close fsock - ??! */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001673 retval = -daemonized;
1674 goto ret;
1675 }
1676 }
1677 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1678 delay *= 2;
1679 if (delay > 30)
1680 delay = 30;
1681 }
1682 }
1683
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001684 /* Perform actual mount */
1685 do_mount:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001686 retval = mount_it_now(mp, vfsflags, (char*)&data);
1687 goto ret;
1688
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001689 /* Abort */
1690 fail:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001691 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001692 if (mclient) {
1693 auth_destroy(mclient->cl_auth);
1694 clnt_destroy(mclient);
1695 }
1696 close(msock);
1697 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001698 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001699 close(fsock);
1700
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001701 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001702 free(hostname);
1703 free(mounthost);
1704 free(filteropts);
1705 return retval;
1706}
1707
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001708#else // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001709
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001710/* Linux 2.6.23+ supports nfs mounts with options passed as a string.
1711 * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
1712 * (However, note that then you lose any chances that NFS over IPv6 would work).
1713 */
1714static int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
1715{
1716 len_and_sockaddr *lsa;
1717 char *opts;
1718 char *end;
1719 char *dotted;
1720 int ret;
1721
1722# if ENABLE_FEATURE_IPV6
1723 end = strchr(mp->mnt_fsname, ']');
1724 if (end && end[1] == ':')
1725 end++;
1726 else
1727# endif
1728 /* mount_main() guarantees that ':' is there */
1729 end = strchr(mp->mnt_fsname, ':');
1730
1731 *end = '\0';
1732 lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
1733 *end = ':';
1734 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1735 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1736 opts = xasprintf("%s%saddr=%s",
1737 filteropts ? filteropts : "",
1738 filteropts ? "," : "",
1739 dotted
1740 );
1741 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1742 ret = mount_it_now(mp, vfsflags, opts);
1743 if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1744
1745 return ret;
1746}
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001747
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001748#endif // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001749
1750// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1751// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001752// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001753static int singlemount(struct mntent *mp, int ignore_busy)
1754{
Denis Vlasenkob4133682008-02-18 13:05:38 +00001755 int rc = -1;
1756 long vfsflags;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001757 char *loopFile = NULL, *filteropts = NULL;
1758 llist_t *fl = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001759 struct stat st;
1760
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001761 errno = 0;
1762
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001763 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1764
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001765 // Treat fstype "auto" as unspecified
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001766 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1767 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001768
Denis Vlasenko2535f122007-09-15 13:28:30 +00001769 // Might this be a virtual filesystem?
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001770 if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1771 char *args[35];
1772 char *s;
1773 int n;
1774 // fsname: "cmd#arg1#arg2..."
1775 // WARNING: allows execution of arbitrary commands!
1776 // Try "mount 'sh#-c#sh' bogus_dir".
1777 // It is safe ONLY because non-root
1778 // cannot use two-argument mount command
1779 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1780 // "mount: can't find sh#-c#sh in /etc/fstab"
1781 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1782
1783 s = mp->mnt_fsname;
1784 n = 0;
1785 args[n++] = s;
1786 while (*s && n < 35 - 2) {
1787 if (*s++ == '#' && *s != '#') {
1788 s[-1] = '\0';
1789 args[n++] = s;
Denis Vlasenko2535f122007-09-15 13:28:30 +00001790 }
1791 }
Denis Vlasenko2535f122007-09-15 13:28:30 +00001792 args[n++] = mp->mnt_dir;
1793 args[n] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +01001794 rc = spawn_and_wait(args);
Denis Vlasenko2535f122007-09-15 13:28:30 +00001795 goto report_error;
1796 }
1797
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001798 // Might this be an CIFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001799 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001800 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1801 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1802 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001803 ) {
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001804 int len;
1805 char c;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001806 len_and_sockaddr *lsa;
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001807 char *hostname, *dotted, *ip;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001808
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001809 hostname = mp->mnt_fsname + 2;
1810 len = strcspn(hostname, "/\\");
1811 if (len == 0 || hostname[len] == '\0')
Denis Vlasenko5c329932009-04-12 12:16:21 +00001812 goto report_error;
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001813 c = hostname[len];
1814 hostname[len] = '\0';
1815 lsa = host2sockaddr(hostname, 0);
1816 hostname[len] = c;
Denis Vlasenko5c329932009-04-12 12:16:21 +00001817 if (!lsa)
1818 goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001819
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001820 // Insert "ip=..." option into options
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001821 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001822 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001823 ip = xasprintf("ip=%s", dotted);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001824 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001825 parse_mount_options(ip, &filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001826 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001827
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001828 // "-o mand" is required [why?]
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001829 vfsflags |= MS_MANDLOCK;
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001830 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001831 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001832
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001833 goto report_error;
1834 }
1835
1836 // Might this be an NFS filesystem?
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001837 if ((!mp->mnt_type || strncmp(mp->mnt_type, "nfs", 3) == 0)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001838 && strchr(mp->mnt_fsname, ':') != NULL
1839 ) {
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001840 if (!mp->mnt_type)
1841 mp->mnt_type = (char*)"nfs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001842 rc = nfsmount(mp, vfsflags, filteropts);
1843 goto report_error;
1844 }
1845
1846 // Look at the file. (Not found isn't a failure for remount, or for
1847 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001848 // (We use stat, not lstat, in order to allow
1849 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001850 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001851 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1852 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001853 // Do we need to allocate a loopback device for it?
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001854 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1855 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001856 mp->mnt_fsname = NULL; // will receive malloced loop dev name
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001857 if (set_loop(&mp->mnt_fsname, loopFile, 0, /*ro:*/ 0) < 0) {
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001858 if (errno == EPERM || errno == EACCES)
Tanguy Pruvot8aeb3712011-06-30 08:59:26 +02001859 bb_error_msg("%s", bb_msg_perm_denied_are_you_root);
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001860 else
Denys Vlasenko6331cf02009-11-13 09:08:27 +01001861 bb_perror_msg("can't setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001862 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001863 }
1864
1865 // Autodetect bind mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001866 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1867 vfsflags |= MS_BIND;
1868 }
1869
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001870 // If we know the fstype (or don't need to), jump straight
1871 // to the actual mount.
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001872 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001873 char *next;
1874 for (;;) {
1875 next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
1876 if (next)
1877 *next = '\0';
1878 rc = mount_it_now(mp, vfsflags, filteropts);
1879 if (rc == 0 || !next)
1880 break;
1881 mp->mnt_type = next + 1;
1882 }
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001883 } else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001884 // Loop through filesystem types until mount succeeds
1885 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001886
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001887 // Initialize list of block backed filesystems.
1888 // This has to be done here so that during "mount -a",
1889 // mounts after /proc shows up can autodetect.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001890 if (!fslist) {
1891 fslist = get_block_backed_filesystems();
1892 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1893 atexit(delete_block_backed_filesystems);
1894 }
1895
1896 for (fl = fslist; fl; fl = fl->link) {
1897 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001898 rc = mount_it_now(mp, vfsflags, filteropts);
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02001899 if (rc == 0)
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001900 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001901 }
1902 }
1903
1904 // If mount failed, clean up loop file (if any).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001905 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1906 del_loop(mp->mnt_fsname);
1907 if (ENABLE_FEATURE_CLEAN_UP) {
1908 free(loopFile);
1909 free(mp->mnt_fsname);
1910 }
1911 }
1912
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001913 report_error:
1914 if (ENABLE_FEATURE_CLEAN_UP)
1915 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001916
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001917 if (errno == EBUSY && ignore_busy)
1918 return 0;
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001919 if (rc != 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001920 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001921 return rc;
1922}
1923
Michael Abbott6b5accb2009-12-04 03:33:07 +01001924// -O support
1925// -O interprets a list of filter options which select whether a mount
1926// point will be mounted: only mounts with options matching *all* filtering
1927// options will be selected.
1928// By default each -O filter option must be present in the list of mount
1929// options, but if it is prefixed by "no" then it must be absent.
1930// For example,
1931// -O a,nob,c matches -o a,c but fails to match -o a,b,c
1932// (and also fails to match -o a because -o c is absent).
1933//
1934// It is different from -t in that each option is matched exactly; a leading
1935// "no" at the beginning of one option does not negate the rest.
1936static int match_opt(const char *fs_opt_in, const char *O_opt)
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001937{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001938 if (!O_opt)
Michael Abbott6b5accb2009-12-04 03:33:07 +01001939 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001940
Michael Abbott6b5accb2009-12-04 03:33:07 +01001941 while (*O_opt) {
1942 const char *fs_opt = fs_opt_in;
1943 int O_len;
1944 int match;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001945
Michael Abbott6b5accb2009-12-04 03:33:07 +01001946 // If option begins with "no" then treat as an inverted match:
1947 // matching is a failure
1948 match = 0;
1949 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
1950 match = 1;
1951 O_opt += 2;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001952 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01001953 // Isolate the current O option
1954 O_len = strchrnul(O_opt, ',') - O_opt;
1955 // Check for a match against existing options
1956 while (1) {
1957 if (strncmp(fs_opt, O_opt, O_len) == 0
1958 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
1959 ) {
1960 if (match)
1961 return 0; // "no" prefix, but option found
1962 match = 1; // current O option found, go check next one
1963 break;
1964 }
1965 fs_opt = strchr(fs_opt, ',');
1966 if (!fs_opt)
1967 break;
1968 fs_opt++;
1969 }
1970 if (match == 0)
1971 return 0; // match wanted but not found
1972 if (O_opt[O_len] == '\0') // end?
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001973 break;
Michael Abbott6b5accb2009-12-04 03:33:07 +01001974 // Step to the next O option
1975 O_opt += O_len + 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001976 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01001977 // If we get here then everything matched
1978 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001979}
1980
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001981// Parse options, if necessary parse fstab/mtab, and call singlemount for
1982// each directory to be mounted.
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001983int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001984int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001985{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001986 char *cmdopts = xzalloc(1);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001987 char *fstype = NULL;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001988 char *O_optmatch = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001989 char *storage_path;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001990 llist_t *lst_o = NULL;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001991 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001992 FILE *fstab;
Denys Vlasenkoa7329662009-12-05 04:25:19 +01001993 int i, j;
1994 int rc = EXIT_SUCCESS;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001995 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001996 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001997 IF_NOT_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001998
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001999 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00002000
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02002001 INIT_G();
2002
Denis Vlasenkof732e962008-02-18 12:07:49 +00002003 // Parse long options, like --bind and --move. Note that -o option
2004 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002005 for (i = j = 1; argv[i]; i++) {
2006 if (argv[i][0] == '-' && argv[i][1] == '-')
2007 append_mount_options(&cmdopts, argv[i] + 2);
2008 else
2009 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002010 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00002011 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002012
2013 // Parse remaining options
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002014 // Max 2 params; -o is a list, -v is a counter
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002015 opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002016 opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00002017 IF_FEATURE_MOUNT_VERBOSE(, &verbose));
Denis Vlasenkof9dde912008-10-18 19:15:57 +00002018 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00002019 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2020 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00002021 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002022
2023 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002024 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00002025 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002026 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2027
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002028 if (!mountTable)
2029 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002030
Denis Vlasenko2535f122007-09-15 13:28:30 +00002031 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002032 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002033 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002034 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00002035 // util-linux 2.12a happily shows rootfs...
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002036 //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002037
Denys Vlasenko4e60f302009-12-15 01:28:59 +01002038 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002039 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2040 mtpair->mnt_dir, mtpair->mnt_type,
2041 mtpair->mnt_opts);
2042 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002043 if (ENABLE_FEATURE_CLEAN_UP)
2044 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002045 return EXIT_SUCCESS;
2046 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002047 storage_path = NULL;
2048 } else {
2049 // When we have two arguments, the second is the directory and we can
2050 // skip looking at fstab entirely. We can always abspath() the directory
2051 // argument when we get it.
2052 if (argv[1]) {
2053 if (nonroot)
Tanguy Pruvot8aeb3712011-06-30 08:59:26 +02002054 bb_error_msg_and_die("%s", bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002055 mtpair->mnt_fsname = argv[0];
2056 mtpair->mnt_dir = argv[1];
2057 mtpair->mnt_type = fstype;
2058 mtpair->mnt_opts = cmdopts;
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002059 resolve_mount_spec(&mtpair->mnt_fsname);
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002060 rc = singlemount(mtpair, /*ignore_busy:*/ 0);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002061 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00002062 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002063 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002064 }
2065
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002066 // Past this point, we are handling either "mount -a [opts]"
2067 // or "mount [opts] single_param"
2068
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002069 i = parse_mount_options(cmdopts, NULL); // FIXME: should be "long", not "int"
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002070 if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
Tanguy Pruvot8aeb3712011-06-30 08:59:26 +02002071 bb_error_msg_and_die("%s", bb_msg_you_must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00002072
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002073 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002074 if (ENABLE_FEATURE_MOUNT_FLAGS
2075 && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
2076 ) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002077 // verbose_mount(source, target, type, flags, data)
2078 rc = verbose_mount("", argv[0], "", i, "");
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002079 if (rc)
2080 bb_simple_perror_msg_and_die(argv[0]);
2081 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002082 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00002083
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002084 // Open either fstab or mtab
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002085 fstabname = "/etc/fstab";
2086 if (i & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002087 // WARNING. I am not sure this matches util-linux's
2088 // behavior. It's possible util-linux does not
2089 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002090 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002091 }
2092 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00002093 if (!fstab)
Denys Vlasenko651a2692010-03-23 16:25:17 +01002094 bb_perror_msg_and_die("can't read '%s'", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002095
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002096 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00002097 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002098 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002099 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002100
2101 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00002102 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002103 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002104 GETMNTENT_BUFSIZE/2)
2105 ) { // End of fstab/mtab is reached
2106 mtcur = mtother; // the thing we found last time
2107 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002108 }
2109
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002110 // If we're trying to mount something specific and this isn't it,
2111 // skip it. Note we must match the exact text in fstab (ala
2112 // "proc") or a full path from root
2113 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002114
2115 // Is this what we're looking for?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002116 if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2117 && strcmp(storage_path, mtcur->mnt_fsname) != 0
2118 && strcmp(argv[0], mtcur->mnt_dir) != 0
2119 && strcmp(storage_path, mtcur->mnt_dir) != 0
2120 ) {
2121 continue; // no
2122 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002123
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002124 // Remember this entry. Something later may have
2125 // overmounted it, and we want the _last_ match.
2126 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002127
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002128 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002129 } else {
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002130 struct mntent *mp;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002131 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002132 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002133 if (nonroot)
Tanguy Pruvot8aeb3712011-06-30 08:59:26 +02002134 bb_error_msg_and_die("%s", bb_msg_you_must_be_root);
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002135
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002136 // Does type match? (NULL matches always)
2137 if (!match_fstype(mtcur, fstype))
2138 continue;
2139
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002140 // Skip noauto and swap anyway
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002141 if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2142 // swap is bogus "fstype", parse_mount_options can't check fstypes
2143 || strcasecmp(mtcur->mnt_type, "swap") == 0
2144 ) {
2145 continue;
2146 }
2147
2148 // Does (at least one) option match?
2149 // (NULL matches always)
2150 if (!match_opt(mtcur->mnt_opts, O_optmatch))
2151 continue;
2152
2153 resolve_mount_spec(&mtcur->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002154
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002155 // NFS mounts want this to be xrealloc-able
2156 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002157
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002158 // If nothing is mounted on this directory...
2159 // (otherwise repeated "mount -a" mounts everything again)
2160 mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2161 // We do not check fsname match of found mount point -
2162 // "/" may have fsname of "/dev/root" while fstab
2163 // says "/dev/something_else".
2164 if (mp) {
Denys Vlasenko86566762009-12-10 21:32:28 +01002165 if (verbose) {
2166 bb_error_msg("according to %s, "
2167 "%s is already mounted on %s",
2168 bb_path_mtab_file,
2169 mp->mnt_fsname, mp->mnt_dir);
2170 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002171 } else {
2172 // ...mount this thing
2173 if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2174 // Count number of failed mounts
2175 rc++;
2176 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002177 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002178 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002179 }
2180 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002181
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002182 // End of fstab/mtab is reached.
2183 // Were we looking for something specific?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002184 if (argv[0]) { // yes
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002185 long l;
2186
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002187 // If we didn't find anything, complain
2188 if (!mtcur->mnt_fsname)
2189 bb_error_msg_and_die("can't find %s in %s",
2190 argv[0], fstabname);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002191
2192 // What happens when we try to "mount swap_partition"?
2193 // (fstab containts "swap_partition swap swap defaults 0 0")
2194 // util-linux-ng 2.13.1 does this:
2195 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2196 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2197 // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory)
2198 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2199 // exit_group(32) = ?
2200#if 0
2201 // In case we want to simply skip swap partitions:
2202 l = parse_mount_options(mtcur->mnt_opts, NULL);
2203 if ((l & MOUNT_SWAP)
2204 // swap is bogus "fstype", parse_mount_options can't check fstypes
2205 || strcasecmp(mtcur->mnt_type, "swap") == 0
2206 ) {
2207 goto ret;
2208 }
2209#endif
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002210 if (nonroot) {
2211 // fstab must have "users" or "user"
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002212 l = parse_mount_options(mtcur->mnt_opts, NULL);
2213 if (!(l & MOUNT_USERS))
Tanguy Pruvot8aeb3712011-06-30 08:59:26 +02002214 bb_error_msg_and_die("%s", bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002215 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002216
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002217 //util-linux-2.12 does not do this check.
2218 //// If nothing is mounted on this directory...
2219 //// (otherwise repeated "mount FOO" mounts FOO again)
2220 //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2221 //if (mp) {
2222 // bb_error_msg("according to %s, "
2223 // "%s is already mounted on %s",
2224 // bb_path_mtab_file,
2225 // mp->mnt_fsname, mp->mnt_dir);
2226 //} else {
2227 // ...mount the last thing we found
2228 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2229 append_mount_options(&(mtcur->mnt_opts), cmdopts);
2230 resolve_mount_spec(&mtpair->mnt_fsname);
2231 rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2232 if (ENABLE_FEATURE_CLEAN_UP)
2233 free(mtcur->mnt_opts);
2234 //}
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002235 }
2236
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002237 //ret:
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002238 if (ENABLE_FEATURE_CLEAN_UP)
2239 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002240 if (ENABLE_FEATURE_CLEAN_UP) {
2241 free(storage_path);
2242 free(cmdopts);
2243 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002244
2245//TODO: exitcode should be ORed mask of (from "man mount"):
2246// 0 success
2247// 1 incorrect invocation or permissions
2248// 2 system error (out of memory, cannot fork, no more loop devices)
2249// 4 internal mount bug or missing nfs support in mount
2250// 8 user interrupt
2251//16 problems writing or locking /etc/mtab
2252//32 mount failure
2253//64 some mount succeeded
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002254 return rc;
2255}