blob: 05e532cda69e326cd41d0db7857b947f1407e2f3 [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)"
41//usage: "\n -t FSTYPE Filesystem type"
42//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 )
342
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100343#if ENABLE_FEATURE_MTAB_SUPPORT
344/*
345 * update_mtab_entry_on_move() is used to update entry in case of mount --move.
346 * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
347 * input mntent and replace it by new one.
348 */
349static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
350{
351 struct mntent *entries, *m;
352 int i, count;
353 FILE *mountTable;
354
355 mountTable = setmntent(bb_path_mtab_file, "r");
356 if (!mountTable) {
357 bb_perror_msg(bb_path_mtab_file);
358 return;
359 }
360
361 entries = NULL;
362 count = 0;
363 while ((m = getmntent(mountTable)) != NULL) {
364 entries = xrealloc_vector(entries, 3, count);
365 entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
366 entries[count].mnt_dir = xstrdup(m->mnt_dir);
367 entries[count].mnt_type = xstrdup(m->mnt_type);
368 entries[count].mnt_opts = xstrdup(m->mnt_opts);
369 entries[count].mnt_freq = m->mnt_freq;
370 entries[count].mnt_passno = m->mnt_passno;
371 count++;
372 }
373 endmntent(mountTable);
374
375 mountTable = setmntent(bb_path_mtab_file, "w");
376 if (mountTable) {
377 for (i = 0; i < count; i++) {
378 if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
379 addmntent(mountTable, &entries[i]);
380 else
381 addmntent(mountTable, mp);
382 }
383 endmntent(mountTable);
384 } else if (errno != EROFS)
385 bb_perror_msg(bb_path_mtab_file);
386
387 if (ENABLE_FEATURE_CLEAN_UP) {
388 for (i = 0; i < count; i++) {
389 free(entries[i].mnt_fsname);
390 free(entries[i].mnt_dir);
391 free(entries[i].mnt_type);
392 free(entries[i].mnt_opts);
393 }
394 free(entries);
395 }
396}
397#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000398
399#if ENABLE_FEATURE_MOUNT_VERBOSE
400static int verbose_mount(const char *source, const char *target,
401 const char *filesystemtype,
402 unsigned long mountflags, const void *data)
403{
404 int rc;
405
406 errno = 0;
407 rc = mount(source, target, filesystemtype, mountflags, data);
408 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000409 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000410 source, target, filesystemtype,
411 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000412 return rc;
413}
414#else
415#define verbose_mount(...) mount(__VA_ARGS__)
416#endif
417
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000418// Append mount options to string
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000419static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000420{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000421 if (*oldopts && **oldopts) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000422 // Do not insert options which are already there
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000423 while (newopts[0]) {
424 char *p;
425 int len = strlen(newopts);
426 p = strchr(newopts, ',');
427 if (p) len = p - newopts;
428 p = *oldopts;
429 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000430 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000431 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000432 goto skip;
433 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000434 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000435 p++;
436 }
437 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
438 free(*oldopts);
439 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000440 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000441 newopts += len;
442 while (newopts[0] == ',') newopts++;
443 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000444 } else {
445 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000446 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000447 }
448}
449
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000450// Use the mount_options list to parse options into flags.
Alexander Shishkin77650952010-10-28 06:10:03 +0200451// Also update list of unrecognized options if unrecognized != NULL
Denis Vlasenkob4133682008-02-18 13:05:38 +0000452static long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000453{
Denis Vlasenkob4133682008-02-18 13:05:38 +0000454 long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000455
Rob Landley6a6798b2005-08-10 20:35:54 +0000456 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000457 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000458 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000459 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000460 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000461
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000462 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000463
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000464// FIXME: use hasmntopt()
Rob Landley6a6798b2005-08-10 20:35:54 +0000465 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000466 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Alexander Shishkin77650952010-10-28 06:10:03 +0200467 if (strcasecmp(option_str, options) == 0) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000468 long fl = mount_options[i];
Alexander Shishkin77650952010-10-28 06:10:03 +0200469 if (fl < 0)
470 flags &= fl;
471 else
472 flags |= fl;
473 goto found;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000474 }
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000475 option_str += strlen(option_str) + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000476 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200477 // We did not recognize this option.
478 // If "unrecognized" is not NULL, append option there.
479 // Note that we should not append *empty* option -
480 // in this case we want to pass NULL, not "", to "data"
481 // parameter of mount(2) syscall.
482 // This is crucial for filesystems that don't accept
483 // any arbitrary mount options, like cgroup fs:
484 // "mount -t cgroup none /mnt"
485 if (options[0] && unrecognized) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000486 // Add it to strflags, to pass on to kernel
Alexander Shishkin77650952010-10-28 06:10:03 +0200487 char *p = *unrecognized;
488 unsigned len = p ? strlen(p) : 0;
489 *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000490
Rob Landley6a6798b2005-08-10 20:35:54 +0000491 // Comma separated if it's not the first one
Alexander Shishkin77650952010-10-28 06:10:03 +0200492 if (len) p[len++] = ',';
493 strcpy(p + len, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000494 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200495 found:
Denis Vlasenko2535f122007-09-15 13:28:30 +0000496 if (!comma)
497 break;
498 // Advance to next option
499 *comma = ',';
500 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000501 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000502
Rob Landleydc0955b2006-03-14 18:16:25 +0000503 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000504}
505
Rob Landleydc0955b2006-03-14 18:16:25 +0000506// Return a list of all block device backed filesystems
Rob Landleydc0955b2006-03-14 18:16:25 +0000507static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000508{
Denis Vlasenko87468852007-04-13 23:22:00 +0000509 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000510 "/etc/filesystems",
511 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000512 };
513 char *fs, *buf;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200514 llist_t *list = NULL;
Rob Landleydc0955b2006-03-14 18:16:25 +0000515 int i;
516 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000517
Denis Vlasenko87468852007-04-13 23:22:00 +0000518 for (i = 0; i < 2; i++) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000519 f = fopen_for_read(filesystems[i]);
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000520 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000521
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000522 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200523 if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
Denis Vlasenko372686b2006-10-12 22:42:33 +0000524 continue;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000525 fs = skip_whitespace(buf);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200526 if (*fs == '#' || *fs == '*' || !*fs)
527 continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000528
Denis Vlasenko372686b2006-10-12 22:42:33 +0000529 llist_add_to_end(&list, xstrdup(fs));
530 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000531 }
532 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
533 }
534
535 return list;
536}
537
Rob Landleydc0955b2006-03-14 18:16:25 +0000538#if ENABLE_FEATURE_CLEAN_UP
539static void delete_block_backed_filesystems(void)
540{
Rob Landleya6b5b602006-05-08 19:03:07 +0000541 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000542}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000543#else
544void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000545#endif
546
Rob Landleydc0955b2006-03-14 18:16:25 +0000547// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000548// NB: mp->xxx fields may be trashed on exit
Denis Vlasenkob4133682008-02-18 13:05:38 +0000549static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000550{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000551 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000552
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200553 if (FAKE_IT) {
Denis Vlasenkob4133682008-02-18 13:05:38 +0000554 if (verbose >= 2)
555 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
556 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
557 vfsflags, filteropts);
558 goto mtab;
559 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000560
Rob Landleydc0955b2006-03-14 18:16:25 +0000561 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000562 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000563 errno = 0;
564 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000565 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000566
567 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000568 // helper program mount.<mnt_type>
Denys Vlasenkoba986032009-09-15 23:00:09 +0200569 if (HELPERS_ALLOWED && rc && mp->mnt_type) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200570 char *args[8];
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000571 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000572 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000573 rc = 1;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200574 if (FAKE_IT)
575 args[rc++] = (char *)"-f";
576 if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
577 args[rc++] = (char *)"-n";
Denis Vlasenko5c329932009-04-12 12:16:21 +0000578 args[rc++] = mp->mnt_fsname;
579 args[rc++] = mp->mnt_dir;
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000580 if (filteropts) {
581 args[rc++] = (char *)"-o";
582 args[rc++] = filteropts;
583 }
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000584 args[rc] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +0100585 rc = spawn_and_wait(args);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000586 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000587 if (!rc)
588 break;
589 errno = errno_save;
590 }
591
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000592 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000593 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000594 if (!(vfsflags & MS_SILENT))
595 bb_error_msg("%s is write-protected, mounting read-only",
596 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000597 vfsflags |= MS_RDONLY;
598 }
599
Rob Landleydc0955b2006-03-14 18:16:25 +0000600 // Abort entirely if permission denied.
601
602 if (rc && errno == EPERM)
603 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
604
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000605 // If the mount was successful, and we're maintaining an old-style
606 // mtab file by hand, add the new entry to it now.
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000607 mtab:
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200608 if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000609 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000610 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000611 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000612 int i;
613
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000614 if (!mountTable) {
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100615 bb_perror_msg(bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000616 goto ret;
617 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000618
619 // Add vfs string flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000620 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
621 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
622 append_mount_options(&(mp->mnt_opts), option_str);
623 option_str += strlen(option_str) + 1;
624 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000625
626 // Remove trailing / (if any) from directory we mounted on
Denis Vlasenko727ef942006-09-14 13:19:19 +0000627 i = strlen(mp->mnt_dir) - 1;
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100628 while (i > 0 && mp->mnt_dir[i] == '/')
Denys Vlasenkoc6450c92011-02-28 11:09:49 +0100629 mp->mnt_dir[i--] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000630
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000631 // Convert to canonical pathnames as needed
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000632 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100633 fsname = NULL;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000634 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000635 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000636 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000637 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000638 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000639
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100640 // Write and close
641#if ENABLE_FEATURE_MTAB_SUPPORT
642 if (vfsflags & MS_MOVE)
643 update_mtab_entry_on_move(mp);
644 else
645#endif
646 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000647 endmntent(mountTable);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100648
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000649 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000650 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000651 free(fsname);
652 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000653 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000654 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000655 return rc;
656}
657
Denis Vlasenko25098f72006-09-14 15:46:33 +0000658#if ENABLE_FEATURE_MOUNT_NFS
659
660/*
661 * Linux NFS mount
662 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
663 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +0200664 * Licensed under GPLv2, see file LICENSE in this source tree.
Denis Vlasenko25098f72006-09-14 15:46:33 +0000665 *
666 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
667 * numbers to be specified on the command line.
668 *
669 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
670 * Omit the call to connect() for Linux version 1.3.11 or later.
671 *
672 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
673 * Implemented the "bg", "fg" and "retry" mount options for NFS.
674 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000675 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000676 * - added Native Language Support
677 *
678 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
679 * plus NFSv3 stuff.
680 */
681
Denis Vlasenko25098f72006-09-14 15:46:33 +0000682#define MOUNTPORT 635
683#define MNTPATHLEN 1024
684#define MNTNAMLEN 255
685#define FHSIZE 32
686#define FHSIZE3 64
687
688typedef char fhandle[FHSIZE];
689
690typedef struct {
691 unsigned int fhandle3_len;
692 char *fhandle3_val;
693} fhandle3;
694
695enum mountstat3 {
696 MNT_OK = 0,
697 MNT3ERR_PERM = 1,
698 MNT3ERR_NOENT = 2,
699 MNT3ERR_IO = 5,
700 MNT3ERR_ACCES = 13,
701 MNT3ERR_NOTDIR = 20,
702 MNT3ERR_INVAL = 22,
703 MNT3ERR_NAMETOOLONG = 63,
704 MNT3ERR_NOTSUPP = 10004,
705 MNT3ERR_SERVERFAULT = 10006,
706};
707typedef enum mountstat3 mountstat3;
708
709struct fhstatus {
710 unsigned int fhs_status;
711 union {
712 fhandle fhs_fhandle;
713 } fhstatus_u;
714};
715typedef struct fhstatus fhstatus;
716
717struct mountres3_ok {
718 fhandle3 fhandle;
719 struct {
720 unsigned int auth_flavours_len;
721 char *auth_flavours_val;
722 } auth_flavours;
723};
724typedef struct mountres3_ok mountres3_ok;
725
726struct mountres3 {
727 mountstat3 fhs_status;
728 union {
729 mountres3_ok mountinfo;
730 } mountres3_u;
731};
732typedef struct mountres3 mountres3;
733
734typedef char *dirpath;
735
736typedef char *name;
737
738typedef struct mountbody *mountlist;
739
740struct mountbody {
741 name ml_hostname;
742 dirpath ml_directory;
743 mountlist ml_next;
744};
745typedef struct mountbody mountbody;
746
747typedef struct groupnode *groups;
748
749struct groupnode {
750 name gr_name;
751 groups gr_next;
752};
753typedef struct groupnode groupnode;
754
755typedef struct exportnode *exports;
756
757struct exportnode {
758 dirpath ex_dir;
759 groups ex_groups;
760 exports ex_next;
761};
762typedef struct exportnode exportnode;
763
764struct ppathcnf {
765 int pc_link_max;
766 short pc_max_canon;
767 short pc_max_input;
768 short pc_name_max;
769 short pc_path_max;
770 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000771 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000772 char pc_xxx;
773 short pc_mask[2];
774};
775typedef struct ppathcnf ppathcnf;
776
777#define MOUNTPROG 100005
778#define MOUNTVERS 1
779
780#define MOUNTPROC_NULL 0
781#define MOUNTPROC_MNT 1
782#define MOUNTPROC_DUMP 2
783#define MOUNTPROC_UMNT 3
784#define MOUNTPROC_UMNTALL 4
785#define MOUNTPROC_EXPORT 5
786#define MOUNTPROC_EXPORTALL 6
787
788#define MOUNTVERS_POSIX 2
789
790#define MOUNTPROC_PATHCONF 7
791
792#define MOUNT_V3 3
793
794#define MOUNTPROC3_NULL 0
795#define MOUNTPROC3_MNT 1
796#define MOUNTPROC3_DUMP 2
797#define MOUNTPROC3_UMNT 3
798#define MOUNTPROC3_UMNTALL 4
799#define MOUNTPROC3_EXPORT 5
800
801enum {
802#ifndef NFS_FHSIZE
803 NFS_FHSIZE = 32,
804#endif
805#ifndef NFS_PORT
806 NFS_PORT = 2049
807#endif
808};
809
Denis Vlasenko25098f72006-09-14 15:46:33 +0000810/*
811 * We want to be able to compile mount on old kernels in such a way
812 * that the binary will work well on more recent kernels.
813 * Thus, if necessary we teach nfsmount.c the structure of new fields
814 * that will come later.
815 *
816 * Moreover, the new kernel includes conflict with glibc includes
817 * so it is easiest to ignore the kernel altogether (at compile time).
818 */
819
820struct nfs2_fh {
821 char data[32];
822};
823struct nfs3_fh {
824 unsigned short size;
825 unsigned char data[64];
826};
827
828struct nfs_mount_data {
829 int version; /* 1 */
830 int fd; /* 1 */
831 struct nfs2_fh old_root; /* 1 */
832 int flags; /* 1 */
833 int rsize; /* 1 */
834 int wsize; /* 1 */
835 int timeo; /* 1 */
836 int retrans; /* 1 */
837 int acregmin; /* 1 */
838 int acregmax; /* 1 */
839 int acdirmin; /* 1 */
840 int acdirmax; /* 1 */
841 struct sockaddr_in addr; /* 1 */
842 char hostname[256]; /* 1 */
843 int namlen; /* 2 */
844 unsigned int bsize; /* 3 */
845 struct nfs3_fh root; /* 4 */
846};
847
848/* bits in the flags field */
849enum {
850 NFS_MOUNT_SOFT = 0x0001, /* 1 */
851 NFS_MOUNT_INTR = 0x0002, /* 1 */
852 NFS_MOUNT_SECURE = 0x0004, /* 1 */
853 NFS_MOUNT_POSIX = 0x0008, /* 1 */
854 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
855 NFS_MOUNT_NOAC = 0x0020, /* 1 */
856 NFS_MOUNT_TCP = 0x0040, /* 2 */
857 NFS_MOUNT_VER3 = 0x0080, /* 3 */
858 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000859 NFS_MOUNT_NONLM = 0x0200, /* 3 */
860 NFS_MOUNT_NORDIRPLUS = 0x4000
Denis Vlasenko25098f72006-09-14 15:46:33 +0000861};
862
863
864/*
865 * We need to translate between nfs status return values and
866 * the local errno values which may not be the same.
867 *
868 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
869 * "after #include <errno.h> the symbol errno is reserved for any use,
870 * it cannot even be used as a struct tag or field name".
871 */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000872#ifndef EDQUOT
Denys Vlasenkocc428142009-12-16 02:06:56 +0100873# define EDQUOT ENOSPC
Denis Vlasenko25098f72006-09-14 15:46:33 +0000874#endif
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000875/* Convert each NFSERR_BLAH into EBLAH */
Denys Vlasenkocc428142009-12-16 02:06:56 +0100876static const uint8_t nfs_err_stat[] = {
877 1, 2, 5, 6, 13, 17,
878 19, 20, 21, 22, 27, 28,
879 30, 63, 66, 69, 70, 71
880};
Denys Vlasenkodc1fd2e2010-05-19 17:01:29 +0200881#if ( \
882 EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \
883 ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \
884 EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
885typedef uint8_t nfs_err_type;
886#else
887typedef uint16_t nfs_err_type;
888#endif
889static const nfs_err_type nfs_err_errnum[] = {
Denys Vlasenkocc428142009-12-16 02:06:56 +0100890 EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST,
891 ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC,
892 EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
Denis Vlasenko25098f72006-09-14 15:46:33 +0000893};
Denis Vlasenko25098f72006-09-14 15:46:33 +0000894static char *nfs_strerror(int status)
895{
896 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000897
Denys Vlasenkocc428142009-12-16 02:06:56 +0100898 for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
899 if (nfs_err_stat[i] == status)
900 return strerror(nfs_err_errnum[i]);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000901 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000902 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000903}
904
905static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
906{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200907 return xdr_opaque(xdrs, objp, FHSIZE);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000908}
909
910static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
911{
912 if (!xdr_u_int(xdrs, &objp->fhs_status))
913 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200914 if (objp->fhs_status == 0)
915 return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000916 return TRUE;
917}
918
919static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
920{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200921 return xdr_string(xdrs, objp, MNTPATHLEN);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000922}
923
924static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
925{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200926 return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
927 (unsigned int *) &objp->fhandle3_len,
928 FHSIZE3);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000929}
930
931static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
932{
933 if (!xdr_fhandle3(xdrs, &objp->fhandle))
934 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200935 return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
936 &(objp->auth_flavours.auth_flavours_len),
937 ~0,
938 sizeof(int),
939 (xdrproc_t) xdr_int);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000940}
941
942static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
943{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200944 return xdr_enum(xdrs, (enum_t *) objp);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000945}
946
947static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
948{
949 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
950 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200951 if (objp->fhs_status == MNT_OK)
952 return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000953 return TRUE;
954}
955
956#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
957
Denis Vlasenko25098f72006-09-14 15:46:33 +0000958/*
959 * Unfortunately, the kernel prints annoying console messages
960 * in case of an unexpected nfs mount version (instead of
961 * just returning some error). Therefore we'll have to try
962 * and figure out what version the kernel expects.
963 *
964 * Variables:
965 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
966 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
967 * nfs_mount_version: version this source and running kernel can handle
968 */
969static void
970find_kernel_nfs_mount_version(void)
971{
Denis Vlasenkob9256052007-09-28 10:29:17 +0000972 int kernel_version;
973
974 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000975 return;
976
977 nfs_mount_version = 4; /* default */
978
979 kernel_version = get_linux_version_code();
980 if (kernel_version) {
Denys Vlasenkocc428142009-12-16 02:06:56 +0100981 if (kernel_version < KERNEL_VERSION(2,2,18))
Denis Vlasenko25098f72006-09-14 15:46:33 +0000982 nfs_mount_version = 3;
983 /* else v4 since 2.3.99pre4 */
984 }
985}
986
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +0000987static void
Denis Vlasenkob9256052007-09-28 10:29:17 +0000988get_mountport(struct pmap *pm_mnt,
989 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +0000990 long unsigned prog,
991 long unsigned version,
992 long unsigned proto,
993 long unsigned port)
994{
995 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000996
997 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +0000998/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
999 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001000 pmap = pmap_getmaps(server_addr);
1001
1002 if (version > MAX_NFSPROT)
1003 version = MAX_NFSPROT;
1004 if (!prog)
1005 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001006 pm_mnt->pm_prog = prog;
1007 pm_mnt->pm_vers = version;
1008 pm_mnt->pm_prot = proto;
1009 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001010
Denis Vlasenko25098f72006-09-14 15:46:33 +00001011 while (pmap) {
1012 if (pmap->pml_map.pm_prog != prog)
1013 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001014 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001015 goto next;
1016 if (version > 2 && pmap->pml_map.pm_vers != version)
1017 goto next;
1018 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1019 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001020 if (pmap->pml_map.pm_vers > MAX_NFSPROT
1021 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1022 || (port && pmap->pml_map.pm_port != port)
1023 ) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001024 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001025 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001026 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1027 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001028 pmap = pmap->pml_next;
1029 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001030 if (!pm_mnt->pm_vers)
1031 pm_mnt->pm_vers = MOUNTVERS;
1032 if (!pm_mnt->pm_port)
1033 pm_mnt->pm_port = MOUNTPORT;
1034 if (!pm_mnt->pm_prot)
1035 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001036}
1037
Denis Vlasenkof0000652007-09-04 18:30:26 +00001038#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001039static int daemonize(void)
1040{
Denis Vlasenko25098f72006-09-14 15:46:33 +00001041 int pid = fork();
1042 if (pid < 0) /* error */
1043 return -errno;
1044 if (pid > 0) /* parent */
1045 return 0;
1046 /* child */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001047 close(0);
1048 xopen(bb_dev_null, O_RDWR);
1049 xdup2(0, 1);
1050 xdup2(0, 2);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001051 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +00001052 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001053 logmode = LOGMODE_SYSLOG;
1054 return 1;
1055}
Denis Vlasenkof0000652007-09-04 18:30:26 +00001056#else
1057static inline int daemonize(void) { return -ENOSYS; }
1058#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001059
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001060/* TODO */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001061static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001062{
1063 return 0;
1064}
1065
1066/* RPC strerror analogs are terminally idiotic:
1067 * *mandatory* prefix and \n at end.
1068 * This hopefully helps. Usage:
1069 * error_msg_rpc(clnt_*error*(" ")) */
1070static void error_msg_rpc(const char *msg)
1071{
Denis Vlasenko23514fe2006-09-19 14:07:52 +00001072 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001073 while (msg[0] == ' ' || msg[0] == ':') msg++;
1074 len = strlen(msg);
1075 while (len && msg[len-1] == '\n') len--;
1076 bb_error_msg("%.*s", len, msg);
1077}
1078
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001079/* NB: mp->xxx fields may be trashed on exit */
Denys Vlasenko810b7162009-05-13 23:48:59 +02001080static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001081{
1082 CLIENT *mclient;
1083 char *hostname;
1084 char *pathname;
1085 char *mounthost;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001086 /* prior to 2.6.23, kernel took NFS options in a form of this struct
1087 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1088 * then data pointer is interpreted as a string. */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001089 struct nfs_mount_data data;
1090 char *opt;
1091 struct hostent *hp;
1092 struct sockaddr_in server_addr;
1093 struct sockaddr_in mount_server_addr;
1094 int msock, fsock;
1095 union {
1096 struct fhstatus nfsv2;
1097 struct mountres3 nfsv3;
1098 } status;
1099 int daemonized;
1100 char *s;
1101 int port;
1102 int mountport;
1103 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001104#if BB_MMU
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001105 smallint bg = 0;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001106#else
1107 enum { bg = 0 };
1108#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001109 int retry;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001110 int mountprog;
1111 int mountvers;
1112 int nfsprog;
1113 int nfsvers;
1114 int retval;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001115 /* these all are one-bit really. gcc 4.3.1 likes this combination: */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001116 smallint tcp;
1117 smallint soft;
1118 int intr;
1119 int posix;
1120 int nocto;
1121 int noac;
1122 int nordirplus;
1123 int nolock;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001124
1125 find_kernel_nfs_mount_version();
1126
1127 daemonized = 0;
1128 mounthost = NULL;
1129 retval = ETIMEDOUT;
1130 msock = fsock = -1;
1131 mclient = NULL;
1132
1133 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1134
1135 filteropts = xstrdup(filteropts); /* going to trash it later... */
1136
1137 hostname = xstrdup(mp->mnt_fsname);
1138 /* mount_main() guarantees that ':' is there */
1139 s = strchr(hostname, ':');
1140 pathname = s + 1;
1141 *s = '\0';
1142 /* Ignore all but first hostname in replicated mounts
1143 until they can be fully supported. (mack@sgi.com) */
1144 s = strchr(hostname, ',');
1145 if (s) {
1146 *s = '\0';
1147 bb_error_msg("warning: multiple hostnames not supported");
1148 }
1149
1150 server_addr.sin_family = AF_INET;
1151 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1152 hp = gethostbyname(hostname);
1153 if (hp == NULL) {
1154 bb_herror_msg("%s", hostname);
1155 goto fail;
1156 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001157 if (hp->h_length != (int)sizeof(struct in_addr)) {
1158 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001159 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001160 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001161 }
1162
1163 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1164
1165 /* add IP address to mtab options for use when unmounting */
1166
1167 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1168 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1169 } else {
1170 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1171 mp->mnt_opts[0] ? "," : "",
1172 inet_ntoa(server_addr.sin_addr));
1173 free(mp->mnt_opts);
1174 mp->mnt_opts = tmp;
1175 }
1176
1177 /* Set default options.
1178 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1179 * let the kernel decide.
1180 * timeo is filled in after we know whether it'll be TCP or UDP. */
1181 memset(&data, 0, sizeof(data));
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001182 data.retrans = 3;
1183 data.acregmin = 3;
1184 data.acregmax = 60;
1185 data.acdirmin = 30;
1186 data.acdirmax = 60;
1187 data.namlen = NAME_MAX;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001188
Denis Vlasenko25098f72006-09-14 15:46:33 +00001189 soft = 0;
1190 intr = 0;
1191 posix = 0;
1192 nocto = 0;
1193 nolock = 0;
1194 noac = 0;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001195 nordirplus = 0;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001196 retry = 10000; /* 10000 minutes ~ 1 week */
Bernhard Reutner-Fischer88206292011-05-04 19:03:30 +02001197 tcp = 1; /* nfs-utils uses tcp per default */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001198
1199 mountprog = MOUNTPROG;
1200 mountvers = 0;
1201 port = 0;
1202 mountport = 0;
1203 nfsprog = 100003;
1204 nfsvers = 0;
1205
1206 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001207 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001208 char *opteq = strchr(opt, '=');
1209 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001210 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001211 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001212 /* 0 */ "rsize\0"
1213 /* 1 */ "wsize\0"
1214 /* 2 */ "timeo\0"
1215 /* 3 */ "retrans\0"
1216 /* 4 */ "acregmin\0"
1217 /* 5 */ "acregmax\0"
1218 /* 6 */ "acdirmin\0"
1219 /* 7 */ "acdirmax\0"
1220 /* 8 */ "actimeo\0"
1221 /* 9 */ "retry\0"
1222 /* 10 */ "port\0"
1223 /* 11 */ "mountport\0"
1224 /* 12 */ "mounthost\0"
1225 /* 13 */ "mountprog\0"
1226 /* 14 */ "mountvers\0"
1227 /* 15 */ "nfsprog\0"
1228 /* 16 */ "nfsvers\0"
1229 /* 17 */ "vers\0"
1230 /* 18 */ "proto\0"
1231 /* 19 */ "namlen\0"
1232 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001233
1234 *opteq++ = '\0';
1235 idx = index_in_strings(options, opt);
1236 switch (idx) {
1237 case 12: // "mounthost"
1238 mounthost = xstrndup(opteq,
1239 strcspn(opteq, " \t\n\r,"));
1240 continue;
1241 case 18: // "proto"
1242 if (!strncmp(opteq, "tcp", 3))
1243 tcp = 1;
1244 else if (!strncmp(opteq, "udp", 3))
1245 tcp = 0;
1246 else
1247 bb_error_msg("warning: unrecognized proto= option");
1248 continue;
1249 case 20: // "addr" - ignore
1250 continue;
Peter Korsgaard301fe502011-02-21 17:52:13 +01001251 case -1: // unknown
1252 if (vfsflags & MS_REMOUNT)
1253 continue;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001254 }
1255
Denys Vlasenko77832482010-08-12 14:14:45 +02001256 val = xatoi_positive(opteq);
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001257 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001258 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001259 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001260 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001261 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001262 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001263 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001264 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001265 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001266 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001267 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001268 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001269 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001270 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001271 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001272 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001273 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001274 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001275 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001276 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001277 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001278 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001279 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001280 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001281 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001282 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001283 data.acregmin = val;
1284 data.acregmax = val;
1285 data.acdirmin = val;
1286 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001287 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001288 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001289 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001290 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001291 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001292 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001293 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001294 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001295 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001296 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001297 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001298 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001299 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001300 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001301 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001302 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001303 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001304 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001305 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001306 case 16: // "nfsvers"
1307 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001308 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001309 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001310 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001311 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001312 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001313 //else
1314 // bb_error_msg("warning: option namlen is not supported\n");
1315 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001316 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001317 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1318 goto fail;
1319 }
1320 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001321 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001322 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001323 "bg\0"
1324 "fg\0"
1325 "soft\0"
1326 "hard\0"
1327 "intr\0"
1328 "posix\0"
1329 "cto\0"
1330 "ac\0"
1331 "tcp\0"
1332 "udp\0"
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001333 "lock\0"
1334 "rdirplus\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001335 int val = 1;
1336 if (!strncmp(opt, "no", 2)) {
1337 val = 0;
1338 opt += 2;
1339 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001340 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001341 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001342#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001343 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001344#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001345 break;
1346 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001347#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001348 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001349#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001350 break;
1351 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001352 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001353 break;
1354 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001355 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001356 break;
1357 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001358 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001359 break;
1360 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001361 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001362 break;
1363 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001364 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001365 break;
1366 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001367 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001368 break;
1369 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001370 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001371 break;
1372 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001373 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001374 break;
1375 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001376 if (nfs_mount_version >= 3)
1377 nolock = !val;
1378 else
1379 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001380 break;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001381 case 11: //rdirplus
1382 nordirplus = !val;
1383 break;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001384 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001385 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1386 goto fail;
1387 }
1388 }
1389 }
1390 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1391
1392 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1393 | (intr ? NFS_MOUNT_INTR : 0)
1394 | (posix ? NFS_MOUNT_POSIX : 0)
1395 | (nocto ? NFS_MOUNT_NOCTO : 0)
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001396 | (noac ? NFS_MOUNT_NOAC : 0)
1397 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001398 if (nfs_mount_version >= 2)
1399 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1400 if (nfs_mount_version >= 3)
1401 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1402 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1403 bb_error_msg("NFSv%d not supported", nfsvers);
1404 goto fail;
1405 }
1406 if (nfsvers && !mountvers)
1407 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1408 if (nfsvers && nfsvers < mountvers) {
1409 mountvers = nfsvers;
1410 }
1411
1412 /* Adjust options if none specified */
1413 if (!data.timeo)
1414 data.timeo = tcp ? 70 : 7;
1415
Denis Vlasenko25098f72006-09-14 15:46:33 +00001416 data.version = nfs_mount_version;
1417
1418 if (vfsflags & MS_REMOUNT)
1419 goto do_mount;
1420
1421 /*
1422 * If the previous mount operation on the same host was
1423 * backgrounded, and the "bg" for this mount is also set,
1424 * give up immediately, to avoid the initial timeout.
1425 */
1426 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001427 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001428 if (daemonized <= 0) { /* parent or error */
1429 retval = -daemonized;
1430 goto ret;
1431 }
1432 }
1433
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001434 /* Create mount daemon client */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001435 /* See if the nfs host = mount host. */
1436 if (mounthost) {
1437 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1438 mount_server_addr.sin_family = AF_INET;
1439 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1440 } else {
1441 hp = gethostbyname(mounthost);
1442 if (hp == NULL) {
1443 bb_herror_msg("%s", mounthost);
1444 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001445 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001446 if (hp->h_length != (int)sizeof(struct in_addr)) {
1447 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001448 }
1449 mount_server_addr.sin_family = AF_INET;
Denys Vlasenko99069332010-02-27 19:38:19 +01001450 memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001451 }
1452 }
1453
1454 /*
1455 * The following loop implements the mount retries. When the mount
1456 * times out, and the "bg" option is set, we background ourself
1457 * and continue trying.
1458 *
1459 * The case where the mount point is not present and the "bg"
1460 * option is set, is treated as a timeout. This is done to
1461 * support nested mounts.
1462 *
1463 * The "retry" count specified by the user is the number of
1464 * minutes to retry before giving up.
1465 */
1466 {
1467 struct timeval total_timeout;
1468 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001469 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001470 time_t t;
1471 time_t prevt;
1472 time_t timeout;
1473
1474 retry_timeout.tv_sec = 3;
1475 retry_timeout.tv_usec = 0;
1476 total_timeout.tv_sec = 20;
1477 total_timeout.tv_usec = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001478/* FIXME: use monotonic()? */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001479 timeout = time(NULL) + 60 * retry;
1480 prevt = 0;
1481 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001482 retry:
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001483 /* Be careful not to use too many CPU cycles */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001484 if (t - prevt < 30)
1485 sleep(30);
1486
Denis Vlasenkob9256052007-09-28 10:29:17 +00001487 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001488 mountprog,
1489 mountvers,
1490 proto,
1491 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001492 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001493
1494 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001495 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001496 msock = RPC_ANYSOCK;
1497
Denis Vlasenkob9256052007-09-28 10:29:17 +00001498 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001499 case IPPROTO_UDP:
1500 mclient = clntudp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001501 pm_mnt.pm_prog,
1502 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001503 retry_timeout,
1504 &msock);
1505 if (mclient)
1506 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001507 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001508 msock = RPC_ANYSOCK;
1509 case IPPROTO_TCP:
1510 mclient = clnttcp_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 &msock, 0, 0);
1514 break;
1515 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001516 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001517 }
1518 if (!mclient) {
1519 if (!daemonized && prevt == 0)
1520 error_msg_rpc(clnt_spcreateerror(" "));
1521 } else {
1522 enum clnt_stat clnt_stat;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001523
1524 /* Try to mount hostname:pathname */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001525 mclient->cl_auth = authunix_create_default();
1526
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001527 /* Make pointers in xdr_mountres3 NULL so
Denis Vlasenko25098f72006-09-14 15:46:33 +00001528 * that xdr_array allocates memory for us
1529 */
1530 memset(&status, 0, sizeof(status));
1531
Denis Vlasenkob9256052007-09-28 10:29:17 +00001532 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001533 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1534 (xdrproc_t) xdr_dirpath,
1535 (caddr_t) &pathname,
1536 (xdrproc_t) xdr_mountres3,
1537 (caddr_t) &status,
1538 total_timeout);
1539 else
1540 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1541 (xdrproc_t) xdr_dirpath,
1542 (caddr_t) &pathname,
1543 (xdrproc_t) xdr_fhstatus,
1544 (caddr_t) &status,
1545 total_timeout);
1546
1547 if (clnt_stat == RPC_SUCCESS)
1548 goto prepare_kernel_data; /* we're done */
1549 if (errno != ECONNREFUSED) {
1550 error_msg_rpc(clnt_sperror(mclient, " "));
1551 goto fail; /* don't retry */
1552 }
1553 /* Connection refused */
1554 if (!daemonized && prevt == 0) /* print just once */
1555 error_msg_rpc(clnt_sperror(mclient, " "));
1556 auth_destroy(mclient->cl_auth);
1557 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001558 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001559 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001560 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001561 }
1562
1563 /* Timeout. We are going to retry... maybe */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001564 if (!bg)
1565 goto fail;
1566 if (!daemonized) {
1567 daemonized = daemonize();
1568 if (daemonized <= 0) { /* parent or error */
1569 retval = -daemonized;
1570 goto ret;
1571 }
1572 }
1573 prevt = t;
1574 t = time(NULL);
1575 if (t >= timeout)
1576 /* TODO error message */
1577 goto fail;
1578
1579 goto retry;
1580 }
1581
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001582 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001583
1584 if (nfsvers == 2) {
1585 if (status.nfsv2.fhs_status != 0) {
1586 bb_error_msg("%s:%s failed, reason given by server: %s",
1587 hostname, pathname,
1588 nfs_strerror(status.nfsv2.fhs_status));
1589 goto fail;
1590 }
1591 memcpy(data.root.data,
1592 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1593 NFS_FHSIZE);
1594 data.root.size = NFS_FHSIZE;
1595 memcpy(data.old_root.data,
1596 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1597 NFS_FHSIZE);
1598 } else {
1599 fhandle3 *my_fhandle;
1600 if (status.nfsv3.fhs_status != 0) {
1601 bb_error_msg("%s:%s failed, reason given by server: %s",
1602 hostname, pathname,
1603 nfs_strerror(status.nfsv3.fhs_status));
1604 goto fail;
1605 }
1606 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1607 memset(data.old_root.data, 0, NFS_FHSIZE);
1608 memset(&data.root, 0, sizeof(data.root));
1609 data.root.size = my_fhandle->fhandle3_len;
1610 memcpy(data.root.data,
1611 (char *) my_fhandle->fhandle3_val,
1612 my_fhandle->fhandle3_len);
1613
1614 data.flags |= NFS_MOUNT_VER3;
1615 }
1616
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001617 /* Create nfs socket for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001618 if (tcp) {
1619 if (nfs_mount_version < 3) {
1620 bb_error_msg("NFS over TCP is not supported");
1621 goto fail;
1622 }
1623 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1624 } else
1625 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1626 if (fsock < 0) {
1627 bb_perror_msg("nfs socket");
1628 goto fail;
1629 }
1630 if (bindresvport(fsock, 0) < 0) {
1631 bb_perror_msg("nfs bindresvport");
1632 goto fail;
1633 }
1634 if (port == 0) {
1635 server_addr.sin_port = PMAPPORT;
1636 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1637 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1638 if (port == 0)
1639 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001640 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001641 server_addr.sin_port = htons(port);
1642
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001643 /* Prepare data structure for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001644 data.fd = fsock;
1645 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1646 strncpy(data.hostname, hostname, sizeof(data.hostname));
1647
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001648 /* Clean up */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001649 auth_destroy(mclient->cl_auth);
1650 clnt_destroy(mclient);
1651 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001652 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001653
1654 if (bg) {
1655 /* We must wait until mount directory is available */
1656 struct stat statbuf;
1657 int delay = 1;
1658 while (stat(mp->mnt_dir, &statbuf) == -1) {
1659 if (!daemonized) {
1660 daemonized = daemonize();
1661 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001662/* FIXME: parent doesn't close fsock - ??! */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001663 retval = -daemonized;
1664 goto ret;
1665 }
1666 }
1667 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1668 delay *= 2;
1669 if (delay > 30)
1670 delay = 30;
1671 }
1672 }
1673
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001674 /* Perform actual mount */
1675 do_mount:
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001676 mp->mnt_type = (char*)"nfs";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001677 retval = mount_it_now(mp, vfsflags, (char*)&data);
1678 goto ret;
1679
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001680 /* Abort */
1681 fail:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001682 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001683 if (mclient) {
1684 auth_destroy(mclient->cl_auth);
1685 clnt_destroy(mclient);
1686 }
1687 close(msock);
1688 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001689 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001690 close(fsock);
1691
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001692 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001693 free(hostname);
1694 free(mounthost);
1695 free(filteropts);
1696 return retval;
1697}
1698
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001699#else // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001700
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001701// Never called. Call should be optimized out.
Denis Vlasenkob4133682008-02-18 13:05:38 +00001702int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001703
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001704#endif // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001705
1706// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1707// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001708// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001709static int singlemount(struct mntent *mp, int ignore_busy)
1710{
Denis Vlasenkob4133682008-02-18 13:05:38 +00001711 int rc = -1;
1712 long vfsflags;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001713 char *loopFile = NULL, *filteropts = NULL;
1714 llist_t *fl = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001715 struct stat st;
1716
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001717 errno = 0;
1718
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001719 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1720
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001721 // Treat fstype "auto" as unspecified
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001722 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1723 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001724
Denis Vlasenko2535f122007-09-15 13:28:30 +00001725 // Might this be a virtual filesystem?
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001726 if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1727 char *args[35];
1728 char *s;
1729 int n;
1730 // fsname: "cmd#arg1#arg2..."
1731 // WARNING: allows execution of arbitrary commands!
1732 // Try "mount 'sh#-c#sh' bogus_dir".
1733 // It is safe ONLY because non-root
1734 // cannot use two-argument mount command
1735 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1736 // "mount: can't find sh#-c#sh in /etc/fstab"
1737 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1738
1739 s = mp->mnt_fsname;
1740 n = 0;
1741 args[n++] = s;
1742 while (*s && n < 35 - 2) {
1743 if (*s++ == '#' && *s != '#') {
1744 s[-1] = '\0';
1745 args[n++] = s;
Denis Vlasenko2535f122007-09-15 13:28:30 +00001746 }
1747 }
Denis Vlasenko2535f122007-09-15 13:28:30 +00001748 args[n++] = mp->mnt_dir;
1749 args[n] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +01001750 rc = spawn_and_wait(args);
Denis Vlasenko2535f122007-09-15 13:28:30 +00001751 goto report_error;
1752 }
1753
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001754 // Might this be an CIFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001755 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001756 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1757 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1758 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001759 ) {
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001760 int len;
1761 char c;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001762 len_and_sockaddr *lsa;
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001763 char *hostname, *dotted, *ip;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001764
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001765 hostname = mp->mnt_fsname + 2;
1766 len = strcspn(hostname, "/\\");
1767 if (len == 0 || hostname[len] == '\0')
Denis Vlasenko5c329932009-04-12 12:16:21 +00001768 goto report_error;
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001769 c = hostname[len];
1770 hostname[len] = '\0';
1771 lsa = host2sockaddr(hostname, 0);
1772 hostname[len] = c;
Denis Vlasenko5c329932009-04-12 12:16:21 +00001773 if (!lsa)
1774 goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001775
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001776 // Insert "ip=..." option into options
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001777 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001778 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001779 ip = xasprintf("ip=%s", dotted);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001780 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001781 parse_mount_options(ip, &filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001782 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001783
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001784 // "-o mand" is required [why?]
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001785 vfsflags |= MS_MANDLOCK;
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001786 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001787 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001788
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001789 goto report_error;
1790 }
1791
1792 // Might this be an NFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001793 if (ENABLE_FEATURE_MOUNT_NFS
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001794 && (!mp->mnt_type || strcmp(mp->mnt_type, "nfs") == 0)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001795 && strchr(mp->mnt_fsname, ':') != NULL
1796 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001797 rc = nfsmount(mp, vfsflags, filteropts);
1798 goto report_error;
1799 }
1800
1801 // Look at the file. (Not found isn't a failure for remount, or for
1802 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001803 // (We use stat, not lstat, in order to allow
1804 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001805 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001806 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1807 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001808 // Do we need to allocate a loopback device for it?
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001809 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1810 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001811 mp->mnt_fsname = NULL; // will receive malloced loop dev name
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001812 if (set_loop(&mp->mnt_fsname, loopFile, 0) < 0) {
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001813 if (errno == EPERM || errno == EACCES)
1814 bb_error_msg(bb_msg_perm_denied_are_you_root);
1815 else
Denys Vlasenko6331cf02009-11-13 09:08:27 +01001816 bb_perror_msg("can't setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001817 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001818 }
1819
1820 // Autodetect bind mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001821 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1822 vfsflags |= MS_BIND;
1823 }
1824
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001825 // If we know the fstype (or don't need to), jump straight
1826 // to the actual mount.
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001827 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001828 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001829 } else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001830 // Loop through filesystem types until mount succeeds
1831 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001832
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001833 // Initialize list of block backed filesystems.
1834 // This has to be done here so that during "mount -a",
1835 // mounts after /proc shows up can autodetect.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001836 if (!fslist) {
1837 fslist = get_block_backed_filesystems();
1838 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1839 atexit(delete_block_backed_filesystems);
1840 }
1841
1842 for (fl = fslist; fl; fl = fl->link) {
1843 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001844 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001845 if (!rc)
1846 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001847 }
1848 }
1849
1850 // If mount failed, clean up loop file (if any).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001851 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1852 del_loop(mp->mnt_fsname);
1853 if (ENABLE_FEATURE_CLEAN_UP) {
1854 free(loopFile);
1855 free(mp->mnt_fsname);
1856 }
1857 }
1858
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001859 report_error:
1860 if (ENABLE_FEATURE_CLEAN_UP)
1861 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001862
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001863 if (errno == EBUSY && ignore_busy)
1864 return 0;
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001865 if (rc != 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001866 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001867 return rc;
1868}
1869
Michael Abbott6b5accb2009-12-04 03:33:07 +01001870// -O support
1871// -O interprets a list of filter options which select whether a mount
1872// point will be mounted: only mounts with options matching *all* filtering
1873// options will be selected.
1874// By default each -O filter option must be present in the list of mount
1875// options, but if it is prefixed by "no" then it must be absent.
1876// For example,
1877// -O a,nob,c matches -o a,c but fails to match -o a,b,c
1878// (and also fails to match -o a because -o c is absent).
1879//
1880// It is different from -t in that each option is matched exactly; a leading
1881// "no" at the beginning of one option does not negate the rest.
1882static int match_opt(const char *fs_opt_in, const char *O_opt)
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001883{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001884 if (!O_opt)
Michael Abbott6b5accb2009-12-04 03:33:07 +01001885 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001886
Michael Abbott6b5accb2009-12-04 03:33:07 +01001887 while (*O_opt) {
1888 const char *fs_opt = fs_opt_in;
1889 int O_len;
1890 int match;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001891
Michael Abbott6b5accb2009-12-04 03:33:07 +01001892 // If option begins with "no" then treat as an inverted match:
1893 // matching is a failure
1894 match = 0;
1895 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
1896 match = 1;
1897 O_opt += 2;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001898 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01001899 // Isolate the current O option
1900 O_len = strchrnul(O_opt, ',') - O_opt;
1901 // Check for a match against existing options
1902 while (1) {
1903 if (strncmp(fs_opt, O_opt, O_len) == 0
1904 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
1905 ) {
1906 if (match)
1907 return 0; // "no" prefix, but option found
1908 match = 1; // current O option found, go check next one
1909 break;
1910 }
1911 fs_opt = strchr(fs_opt, ',');
1912 if (!fs_opt)
1913 break;
1914 fs_opt++;
1915 }
1916 if (match == 0)
1917 return 0; // match wanted but not found
1918 if (O_opt[O_len] == '\0') // end?
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001919 break;
Michael Abbott6b5accb2009-12-04 03:33:07 +01001920 // Step to the next O option
1921 O_opt += O_len + 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001922 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01001923 // If we get here then everything matched
1924 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001925}
1926
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001927// Parse options, if necessary parse fstab/mtab, and call singlemount for
1928// each directory to be mounted.
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001929int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001930int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001931{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001932 char *cmdopts = xzalloc(1);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001933 char *fstype = NULL;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001934 char *O_optmatch = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001935 char *storage_path;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001936 llist_t *lst_o = NULL;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001937 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001938 FILE *fstab;
Denys Vlasenkoa7329662009-12-05 04:25:19 +01001939 int i, j;
1940 int rc = EXIT_SUCCESS;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001941 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001942 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001943 IF_NOT_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001944
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001945 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00001946
Denis Vlasenkof732e962008-02-18 12:07:49 +00001947 // Parse long options, like --bind and --move. Note that -o option
1948 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001949 for (i = j = 1; argv[i]; i++) {
1950 if (argv[i][0] == '-' && argv[i][1] == '-')
1951 append_mount_options(&cmdopts, argv[i] + 2);
1952 else
1953 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001954 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001955 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001956
1957 // Parse remaining options
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001958 // Max 2 params; -o is a list, -v is a counter
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001959 opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001960 opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001961 IF_FEATURE_MOUNT_VERBOSE(, &verbose));
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001962 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00001963 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
1964 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001965 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001966
1967 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001968 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00001969 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001970 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1971
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001972 if (!mountTable)
1973 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001974
Denis Vlasenko2535f122007-09-15 13:28:30 +00001975 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001976 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001977 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001978 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00001979 // util-linux 2.12a happily shows rootfs...
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001980 //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001981
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001982 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001983 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1984 mtpair->mnt_dir, mtpair->mnt_type,
1985 mtpair->mnt_opts);
1986 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001987 if (ENABLE_FEATURE_CLEAN_UP)
1988 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001989 return EXIT_SUCCESS;
1990 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001991 storage_path = NULL;
1992 } else {
1993 // When we have two arguments, the second is the directory and we can
1994 // skip looking at fstab entirely. We can always abspath() the directory
1995 // argument when we get it.
1996 if (argv[1]) {
1997 if (nonroot)
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01001998 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001999 mtpair->mnt_fsname = argv[0];
2000 mtpair->mnt_dir = argv[1];
2001 mtpair->mnt_type = fstype;
2002 mtpair->mnt_opts = cmdopts;
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002003 resolve_mount_spec(&mtpair->mnt_fsname);
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002004 rc = singlemount(mtpair, /*ignore_busy:*/ 0);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002005 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00002006 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002007 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002008 }
2009
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002010 // Past this point, we are handling either "mount -a [opts]"
2011 // or "mount [opts] single_param"
2012
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002013 i = parse_mount_options(cmdopts, NULL); // FIXME: should be "long", not "int"
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002014 if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002015 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00002016
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002017 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002018 if (ENABLE_FEATURE_MOUNT_FLAGS
2019 && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
2020 ) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002021 // verbose_mount(source, target, type, flags, data)
2022 rc = verbose_mount("", argv[0], "", i, "");
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002023 if (rc)
2024 bb_simple_perror_msg_and_die(argv[0]);
2025 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002026 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00002027
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002028 // Open either fstab or mtab
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002029 fstabname = "/etc/fstab";
2030 if (i & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002031 // WARNING. I am not sure this matches util-linux's
2032 // behavior. It's possible util-linux does not
2033 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002034 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002035 }
2036 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00002037 if (!fstab)
Denys Vlasenko651a2692010-03-23 16:25:17 +01002038 bb_perror_msg_and_die("can't read '%s'", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002039
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002040 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00002041 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002042 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002043 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002044
2045 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00002046 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002047 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002048 GETMNTENT_BUFSIZE/2)
2049 ) { // End of fstab/mtab is reached
2050 mtcur = mtother; // the thing we found last time
2051 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002052 }
2053
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002054 // If we're trying to mount something specific and this isn't it,
2055 // skip it. Note we must match the exact text in fstab (ala
2056 // "proc") or a full path from root
2057 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002058
2059 // Is this what we're looking for?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002060 if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2061 && strcmp(storage_path, mtcur->mnt_fsname) != 0
2062 && strcmp(argv[0], mtcur->mnt_dir) != 0
2063 && strcmp(storage_path, mtcur->mnt_dir) != 0
2064 ) {
2065 continue; // no
2066 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002067
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002068 // Remember this entry. Something later may have
2069 // overmounted it, and we want the _last_ match.
2070 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002071
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002072 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002073 } else {
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002074 struct mntent *mp;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002075 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002076 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002077 if (nonroot)
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002078 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002079
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002080 // Does type match? (NULL matches always)
2081 if (!match_fstype(mtcur, fstype))
2082 continue;
2083
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002084 // Skip noauto and swap anyway
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002085 if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2086 // swap is bogus "fstype", parse_mount_options can't check fstypes
2087 || strcasecmp(mtcur->mnt_type, "swap") == 0
2088 ) {
2089 continue;
2090 }
2091
2092 // Does (at least one) option match?
2093 // (NULL matches always)
2094 if (!match_opt(mtcur->mnt_opts, O_optmatch))
2095 continue;
2096
2097 resolve_mount_spec(&mtcur->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002098
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002099 // NFS mounts want this to be xrealloc-able
2100 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002101
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002102 // If nothing is mounted on this directory...
2103 // (otherwise repeated "mount -a" mounts everything again)
2104 mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2105 // We do not check fsname match of found mount point -
2106 // "/" may have fsname of "/dev/root" while fstab
2107 // says "/dev/something_else".
2108 if (mp) {
Denys Vlasenko86566762009-12-10 21:32:28 +01002109 if (verbose) {
2110 bb_error_msg("according to %s, "
2111 "%s is already mounted on %s",
2112 bb_path_mtab_file,
2113 mp->mnt_fsname, mp->mnt_dir);
2114 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002115 } else {
2116 // ...mount this thing
2117 if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2118 // Count number of failed mounts
2119 rc++;
2120 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002121 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002122 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002123 }
2124 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002125
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002126 // End of fstab/mtab is reached.
2127 // Were we looking for something specific?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002128 if (argv[0]) { // yes
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002129 long l;
2130
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002131 // If we didn't find anything, complain
2132 if (!mtcur->mnt_fsname)
2133 bb_error_msg_and_die("can't find %s in %s",
2134 argv[0], fstabname);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002135
2136 // What happens when we try to "mount swap_partition"?
2137 // (fstab containts "swap_partition swap swap defaults 0 0")
2138 // util-linux-ng 2.13.1 does this:
2139 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2140 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2141 // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory)
2142 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2143 // exit_group(32) = ?
2144#if 0
2145 // In case we want to simply skip swap partitions:
2146 l = parse_mount_options(mtcur->mnt_opts, NULL);
2147 if ((l & MOUNT_SWAP)
2148 // swap is bogus "fstype", parse_mount_options can't check fstypes
2149 || strcasecmp(mtcur->mnt_type, "swap") == 0
2150 ) {
2151 goto ret;
2152 }
2153#endif
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002154 if (nonroot) {
2155 // fstab must have "users" or "user"
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002156 l = parse_mount_options(mtcur->mnt_opts, NULL);
2157 if (!(l & MOUNT_USERS))
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002158 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002159 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002160
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002161 //util-linux-2.12 does not do this check.
2162 //// If nothing is mounted on this directory...
2163 //// (otherwise repeated "mount FOO" mounts FOO again)
2164 //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2165 //if (mp) {
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 //} else {
2171 // ...mount the last thing we found
2172 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2173 append_mount_options(&(mtcur->mnt_opts), cmdopts);
2174 resolve_mount_spec(&mtpair->mnt_fsname);
2175 rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2176 if (ENABLE_FEATURE_CLEAN_UP)
2177 free(mtcur->mnt_opts);
2178 //}
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002179 }
2180
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002181 //ret:
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002182 if (ENABLE_FEATURE_CLEAN_UP)
2183 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002184 if (ENABLE_FEATURE_CLEAN_UP) {
2185 free(storage_path);
2186 free(cmdopts);
2187 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002188
2189//TODO: exitcode should be ORed mask of (from "man mount"):
2190// 0 success
2191// 1 incorrect invocation or permissions
2192// 2 system error (out of memory, cannot fork, no more loop devices)
2193// 4 internal mount bug or missing nfs support in mount
2194// 8 user interrupt
2195//16 problems writing or locking /etc/mtab
2196//32 mount failure
2197//64 some mount succeeded
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002198 return rc;
2199}