blob: 9f465f131d107363417f99706231020d6c1fb23c [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 *
Rob Landley7b363fd2005-12-20 17:18:01 +00009 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
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//
Eric Andersencc8ed391999-10-05 16:24:54 +000019#include <mntent.h>
Bernhard Reutner-Fischerf4701962008-01-27 12:50:12 +000020#include <syslog.h>
Denys Vlasenkoda49f582009-07-08 02:58:38 +020021#include <sys/mount.h>
22#ifndef MS_BIND
23# define MS_BIND (1 << 12)
24#endif
25#ifndef MS_MOVE
26# define MS_MOVE (1 << 13)
27#endif
28#ifndef MS_RECURSIVE
29# define MS_RECURSIVE (1 << 14)
30#endif
31#ifndef MS_SILENT
32# define MS_SILENT (1 << 15)
33#endif
34/* The shared subtree stuff, which went in around 2.6.15. */
35#ifndef MS_UNBINDABLE
36# define MS_UNBINDABLE (1 << 17)
37#endif
38#ifndef MS_PRIVATE
39# define MS_PRIVATE (1 << 18)
40#endif
41#ifndef MS_SLAVE
42# define MS_SLAVE (1 << 19)
43#endif
44#ifndef MS_SHARED
45# define MS_SHARED (1 << 20)
46#endif
47#ifndef MS_RELATIME
48# define MS_RELATIME (1 << 21)
49#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +000050#include "libbb.h"
Eric Andersenbd22ed82000-07-08 18:55:24 +000051
Denis Vlasenko6aa76962008-03-18 01:44:52 +000052#if ENABLE_FEATURE_MOUNT_LABEL
Natanael Copa9aff2992009-09-20 04:28:22 +020053# include "volume_id.h"
54#else
55# define resolve_mount_spec(fsname) ((void)0)
Denis Vlasenko6aa76962008-03-18 01:44:52 +000056#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +000057
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000058// Needed for nfs support only
Denis Vlasenko30a64cd2006-09-15 15:12:00 +000059#include <sys/utsname.h>
60#undef TRUE
61#undef FALSE
62#include <rpc/rpc.h>
63#include <rpc/pmap_prot.h>
64#include <rpc/pmap_clnt.h>
65
Denis Vlasenko2535f122007-09-15 13:28:30 +000066#ifndef MS_SILENT
67#define MS_SILENT (1 << 15)
68#endif
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000069// Grab more as needed from util-linux's mount/mount_constants.h
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +000070#ifndef MS_DIRSYNC
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000071#define MS_DIRSYNC 128 // Directory modifications are synchronous
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +000072#endif
73
Denis Vlasenko30a64cd2006-09-15 15:12:00 +000074
Denis Vlasenko908d6b72006-12-18 23:07:42 +000075#if defined(__dietlibc__)
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000076// 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
77// dietlibc-0.30 does not have implementation of getmntent_r()
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +000078static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000079 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
Denis Vlasenko908d6b72006-12-18 23:07:42 +000080{
Denis Vlasenko908d6b72006-12-18 23:07:42 +000081 struct mntent* ment = getmntent(stream);
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +000082 return memcpy(result, ment, sizeof(*ment));
Denis Vlasenko908d6b72006-12-18 23:07:42 +000083}
84#endif
85
86
Rob Landleydc0955b2006-03-14 18:16:25 +000087// Not real flags, but we want to be able to check for this.
Denis Vlasenko13c5a682006-10-16 22:39:51 +000088enum {
Denis Vlasenkoa4522c52008-03-17 08:46:43 +000089 MOUNT_USERS = (1 << 28) * ENABLE_DESKTOP,
90 MOUNT_NOAUTO = (1 << 29),
91 MOUNT_SWAP = (1 << 30),
Denis Vlasenko13c5a682006-10-16 22:39:51 +000092};
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +000093
94
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +000095#define OPTION_STR "o:t:rwanfvsiO:"
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +000096enum {
97 OPT_o = (1 << 0),
98 OPT_t = (1 << 1),
99 OPT_r = (1 << 2),
100 OPT_w = (1 << 3),
101 OPT_a = (1 << 4),
102 OPT_n = (1 << 5),
103 OPT_f = (1 << 6),
104 OPT_v = (1 << 7),
105 OPT_s = (1 << 8),
106 OPT_i = (1 << 9),
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +0000107 OPT_O = (1 << 10),
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000108};
109
110#if ENABLE_FEATURE_MTAB_SUPPORT
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200111#define USE_MTAB (!(option_mask32 & OPT_n))
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000112#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200113#define USE_MTAB 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000114#endif
115
116#if ENABLE_FEATURE_MOUNT_FAKE
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200117#define FAKE_IT (option_mask32 & OPT_f)
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000118#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200119#define FAKE_IT 0
120#endif
121
122#if ENABLE_FEATURE_MOUNT_HELPERS
123#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
124#else
125#define HELPERS_ALLOWED 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000126#endif
127
128
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000129// TODO: more "user" flag compatibility.
130// "user" option (from mount manpage):
131// Only the user that mounted a filesystem can unmount it again.
132// If any user should be able to unmount, then use users instead of user
133// in the fstab line. The owner option is similar to the user option,
134// with the restriction that the user must be the owner of the special file.
135// This may be useful e.g. for /dev/fd if a login script makes
136// the console user owner of this device.
Rob Landley3ba7bd12006-08-09 19:51:13 +0000137
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000138// Standard mount options (from -o options or --options),
139// with corresponding flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000140static const int32_t mount_options[] = {
Rob Landleye3781b72006-08-08 01:39:49 +0000141 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
Rob Landleydc0955b2006-03-14 18:16:25 +0000142
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000143 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000144 /* "loop" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000145 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000146
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000147 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000148 /* "defaults" */ 0,
149 /* "quiet" 0 - do not filter out, vfat wants to see it */
150 /* "noauto" */ MOUNT_NOAUTO,
151 /* "sw" */ MOUNT_SWAP,
152 /* "swap" */ MOUNT_SWAP,
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000153 IF_DESKTOP(/* "user" */ MOUNT_USERS,)
154 IF_DESKTOP(/* "users" */ MOUNT_USERS,)
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000155 /* "_netdev" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000156 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000157
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000158 IF_FEATURE_MOUNT_FLAGS(
Rob Landleye3781b72006-08-08 01:39:49 +0000159 // vfs flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000160 /* "nosuid" */ MS_NOSUID,
161 /* "suid" */ ~MS_NOSUID,
162 /* "dev" */ ~MS_NODEV,
163 /* "nodev" */ MS_NODEV,
164 /* "exec" */ ~MS_NOEXEC,
165 /* "noexec" */ MS_NOEXEC,
166 /* "sync" */ MS_SYNCHRONOUS,
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +0000167 /* "dirsync" */ MS_DIRSYNC,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000168 /* "async" */ ~MS_SYNCHRONOUS,
169 /* "atime" */ ~MS_NOATIME,
170 /* "noatime" */ MS_NOATIME,
171 /* "diratime" */ ~MS_NODIRATIME,
172 /* "nodiratime" */ MS_NODIRATIME,
Denis Vlasenko580ce2d2008-07-08 02:56:53 +0000173 /* "mand" */ MS_MANDLOCK,
174 /* "nomand" */ ~MS_MANDLOCK,
Bernhard Reutner-Fischerfb5902c2008-08-06 18:14:38 +0000175 /* "relatime" */ MS_RELATIME,
176 /* "norelatime" */ ~MS_RELATIME,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000177 /* "loud" */ ~MS_SILENT,
Eric Andersen9601a1c2006-03-20 18:07:50 +0000178
Rob Landleye3781b72006-08-08 01:39:49 +0000179 // action flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000180 /* "bind" */ MS_BIND,
181 /* "move" */ MS_MOVE,
182 /* "shared" */ MS_SHARED,
183 /* "slave" */ MS_SLAVE,
184 /* "private" */ MS_PRIVATE,
185 /* "unbindable" */ MS_UNBINDABLE,
186 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
187 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
188 /* "rprivate" */ MS_SLAVE|MS_RECURSIVE,
189 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
Rob Landleye3781b72006-08-08 01:39:49 +0000190 )
191
192 // Always understood.
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000193 /* "ro" */ MS_RDONLY, // vfs flag
194 /* "rw" */ ~MS_RDONLY, // vfs flag
195 /* "remount" */ MS_REMOUNT // action flag
Eric Andersencc8ed391999-10-05 16:24:54 +0000196};
197
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000198static const char mount_option_str[] =
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000199 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000200 "loop\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000201 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000202 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000203 "defaults\0"
204 // "quiet\0" - do not filter out, vfat wants to see it
205 "noauto\0"
206 "sw\0"
207 "swap\0"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000208 IF_DESKTOP("user\0")
209 IF_DESKTOP("users\0")
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000210 "_netdev\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000211 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000212 IF_FEATURE_MOUNT_FLAGS(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000213 // vfs flags
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000214 "nosuid\0"
215 "suid\0"
216 "dev\0"
217 "nodev\0"
218 "exec\0"
219 "noexec\0"
220 "sync\0"
221 "dirsync\0"
222 "async\0"
223 "atime\0"
224 "noatime\0"
225 "diratime\0"
226 "nodiratime\0"
227 "mand\0"
228 "nomand\0"
229 "relatime\0"
230 "norelatime\0"
231 "loud\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000232
233 // action flags
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000234 "bind\0"
235 "move\0"
236 "shared\0"
237 "slave\0"
238 "private\0"
239 "unbindable\0"
240 "rshared\0"
241 "rslave\0"
242 "rprivate\0"
243 "runbindable\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000244 )
245
246 // Always understood.
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000247 "ro\0" // vfs flag
248 "rw\0" // vfs flag
249 "remount\0" // action flag
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000250;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000251
Denis Vlasenkof732e962008-02-18 12:07:49 +0000252
253struct globals {
254#if ENABLE_FEATURE_MOUNT_NFS
255 smalluint nfs_mount_version;
256#endif
257#if ENABLE_FEATURE_MOUNT_VERBOSE
258 unsigned verbose;
259#endif
260 llist_t *fslist;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000261 char getmntent_buf[1];
Denis Vlasenkof732e962008-02-18 12:07:49 +0000262
263};
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000264enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
Denis Vlasenkof732e962008-02-18 12:07:49 +0000265#define G (*(struct globals*)&bb_common_bufsiz1)
266#define nfs_mount_version (G.nfs_mount_version)
Denis Vlasenkob4133682008-02-18 13:05:38 +0000267#if ENABLE_FEATURE_MOUNT_VERBOSE
Denis Vlasenkof732e962008-02-18 12:07:49 +0000268#define verbose (G.verbose )
Denis Vlasenkob4133682008-02-18 13:05:38 +0000269#else
270#define verbose 0
271#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000272#define fslist (G.fslist )
273#define getmntent_buf (G.getmntent_buf )
274
275
276#if ENABLE_FEATURE_MOUNT_VERBOSE
277static int verbose_mount(const char *source, const char *target,
278 const char *filesystemtype,
279 unsigned long mountflags, const void *data)
280{
281 int rc;
282
283 errno = 0;
284 rc = mount(source, target, filesystemtype, mountflags, data);
285 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000286 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000287 source, target, filesystemtype,
288 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000289 return rc;
290}
291#else
292#define verbose_mount(...) mount(__VA_ARGS__)
293#endif
294
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000295// Append mount options to string
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000296static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000297{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000298 if (*oldopts && **oldopts) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000299 // Do not insert options which are already there
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000300 while (newopts[0]) {
301 char *p;
302 int len = strlen(newopts);
303 p = strchr(newopts, ',');
304 if (p) len = p - newopts;
305 p = *oldopts;
306 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000307 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000308 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000309 goto skip;
310 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000311 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000312 p++;
313 }
314 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
315 free(*oldopts);
316 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000317 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000318 newopts += len;
319 while (newopts[0] == ',') newopts++;
320 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000321 } else {
322 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000323 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000324 }
325}
326
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000327// Use the mount_options list to parse options into flags.
328// Also return list of unrecognized options if unrecognized != NULL
Denis Vlasenkob4133682008-02-18 13:05:38 +0000329static long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000330{
Denis Vlasenkob4133682008-02-18 13:05:38 +0000331 long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000332
Rob Landley6a6798b2005-08-10 20:35:54 +0000333 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000334 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000335 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000336 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000337 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000338
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000339 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000340
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000341// FIXME: use hasmntopt()
Rob Landley6a6798b2005-08-10 20:35:54 +0000342 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000343 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000344 if (!strcasecmp(option_str, options)) {
345 long fl = mount_options[i];
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000346 if (fl < 0) flags &= fl;
Rob Landleydc0955b2006-03-14 18:16:25 +0000347 else flags |= fl;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000348 break;
349 }
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000350 option_str += strlen(option_str) + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000351 }
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000352 // If unrecognized not NULL, append unrecognized mount options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000353 if (unrecognized && i == ARRAY_SIZE(mount_options)) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000354 // Add it to strflags, to pass on to kernel
Rob Landleydc0955b2006-03-14 18:16:25 +0000355 i = *unrecognized ? strlen(*unrecognized) : 0;
Denis Vlasenkodeeed592008-07-08 05:14:36 +0000356 *unrecognized = xrealloc(*unrecognized, i + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000357
Rob Landley6a6798b2005-08-10 20:35:54 +0000358 // Comma separated if it's not the first one
Rob Landleydc0955b2006-03-14 18:16:25 +0000359 if (i) (*unrecognized)[i++] = ',';
360 strcpy((*unrecognized)+i, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000361 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000362
Denis Vlasenko2535f122007-09-15 13:28:30 +0000363 if (!comma)
364 break;
365 // Advance to next option
366 *comma = ',';
367 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000368 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000369
Rob Landleydc0955b2006-03-14 18:16:25 +0000370 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000371}
372
Rob Landleydc0955b2006-03-14 18:16:25 +0000373// Return a list of all block device backed filesystems
Rob Landleydc0955b2006-03-14 18:16:25 +0000374static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000375{
Denis Vlasenko87468852007-04-13 23:22:00 +0000376 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000377 "/etc/filesystems",
378 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000379 };
380 char *fs, *buf;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200381 llist_t *list = NULL;
Rob Landleydc0955b2006-03-14 18:16:25 +0000382 int i;
383 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000384
Denis Vlasenko87468852007-04-13 23:22:00 +0000385 for (i = 0; i < 2; i++) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000386 f = fopen_for_read(filesystems[i]);
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000387 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000388
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000389 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200390 if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
Denis Vlasenko372686b2006-10-12 22:42:33 +0000391 continue;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000392 fs = skip_whitespace(buf);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200393 if (*fs == '#' || *fs == '*' || !*fs)
394 continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000395
Denis Vlasenko372686b2006-10-12 22:42:33 +0000396 llist_add_to_end(&list, xstrdup(fs));
397 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000398 }
399 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
400 }
401
402 return list;
403}
404
Rob Landleydc0955b2006-03-14 18:16:25 +0000405#if ENABLE_FEATURE_CLEAN_UP
406static void delete_block_backed_filesystems(void)
407{
Rob Landleya6b5b602006-05-08 19:03:07 +0000408 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000409}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000410#else
411void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000412#endif
413
Rob Landleydc0955b2006-03-14 18:16:25 +0000414// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000415// NB: mp->xxx fields may be trashed on exit
Denis Vlasenkob4133682008-02-18 13:05:38 +0000416static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000417{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000418 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000419
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200420 if (FAKE_IT) {
Denis Vlasenkob4133682008-02-18 13:05:38 +0000421 if (verbose >= 2)
422 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
423 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
424 vfsflags, filteropts);
425 goto mtab;
426 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000427
Rob Landleydc0955b2006-03-14 18:16:25 +0000428 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000429 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000430 errno = 0;
431 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000432 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000433
434 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000435 // helper program mount.<mnt_type>
Denys Vlasenkoba986032009-09-15 23:00:09 +0200436 if (HELPERS_ALLOWED && rc && mp->mnt_type) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200437 char *args[8];
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000438 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000439 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000440 rc = 1;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200441 if (FAKE_IT)
442 args[rc++] = (char *)"-f";
443 if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
444 args[rc++] = (char *)"-n";
Denis Vlasenko5c329932009-04-12 12:16:21 +0000445 args[rc++] = mp->mnt_fsname;
446 args[rc++] = mp->mnt_dir;
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000447 if (filteropts) {
448 args[rc++] = (char *)"-o";
449 args[rc++] = filteropts;
450 }
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000451 args[rc] = NULL;
452 rc = wait4pid(spawn(args));
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000453 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000454 if (!rc)
455 break;
456 errno = errno_save;
457 }
458
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000459 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000460 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000461 if (!(vfsflags & MS_SILENT))
462 bb_error_msg("%s is write-protected, mounting read-only",
463 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000464 vfsflags |= MS_RDONLY;
465 }
466
Rob Landleydc0955b2006-03-14 18:16:25 +0000467 // Abort entirely if permission denied.
468
469 if (rc && errno == EPERM)
470 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
471
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000472 // If the mount was successful, and we're maintaining an old-style
473 // mtab file by hand, add the new entry to it now.
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000474 mtab:
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200475 if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000476 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000477 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000478 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000479 int i;
480
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000481 if (!mountTable) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000482 bb_error_msg("no %s", bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000483 goto ret;
484 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000485
486 // Add vfs string flags
487
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000488 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
489 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
490 append_mount_options(&(mp->mnt_opts), option_str);
491 option_str += strlen(option_str) + 1;
492 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000493
494 // Remove trailing / (if any) from directory we mounted on
495
Denis Vlasenko727ef942006-09-14 13:19:19 +0000496 i = strlen(mp->mnt_dir) - 1;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000497 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000498
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000499 // Convert to canonical pathnames as needed
Denis Vlasenko727ef942006-09-14 13:19:19 +0000500
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000501 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000502 fsname = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000503 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000504 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000505 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000506 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000507 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000508
509 // Write and close.
510
Denis Vlasenko727ef942006-09-14 13:19:19 +0000511 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000512 endmntent(mountTable);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000513 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000514 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000515 free(fsname);
516 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000517 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000518 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000519 return rc;
520}
521
Denis Vlasenko25098f72006-09-14 15:46:33 +0000522#if ENABLE_FEATURE_MOUNT_NFS
523
524/*
525 * Linux NFS mount
526 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
527 *
528 * Licensed under GPLv2, see file LICENSE in this tarball for details.
529 *
530 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
531 * numbers to be specified on the command line.
532 *
533 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
534 * Omit the call to connect() for Linux version 1.3.11 or later.
535 *
536 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
537 * Implemented the "bg", "fg" and "retry" mount options for NFS.
538 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000539 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000540 * - added Native Language Support
541 *
542 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
543 * plus NFSv3 stuff.
544 */
545
Denis Vlasenko25098f72006-09-14 15:46:33 +0000546/* This is just a warning of a common mistake. Possibly this should be a
547 * uclibc faq entry rather than in busybox... */
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000548#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000549#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
550#endif
551
552#define MOUNTPORT 635
553#define MNTPATHLEN 1024
554#define MNTNAMLEN 255
555#define FHSIZE 32
556#define FHSIZE3 64
557
558typedef char fhandle[FHSIZE];
559
560typedef struct {
561 unsigned int fhandle3_len;
562 char *fhandle3_val;
563} fhandle3;
564
565enum mountstat3 {
566 MNT_OK = 0,
567 MNT3ERR_PERM = 1,
568 MNT3ERR_NOENT = 2,
569 MNT3ERR_IO = 5,
570 MNT3ERR_ACCES = 13,
571 MNT3ERR_NOTDIR = 20,
572 MNT3ERR_INVAL = 22,
573 MNT3ERR_NAMETOOLONG = 63,
574 MNT3ERR_NOTSUPP = 10004,
575 MNT3ERR_SERVERFAULT = 10006,
576};
577typedef enum mountstat3 mountstat3;
578
579struct fhstatus {
580 unsigned int fhs_status;
581 union {
582 fhandle fhs_fhandle;
583 } fhstatus_u;
584};
585typedef struct fhstatus fhstatus;
586
587struct mountres3_ok {
588 fhandle3 fhandle;
589 struct {
590 unsigned int auth_flavours_len;
591 char *auth_flavours_val;
592 } auth_flavours;
593};
594typedef struct mountres3_ok mountres3_ok;
595
596struct mountres3 {
597 mountstat3 fhs_status;
598 union {
599 mountres3_ok mountinfo;
600 } mountres3_u;
601};
602typedef struct mountres3 mountres3;
603
604typedef char *dirpath;
605
606typedef char *name;
607
608typedef struct mountbody *mountlist;
609
610struct mountbody {
611 name ml_hostname;
612 dirpath ml_directory;
613 mountlist ml_next;
614};
615typedef struct mountbody mountbody;
616
617typedef struct groupnode *groups;
618
619struct groupnode {
620 name gr_name;
621 groups gr_next;
622};
623typedef struct groupnode groupnode;
624
625typedef struct exportnode *exports;
626
627struct exportnode {
628 dirpath ex_dir;
629 groups ex_groups;
630 exports ex_next;
631};
632typedef struct exportnode exportnode;
633
634struct ppathcnf {
635 int pc_link_max;
636 short pc_max_canon;
637 short pc_max_input;
638 short pc_name_max;
639 short pc_path_max;
640 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000641 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000642 char pc_xxx;
643 short pc_mask[2];
644};
645typedef struct ppathcnf ppathcnf;
646
647#define MOUNTPROG 100005
648#define MOUNTVERS 1
649
650#define MOUNTPROC_NULL 0
651#define MOUNTPROC_MNT 1
652#define MOUNTPROC_DUMP 2
653#define MOUNTPROC_UMNT 3
654#define MOUNTPROC_UMNTALL 4
655#define MOUNTPROC_EXPORT 5
656#define MOUNTPROC_EXPORTALL 6
657
658#define MOUNTVERS_POSIX 2
659
660#define MOUNTPROC_PATHCONF 7
661
662#define MOUNT_V3 3
663
664#define MOUNTPROC3_NULL 0
665#define MOUNTPROC3_MNT 1
666#define MOUNTPROC3_DUMP 2
667#define MOUNTPROC3_UMNT 3
668#define MOUNTPROC3_UMNTALL 4
669#define MOUNTPROC3_EXPORT 5
670
671enum {
672#ifndef NFS_FHSIZE
673 NFS_FHSIZE = 32,
674#endif
675#ifndef NFS_PORT
676 NFS_PORT = 2049
677#endif
678};
679
Denis Vlasenko25098f72006-09-14 15:46:33 +0000680/*
681 * We want to be able to compile mount on old kernels in such a way
682 * that the binary will work well on more recent kernels.
683 * Thus, if necessary we teach nfsmount.c the structure of new fields
684 * that will come later.
685 *
686 * Moreover, the new kernel includes conflict with glibc includes
687 * so it is easiest to ignore the kernel altogether (at compile time).
688 */
689
690struct nfs2_fh {
691 char data[32];
692};
693struct nfs3_fh {
694 unsigned short size;
695 unsigned char data[64];
696};
697
698struct nfs_mount_data {
699 int version; /* 1 */
700 int fd; /* 1 */
701 struct nfs2_fh old_root; /* 1 */
702 int flags; /* 1 */
703 int rsize; /* 1 */
704 int wsize; /* 1 */
705 int timeo; /* 1 */
706 int retrans; /* 1 */
707 int acregmin; /* 1 */
708 int acregmax; /* 1 */
709 int acdirmin; /* 1 */
710 int acdirmax; /* 1 */
711 struct sockaddr_in addr; /* 1 */
712 char hostname[256]; /* 1 */
713 int namlen; /* 2 */
714 unsigned int bsize; /* 3 */
715 struct nfs3_fh root; /* 4 */
716};
717
718/* bits in the flags field */
719enum {
720 NFS_MOUNT_SOFT = 0x0001, /* 1 */
721 NFS_MOUNT_INTR = 0x0002, /* 1 */
722 NFS_MOUNT_SECURE = 0x0004, /* 1 */
723 NFS_MOUNT_POSIX = 0x0008, /* 1 */
724 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
725 NFS_MOUNT_NOAC = 0x0020, /* 1 */
726 NFS_MOUNT_TCP = 0x0040, /* 2 */
727 NFS_MOUNT_VER3 = 0x0080, /* 3 */
728 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000729 NFS_MOUNT_NONLM = 0x0200, /* 3 */
730 NFS_MOUNT_NORDIRPLUS = 0x4000
Denis Vlasenko25098f72006-09-14 15:46:33 +0000731};
732
733
734/*
735 * We need to translate between nfs status return values and
736 * the local errno values which may not be the same.
737 *
738 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
739 * "after #include <errno.h> the symbol errno is reserved for any use,
740 * it cannot even be used as a struct tag or field name".
741 */
742
743#ifndef EDQUOT
744#define EDQUOT ENOSPC
745#endif
746
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000747/* Convert each NFSERR_BLAH into EBLAH */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000748static const struct {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000749 short stat;
750 short errnum;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000751} nfs_errtbl[] = {
752 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
753 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
754 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
755 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
756};
Denis Vlasenko25098f72006-09-14 15:46:33 +0000757static char *nfs_strerror(int status)
758{
759 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000760
761 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
762 if (nfs_errtbl[i].stat == status)
763 return strerror(nfs_errtbl[i].errnum);
764 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000765 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000766}
767
768static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
769{
770 if (!xdr_opaque(xdrs, objp, FHSIZE))
771 return FALSE;
772 return TRUE;
773}
774
775static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
776{
777 if (!xdr_u_int(xdrs, &objp->fhs_status))
778 return FALSE;
779 switch (objp->fhs_status) {
780 case 0:
781 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
782 return FALSE;
783 break;
784 default:
785 break;
786 }
787 return TRUE;
788}
789
790static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
791{
792 if (!xdr_string(xdrs, objp, MNTPATHLEN))
793 return FALSE;
794 return TRUE;
795}
796
797static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
798{
799 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
800 return FALSE;
801 return TRUE;
802}
803
804static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
805{
806 if (!xdr_fhandle3(xdrs, &objp->fhandle))
807 return FALSE;
808 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
Denys Vlasenko810b7162009-05-13 23:48:59 +0200809 sizeof(int), (xdrproc_t) xdr_int))
Denis Vlasenko25098f72006-09-14 15:46:33 +0000810 return FALSE;
811 return TRUE;
812}
813
814static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
815{
816 if (!xdr_enum(xdrs, (enum_t *) objp))
817 return FALSE;
818 return TRUE;
819}
820
821static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
822{
823 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
824 return FALSE;
825 switch (objp->fhs_status) {
826 case MNT_OK:
827 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
828 return FALSE;
829 break;
830 default:
831 break;
832 }
833 return TRUE;
834}
835
836#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
837
Denis Vlasenko25098f72006-09-14 15:46:33 +0000838/*
839 * Unfortunately, the kernel prints annoying console messages
840 * in case of an unexpected nfs mount version (instead of
841 * just returning some error). Therefore we'll have to try
842 * and figure out what version the kernel expects.
843 *
844 * Variables:
845 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
846 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
847 * nfs_mount_version: version this source and running kernel can handle
848 */
849static void
850find_kernel_nfs_mount_version(void)
851{
Denis Vlasenkob9256052007-09-28 10:29:17 +0000852 int kernel_version;
853
854 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000855 return;
856
857 nfs_mount_version = 4; /* default */
858
859 kernel_version = get_linux_version_code();
860 if (kernel_version) {
861 if (kernel_version < KERNEL_VERSION(2,1,32))
862 nfs_mount_version = 1;
863 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
864 (kernel_version >= KERNEL_VERSION(2,3,0) &&
865 kernel_version < KERNEL_VERSION(2,3,99)))
866 nfs_mount_version = 3;
867 /* else v4 since 2.3.99pre4 */
868 }
869}
870
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +0000871static void
Denis Vlasenkob9256052007-09-28 10:29:17 +0000872get_mountport(struct pmap *pm_mnt,
873 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +0000874 long unsigned prog,
875 long unsigned version,
876 long unsigned proto,
877 long unsigned port)
878{
879 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000880
881 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +0000882/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
883 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000884 pmap = pmap_getmaps(server_addr);
885
886 if (version > MAX_NFSPROT)
887 version = MAX_NFSPROT;
888 if (!prog)
889 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000890 pm_mnt->pm_prog = prog;
891 pm_mnt->pm_vers = version;
892 pm_mnt->pm_prot = proto;
893 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000894
Denis Vlasenko25098f72006-09-14 15:46:33 +0000895 while (pmap) {
896 if (pmap->pml_map.pm_prog != prog)
897 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000898 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000899 goto next;
900 if (version > 2 && pmap->pml_map.pm_vers != version)
901 goto next;
902 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
903 goto next;
904 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
Denis Vlasenkob9256052007-09-28 10:29:17 +0000905 (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto) ||
Denis Vlasenko25098f72006-09-14 15:46:33 +0000906 (port && pmap->pml_map.pm_port != port))
907 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000908 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
909 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +0000910 pmap = pmap->pml_next;
911 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000912 if (!pm_mnt->pm_vers)
913 pm_mnt->pm_vers = MOUNTVERS;
914 if (!pm_mnt->pm_port)
915 pm_mnt->pm_port = MOUNTPORT;
916 if (!pm_mnt->pm_prot)
917 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000918}
919
Denis Vlasenkof0000652007-09-04 18:30:26 +0000920#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +0000921static int daemonize(void)
922{
Denis Vlasenko25098f72006-09-14 15:46:33 +0000923 int pid = fork();
924 if (pid < 0) /* error */
925 return -errno;
926 if (pid > 0) /* parent */
927 return 0;
928 /* child */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000929 close(0);
930 xopen(bb_dev_null, O_RDWR);
931 xdup2(0, 1);
932 xdup2(0, 2);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000933 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +0000934 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000935 logmode = LOGMODE_SYSLOG;
936 return 1;
937}
Denis Vlasenkof0000652007-09-04 18:30:26 +0000938#else
939static inline int daemonize(void) { return -ENOSYS; }
940#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000941
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000942/* TODO */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000943static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000944{
945 return 0;
946}
947
948/* RPC strerror analogs are terminally idiotic:
949 * *mandatory* prefix and \n at end.
950 * This hopefully helps. Usage:
951 * error_msg_rpc(clnt_*error*(" ")) */
952static void error_msg_rpc(const char *msg)
953{
Denis Vlasenko23514fe2006-09-19 14:07:52 +0000954 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000955 while (msg[0] == ' ' || msg[0] == ':') msg++;
956 len = strlen(msg);
957 while (len && msg[len-1] == '\n') len--;
958 bb_error_msg("%.*s", len, msg);
959}
960
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000961/* NB: mp->xxx fields may be trashed on exit */
Denys Vlasenko810b7162009-05-13 23:48:59 +0200962static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000963{
964 CLIENT *mclient;
965 char *hostname;
966 char *pathname;
967 char *mounthost;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +0200968 /* prior to 2.6.23, kernel took NFS options in a form of this struct
969 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
970 * then data pointer is interpreted as a string. */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000971 struct nfs_mount_data data;
972 char *opt;
973 struct hostent *hp;
974 struct sockaddr_in server_addr;
975 struct sockaddr_in mount_server_addr;
976 int msock, fsock;
977 union {
978 struct fhstatus nfsv2;
979 struct mountres3 nfsv3;
980 } status;
981 int daemonized;
982 char *s;
983 int port;
984 int mountport;
985 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +0000986#if BB_MMU
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000987 smallint bg = 0;
Denis Vlasenkof0000652007-09-04 18:30:26 +0000988#else
989 enum { bg = 0 };
990#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000991 int retry;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000992 int mountprog;
993 int mountvers;
994 int nfsprog;
995 int nfsvers;
996 int retval;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +0200997 /* these all are one-bit really. gcc 4.3.1 likes this combination: */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000998 smallint tcp;
999 smallint soft;
1000 int intr;
1001 int posix;
1002 int nocto;
1003 int noac;
1004 int nordirplus;
1005 int nolock;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001006
1007 find_kernel_nfs_mount_version();
1008
1009 daemonized = 0;
1010 mounthost = NULL;
1011 retval = ETIMEDOUT;
1012 msock = fsock = -1;
1013 mclient = NULL;
1014
1015 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1016
1017 filteropts = xstrdup(filteropts); /* going to trash it later... */
1018
1019 hostname = xstrdup(mp->mnt_fsname);
1020 /* mount_main() guarantees that ':' is there */
1021 s = strchr(hostname, ':');
1022 pathname = s + 1;
1023 *s = '\0';
1024 /* Ignore all but first hostname in replicated mounts
1025 until they can be fully supported. (mack@sgi.com) */
1026 s = strchr(hostname, ',');
1027 if (s) {
1028 *s = '\0';
1029 bb_error_msg("warning: multiple hostnames not supported");
1030 }
1031
1032 server_addr.sin_family = AF_INET;
1033 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1034 hp = gethostbyname(hostname);
1035 if (hp == NULL) {
1036 bb_herror_msg("%s", hostname);
1037 goto fail;
1038 }
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001039 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001040 bb_error_msg("got bad hp->h_length");
1041 hp->h_length = sizeof(struct in_addr);
1042 }
1043 memcpy(&server_addr.sin_addr,
1044 hp->h_addr, hp->h_length);
1045 }
1046
1047 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1048
1049 /* add IP address to mtab options for use when unmounting */
1050
1051 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1052 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1053 } else {
1054 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1055 mp->mnt_opts[0] ? "," : "",
1056 inet_ntoa(server_addr.sin_addr));
1057 free(mp->mnt_opts);
1058 mp->mnt_opts = tmp;
1059 }
1060
1061 /* Set default options.
1062 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1063 * let the kernel decide.
1064 * timeo is filled in after we know whether it'll be TCP or UDP. */
1065 memset(&data, 0, sizeof(data));
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001066 data.retrans = 3;
1067 data.acregmin = 3;
1068 data.acregmax = 60;
1069 data.acdirmin = 30;
1070 data.acdirmax = 60;
1071 data.namlen = NAME_MAX;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001072
Denis Vlasenko25098f72006-09-14 15:46:33 +00001073 soft = 0;
1074 intr = 0;
1075 posix = 0;
1076 nocto = 0;
1077 nolock = 0;
1078 noac = 0;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001079 nordirplus = 0;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001080 retry = 10000; /* 10000 minutes ~ 1 week */
1081 tcp = 0;
1082
1083 mountprog = MOUNTPROG;
1084 mountvers = 0;
1085 port = 0;
1086 mountport = 0;
1087 nfsprog = 100003;
1088 nfsvers = 0;
1089
1090 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001091 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001092 char *opteq = strchr(opt, '=');
1093 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001094 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001095 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001096 /* 0 */ "rsize\0"
1097 /* 1 */ "wsize\0"
1098 /* 2 */ "timeo\0"
1099 /* 3 */ "retrans\0"
1100 /* 4 */ "acregmin\0"
1101 /* 5 */ "acregmax\0"
1102 /* 6 */ "acdirmin\0"
1103 /* 7 */ "acdirmax\0"
1104 /* 8 */ "actimeo\0"
1105 /* 9 */ "retry\0"
1106 /* 10 */ "port\0"
1107 /* 11 */ "mountport\0"
1108 /* 12 */ "mounthost\0"
1109 /* 13 */ "mountprog\0"
1110 /* 14 */ "mountvers\0"
1111 /* 15 */ "nfsprog\0"
1112 /* 16 */ "nfsvers\0"
1113 /* 17 */ "vers\0"
1114 /* 18 */ "proto\0"
1115 /* 19 */ "namlen\0"
1116 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001117
1118 *opteq++ = '\0';
1119 idx = index_in_strings(options, opt);
1120 switch (idx) {
1121 case 12: // "mounthost"
1122 mounthost = xstrndup(opteq,
1123 strcspn(opteq, " \t\n\r,"));
1124 continue;
1125 case 18: // "proto"
1126 if (!strncmp(opteq, "tcp", 3))
1127 tcp = 1;
1128 else if (!strncmp(opteq, "udp", 3))
1129 tcp = 0;
1130 else
1131 bb_error_msg("warning: unrecognized proto= option");
1132 continue;
1133 case 20: // "addr" - ignore
1134 continue;
1135 }
1136
1137 val = xatoi_u(opteq);
1138 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001139 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001140 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001141 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001142 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001143 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001144 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001145 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001146 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001147 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001148 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001149 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001150 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001151 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001152 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001153 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001154 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001155 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001156 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001157 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001158 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001159 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001160 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001161 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001162 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001163 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001164 data.acregmin = val;
1165 data.acregmax = val;
1166 data.acdirmin = val;
1167 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001168 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001169 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001170 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001171 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001172 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001173 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001174 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001175 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001176 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001177 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001178 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001179 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001180 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001181 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001182 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001183 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001184 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001185 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001186 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001187 case 16: // "nfsvers"
1188 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001189 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001190 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001191 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001192 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001193 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001194 //else
1195 // bb_error_msg("warning: option namlen is not supported\n");
1196 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001197 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001198 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1199 goto fail;
1200 }
1201 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001202 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001203 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001204 "bg\0"
1205 "fg\0"
1206 "soft\0"
1207 "hard\0"
1208 "intr\0"
1209 "posix\0"
1210 "cto\0"
1211 "ac\0"
1212 "tcp\0"
1213 "udp\0"
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001214 "lock\0"
1215 "rdirplus\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001216 int val = 1;
1217 if (!strncmp(opt, "no", 2)) {
1218 val = 0;
1219 opt += 2;
1220 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001221 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001222 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001223#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001224 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001225#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001226 break;
1227 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001228#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001229 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001230#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001231 break;
1232 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001233 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001234 break;
1235 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001236 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001237 break;
1238 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001239 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001240 break;
1241 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001242 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001243 break;
1244 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001245 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001246 break;
1247 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001248 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001249 break;
1250 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001251 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001252 break;
1253 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001254 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001255 break;
1256 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001257 if (nfs_mount_version >= 3)
1258 nolock = !val;
1259 else
1260 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001261 break;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001262 case 11: //rdirplus
1263 nordirplus = !val;
1264 break;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001265 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001266 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1267 goto fail;
1268 }
1269 }
1270 }
1271 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1272
1273 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1274 | (intr ? NFS_MOUNT_INTR : 0)
1275 | (posix ? NFS_MOUNT_POSIX : 0)
1276 | (nocto ? NFS_MOUNT_NOCTO : 0)
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001277 | (noac ? NFS_MOUNT_NOAC : 0)
1278 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001279 if (nfs_mount_version >= 2)
1280 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1281 if (nfs_mount_version >= 3)
1282 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1283 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1284 bb_error_msg("NFSv%d not supported", nfsvers);
1285 goto fail;
1286 }
1287 if (nfsvers && !mountvers)
1288 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1289 if (nfsvers && nfsvers < mountvers) {
1290 mountvers = nfsvers;
1291 }
1292
1293 /* Adjust options if none specified */
1294 if (!data.timeo)
1295 data.timeo = tcp ? 70 : 7;
1296
Denis Vlasenko25098f72006-09-14 15:46:33 +00001297 data.version = nfs_mount_version;
1298
1299 if (vfsflags & MS_REMOUNT)
1300 goto do_mount;
1301
1302 /*
1303 * If the previous mount operation on the same host was
1304 * backgrounded, and the "bg" for this mount is also set,
1305 * give up immediately, to avoid the initial timeout.
1306 */
1307 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001308 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001309 if (daemonized <= 0) { /* parent or error */
1310 retval = -daemonized;
1311 goto ret;
1312 }
1313 }
1314
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001315 /* Create mount daemon client */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001316 /* See if the nfs host = mount host. */
1317 if (mounthost) {
1318 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1319 mount_server_addr.sin_family = AF_INET;
1320 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1321 } else {
1322 hp = gethostbyname(mounthost);
1323 if (hp == NULL) {
1324 bb_herror_msg("%s", mounthost);
1325 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001326 }
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001327 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
1328 bb_error_msg("got bad hp->h_length");
1329 hp->h_length = sizeof(struct in_addr);
1330 }
1331 mount_server_addr.sin_family = AF_INET;
1332 memcpy(&mount_server_addr.sin_addr,
1333 hp->h_addr, hp->h_length);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001334 }
1335 }
1336
1337 /*
1338 * The following loop implements the mount retries. When the mount
1339 * times out, and the "bg" option is set, we background ourself
1340 * and continue trying.
1341 *
1342 * The case where the mount point is not present and the "bg"
1343 * option is set, is treated as a timeout. This is done to
1344 * support nested mounts.
1345 *
1346 * The "retry" count specified by the user is the number of
1347 * minutes to retry before giving up.
1348 */
1349 {
1350 struct timeval total_timeout;
1351 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001352 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001353 time_t t;
1354 time_t prevt;
1355 time_t timeout;
1356
1357 retry_timeout.tv_sec = 3;
1358 retry_timeout.tv_usec = 0;
1359 total_timeout.tv_sec = 20;
1360 total_timeout.tv_usec = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001361/* FIXME: use monotonic()? */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001362 timeout = time(NULL) + 60 * retry;
1363 prevt = 0;
1364 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001365 retry:
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001366 /* Be careful not to use too many CPU cycles */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001367 if (t - prevt < 30)
1368 sleep(30);
1369
Denis Vlasenkob9256052007-09-28 10:29:17 +00001370 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001371 mountprog,
1372 mountvers,
1373 proto,
1374 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001375 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001376
1377 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001378 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001379 msock = RPC_ANYSOCK;
1380
Denis Vlasenkob9256052007-09-28 10:29:17 +00001381 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001382 case IPPROTO_UDP:
1383 mclient = clntudp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001384 pm_mnt.pm_prog,
1385 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001386 retry_timeout,
1387 &msock);
1388 if (mclient)
1389 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001390 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001391 msock = RPC_ANYSOCK;
1392 case IPPROTO_TCP:
1393 mclient = clnttcp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001394 pm_mnt.pm_prog,
1395 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001396 &msock, 0, 0);
1397 break;
1398 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001399 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001400 }
1401 if (!mclient) {
1402 if (!daemonized && prevt == 0)
1403 error_msg_rpc(clnt_spcreateerror(" "));
1404 } else {
1405 enum clnt_stat clnt_stat;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001406
1407 /* Try to mount hostname:pathname */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001408 mclient->cl_auth = authunix_create_default();
1409
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001410 /* Make pointers in xdr_mountres3 NULL so
Denis Vlasenko25098f72006-09-14 15:46:33 +00001411 * that xdr_array allocates memory for us
1412 */
1413 memset(&status, 0, sizeof(status));
1414
Denis Vlasenkob9256052007-09-28 10:29:17 +00001415 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001416 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1417 (xdrproc_t) xdr_dirpath,
1418 (caddr_t) &pathname,
1419 (xdrproc_t) xdr_mountres3,
1420 (caddr_t) &status,
1421 total_timeout);
1422 else
1423 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1424 (xdrproc_t) xdr_dirpath,
1425 (caddr_t) &pathname,
1426 (xdrproc_t) xdr_fhstatus,
1427 (caddr_t) &status,
1428 total_timeout);
1429
1430 if (clnt_stat == RPC_SUCCESS)
1431 goto prepare_kernel_data; /* we're done */
1432 if (errno != ECONNREFUSED) {
1433 error_msg_rpc(clnt_sperror(mclient, " "));
1434 goto fail; /* don't retry */
1435 }
1436 /* Connection refused */
1437 if (!daemonized && prevt == 0) /* print just once */
1438 error_msg_rpc(clnt_sperror(mclient, " "));
1439 auth_destroy(mclient->cl_auth);
1440 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001441 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001442 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001443 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001444 }
1445
1446 /* Timeout. We are going to retry... maybe */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001447 if (!bg)
1448 goto fail;
1449 if (!daemonized) {
1450 daemonized = daemonize();
1451 if (daemonized <= 0) { /* parent or error */
1452 retval = -daemonized;
1453 goto ret;
1454 }
1455 }
1456 prevt = t;
1457 t = time(NULL);
1458 if (t >= timeout)
1459 /* TODO error message */
1460 goto fail;
1461
1462 goto retry;
1463 }
1464
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001465 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001466
1467 if (nfsvers == 2) {
1468 if (status.nfsv2.fhs_status != 0) {
1469 bb_error_msg("%s:%s failed, reason given by server: %s",
1470 hostname, pathname,
1471 nfs_strerror(status.nfsv2.fhs_status));
1472 goto fail;
1473 }
1474 memcpy(data.root.data,
1475 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1476 NFS_FHSIZE);
1477 data.root.size = NFS_FHSIZE;
1478 memcpy(data.old_root.data,
1479 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1480 NFS_FHSIZE);
1481 } else {
1482 fhandle3 *my_fhandle;
1483 if (status.nfsv3.fhs_status != 0) {
1484 bb_error_msg("%s:%s failed, reason given by server: %s",
1485 hostname, pathname,
1486 nfs_strerror(status.nfsv3.fhs_status));
1487 goto fail;
1488 }
1489 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1490 memset(data.old_root.data, 0, NFS_FHSIZE);
1491 memset(&data.root, 0, sizeof(data.root));
1492 data.root.size = my_fhandle->fhandle3_len;
1493 memcpy(data.root.data,
1494 (char *) my_fhandle->fhandle3_val,
1495 my_fhandle->fhandle3_len);
1496
1497 data.flags |= NFS_MOUNT_VER3;
1498 }
1499
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001500 /* Create nfs socket for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001501 if (tcp) {
1502 if (nfs_mount_version < 3) {
1503 bb_error_msg("NFS over TCP is not supported");
1504 goto fail;
1505 }
1506 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1507 } else
1508 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1509 if (fsock < 0) {
1510 bb_perror_msg("nfs socket");
1511 goto fail;
1512 }
1513 if (bindresvport(fsock, 0) < 0) {
1514 bb_perror_msg("nfs bindresvport");
1515 goto fail;
1516 }
1517 if (port == 0) {
1518 server_addr.sin_port = PMAPPORT;
1519 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1520 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1521 if (port == 0)
1522 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001523 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001524 server_addr.sin_port = htons(port);
1525
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001526 /* Prepare data structure for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001527 data.fd = fsock;
1528 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1529 strncpy(data.hostname, hostname, sizeof(data.hostname));
1530
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001531 /* Clean up */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001532 auth_destroy(mclient->cl_auth);
1533 clnt_destroy(mclient);
1534 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001535 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001536
1537 if (bg) {
1538 /* We must wait until mount directory is available */
1539 struct stat statbuf;
1540 int delay = 1;
1541 while (stat(mp->mnt_dir, &statbuf) == -1) {
1542 if (!daemonized) {
1543 daemonized = daemonize();
1544 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001545/* FIXME: parent doesn't close fsock - ??! */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001546 retval = -daemonized;
1547 goto ret;
1548 }
1549 }
1550 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1551 delay *= 2;
1552 if (delay > 30)
1553 delay = 30;
1554 }
1555 }
1556
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001557 /* Perform actual mount */
1558 do_mount:
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001559 mp->mnt_type = (char*)"nfs";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001560 retval = mount_it_now(mp, vfsflags, (char*)&data);
1561 goto ret;
1562
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001563 /* Abort */
1564 fail:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001565 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001566 if (mclient) {
1567 auth_destroy(mclient->cl_auth);
1568 clnt_destroy(mclient);
1569 }
1570 close(msock);
1571 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001572 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001573 close(fsock);
1574
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001575 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001576 free(hostname);
1577 free(mounthost);
1578 free(filteropts);
1579 return retval;
1580}
1581
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001582#else // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001583
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001584// Never called. Call should be optimized out.
Denis Vlasenkob4133682008-02-18 13:05:38 +00001585int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001586
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001587#endif // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001588
1589// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1590// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001591// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001592static int singlemount(struct mntent *mp, int ignore_busy)
1593{
Denis Vlasenkob4133682008-02-18 13:05:38 +00001594 int rc = -1;
1595 long vfsflags;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001596 char *loopFile = NULL, *filteropts = NULL;
1597 llist_t *fl = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001598 struct stat st;
1599
1600 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1601
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001602 // Treat fstype "auto" as unspecified
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001603 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1604 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001605
Denis Vlasenko2535f122007-09-15 13:28:30 +00001606 // Might this be a virtual filesystem?
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001607 if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1608 char *args[35];
1609 char *s;
1610 int n;
1611 // fsname: "cmd#arg1#arg2..."
1612 // WARNING: allows execution of arbitrary commands!
1613 // Try "mount 'sh#-c#sh' bogus_dir".
1614 // It is safe ONLY because non-root
1615 // cannot use two-argument mount command
1616 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1617 // "mount: can't find sh#-c#sh in /etc/fstab"
1618 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1619
1620 s = mp->mnt_fsname;
1621 n = 0;
1622 args[n++] = s;
1623 while (*s && n < 35 - 2) {
1624 if (*s++ == '#' && *s != '#') {
1625 s[-1] = '\0';
1626 args[n++] = s;
Denis Vlasenko2535f122007-09-15 13:28:30 +00001627 }
1628 }
Denis Vlasenko2535f122007-09-15 13:28:30 +00001629 args[n++] = mp->mnt_dir;
1630 args[n] = NULL;
1631 rc = wait4pid(xspawn(args));
1632 goto report_error;
1633 }
1634
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001635 // Might this be an CIFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001636 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001637 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1638 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1639 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001640 ) {
Denis Vlasenko5c329932009-04-12 12:16:21 +00001641#if 0 /* reported to break things */
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001642 len_and_sockaddr *lsa;
1643 char *ip, *dotted;
1644 char *s;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001645
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001646 // Replace '/' with '\' and verify that unc points to "//server/share".
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001647 for (s = mp->mnt_fsname; *s; ++s)
1648 if (*s == '/') *s = '\\';
1649
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001650 // Get server IP
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001651 s = strrchr(mp->mnt_fsname, '\\');
Denis Vlasenko5c329932009-04-12 12:16:21 +00001652 if (s <= mp->mnt_fsname+1)
1653 goto report_error;
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001654 *s = '\0';
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001655 lsa = host2sockaddr(mp->mnt_fsname+2, 0);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001656 *s = '\\';
Denis Vlasenko5c329932009-04-12 12:16:21 +00001657 if (!lsa)
1658 goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001659
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001660 // Insert ip=... option into string flags.
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001661 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001662 ip = xasprintf("ip=%s", dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001663 parse_mount_options(ip, &filteropts);
1664
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001665 // Compose new unc '\\server-ip\share'
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001666 // (s => slash after hostname)
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001667 mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
Denis Vlasenko5c329932009-04-12 12:16:21 +00001668#endif
1669 // Lock is required [why?]
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001670 vfsflags |= MS_MANDLOCK;
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001671 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001672 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko5c329932009-04-12 12:16:21 +00001673#if 0
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001674 if (ENABLE_FEATURE_CLEAN_UP) {
1675 free(mp->mnt_fsname);
1676 free(ip);
1677 free(dotted);
1678 free(lsa);
1679 }
Denis Vlasenko5c329932009-04-12 12:16:21 +00001680#endif
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001681 goto report_error;
1682 }
1683
1684 // Might this be an NFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001685 if (ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001686 && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001687 && strchr(mp->mnt_fsname, ':') != NULL
1688 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001689 rc = nfsmount(mp, vfsflags, filteropts);
1690 goto report_error;
1691 }
1692
1693 // Look at the file. (Not found isn't a failure for remount, or for
1694 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001695 // (We use stat, not lstat, in order to allow
1696 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001697 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001698 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1699 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001700 // Do we need to allocate a loopback device for it?
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001701 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1702 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001703 mp->mnt_fsname = NULL; // will receive malloced loop dev name
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001704 if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) {
1705 if (errno == EPERM || errno == EACCES)
1706 bb_error_msg(bb_msg_perm_denied_are_you_root);
1707 else
1708 bb_perror_msg("cannot setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001709 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001710 }
1711
1712 // Autodetect bind mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001713 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1714 vfsflags |= MS_BIND;
1715 }
1716
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001717 // If we know the fstype (or don't need to), jump straight
1718 // to the actual mount.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001719 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1720 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001721 else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001722 // Loop through filesystem types until mount succeeds
1723 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001724
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001725 // Initialize list of block backed filesystems. This has to be
1726 // done here so that during "mount -a", mounts after /proc shows up
1727 // can autodetect.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001728 if (!fslist) {
1729 fslist = get_block_backed_filesystems();
1730 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1731 atexit(delete_block_backed_filesystems);
1732 }
1733
1734 for (fl = fslist; fl; fl = fl->link) {
1735 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001736 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001737 if (!rc)
1738 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001739 }
1740 }
1741
1742 // If mount failed, clean up loop file (if any).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001743 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1744 del_loop(mp->mnt_fsname);
1745 if (ENABLE_FEATURE_CLEAN_UP) {
1746 free(loopFile);
1747 free(mp->mnt_fsname);
1748 }
1749 }
1750
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001751 report_error:
1752 if (ENABLE_FEATURE_CLEAN_UP)
1753 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001754
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001755 if (errno == EBUSY && ignore_busy)
1756 return 0;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001757 if (rc < 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001758 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001759 return rc;
1760}
1761
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001762/* -O support
1763 * Unlike -t, -O should interpret "no" prefix differently:
1764 * -t noa,b,c = -t no(a,b,c) = mount all except fs'es with types a,b, and c
1765 * -O noa,b,c = -O noa,b,c = mount all with without option a,
1766 * or with option b or c.
1767 * But for now we do not support -O a,b,c at all (only -O a).
1768 *
1769 * Another difference from -t support (match_fstype) is that
1770 * we need to examine the _list_ of options in fsopt, not just a string.
1771 */
1772static int match_opt(const char *fs_opt, const char *O_opt)
1773{
1774 int match = 1;
1775 int len;
1776
1777 if (!O_opt)
1778 return match;
1779
1780 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
1781 match--;
1782 O_opt += 2;
1783 }
1784
1785 len = strlen(O_opt);
1786 while (1) {
1787 if (strncmp(fs_opt, O_opt, len) == 0
1788 && (fs_opt[len] == '\0' || fs_opt[len] == ',')
1789 ) {
1790 return match;
1791 }
1792 fs_opt = strchr(fs_opt, ',');
1793 if (!fs_opt)
1794 break;
1795 fs_opt++;
1796 }
1797
1798 return !match;
1799}
1800
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001801// Parse options, if necessary parse fstab/mtab, and call singlemount for
1802// each directory to be mounted.
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001803static const char must_be_root[] ALIGN1 = "you must be root";
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001804
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001805int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001806int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001807{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001808 char *cmdopts = xzalloc(1);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001809 char *fstype = NULL;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001810 char *O_optmatch = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001811 char *storage_path;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001812 llist_t *lst_o = NULL;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001813 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001814 FILE *fstab;
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001815 int i, j, rc = 0;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001816 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001817 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001818 IF_NOT_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001819
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001820 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00001821
Denis Vlasenkof732e962008-02-18 12:07:49 +00001822 // Parse long options, like --bind and --move. Note that -o option
1823 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001824 for (i = j = 1; argv[i]; i++) {
1825 if (argv[i][0] == '-' && argv[i][1] == '-')
1826 append_mount_options(&cmdopts, argv[i] + 2);
1827 else
1828 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001829 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001830 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001831
1832 // Parse remaining options
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001833 // Max 2 params; -o is a list, -v is a counter
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001834 opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001835 opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001836 IF_FEATURE_MOUNT_VERBOSE(, &verbose));
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001837 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00001838 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
1839 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001840 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001841
1842 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001843 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00001844 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001845 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1846
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001847 if (!mountTable)
1848 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001849
Denis Vlasenko2535f122007-09-15 13:28:30 +00001850 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001851 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001852 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001853 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00001854 // util-linux 2.12a happily shows rootfs...
1855 //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001856
1857 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1858 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1859 mtpair->mnt_dir, mtpair->mnt_type,
1860 mtpair->mnt_opts);
1861 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001862 if (ENABLE_FEATURE_CLEAN_UP)
1863 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001864 return EXIT_SUCCESS;
1865 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001866 storage_path = NULL;
1867 } else {
1868 // When we have two arguments, the second is the directory and we can
1869 // skip looking at fstab entirely. We can always abspath() the directory
1870 // argument when we get it.
1871 if (argv[1]) {
1872 if (nonroot)
1873 bb_error_msg_and_die(must_be_root);
1874 mtpair->mnt_fsname = argv[0];
1875 mtpair->mnt_dir = argv[1];
1876 mtpair->mnt_type = fstype;
1877 mtpair->mnt_opts = cmdopts;
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00001878 resolve_mount_spec(&mtpair->mnt_fsname);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001879 rc = singlemount(mtpair, 0);
1880 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00001881 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001882 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001883 }
1884
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001885 // Past this point, we are handling either "mount -a [opts]"
1886 // or "mount [opts] single_param"
1887
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001888 i = parse_mount_options(cmdopts, NULL); // FIXME: should be "long", not "int"
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001889 if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
1890 bb_error_msg_and_die(must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00001891
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001892 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001893 if (ENABLE_FEATURE_MOUNT_FLAGS
1894 && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
1895 ) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001896 // verbose_mount(source, target, type, flags, data)
1897 rc = verbose_mount("", argv[0], "", i, "");
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001898 if (rc)
1899 bb_simple_perror_msg_and_die(argv[0]);
1900 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001901 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001902
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001903 // Open either fstab or mtab
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001904 fstabname = "/etc/fstab";
1905 if (i & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001906 // WARNING. I am not sure this matches util-linux's
1907 // behavior. It's possible util-linux does not
1908 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001909 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001910 }
1911 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001912 if (!fstab)
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001913 bb_perror_msg_and_die("cannot read %s", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001914
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001915 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00001916 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001917 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001918 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001919
1920 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00001921 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001922 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001923 GETMNTENT_BUFSIZE/2)
1924 ) { // End of fstab/mtab is reached
1925 mtcur = mtother; // the thing we found last time
1926 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001927 }
1928
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001929 // If we're trying to mount something specific and this isn't it,
1930 // skip it. Note we must match the exact text in fstab (ala
1931 // "proc") or a full path from root
1932 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001933
1934 // Is this what we're looking for?
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001935 if (strcmp(argv[0], mtcur->mnt_fsname) &&
1936 strcmp(storage_path, mtcur->mnt_fsname) &&
1937 strcmp(argv[0], mtcur->mnt_dir) &&
1938 strcmp(storage_path, mtcur->mnt_dir)) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001939
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001940 // Remember this entry. Something later may have
1941 // overmounted it, and we want the _last_ match.
1942 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001943
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001944 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001945 } else {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001946 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001947 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001948 if (nonroot)
1949 bb_error_msg_and_die(must_be_root);
1950
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001951 // Does type match? (NULL matches always)
1952 if (!match_fstype(mtcur, fstype))
1953 continue;
1954
1955 // Skip noauto and swap anyway.
1956 if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
1957 // swap is bogus "fstype", parse_mount_options can't check fstypes
1958 || strcasecmp(mtcur->mnt_type, "swap") == 0
1959 ) {
1960 continue;
1961 }
1962
1963 // Does (at least one) option match?
1964 // (NULL matches always)
1965 if (!match_opt(mtcur->mnt_opts, O_optmatch))
1966 continue;
1967
1968 resolve_mount_spec(&mtcur->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001969
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001970 // NFS mounts want this to be xrealloc-able
1971 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00001972
1973 // Mount this thing
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001974 if (singlemount(mtcur, 1)) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001975 // Count number of failed mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001976 rc++;
1977 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001978 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001979 }
1980 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001981
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001982 // End of fstab/mtab is reached.
1983 // Were we looking for something specific?
1984 if (argv[0]) {
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001985 long l;
1986
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001987 // If we didn't find anything, complain
1988 if (!mtcur->mnt_fsname)
1989 bb_error_msg_and_die("can't find %s in %s",
1990 argv[0], fstabname);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001991
1992 // What happens when we try to "mount swap_partition"?
1993 // (fstab containts "swap_partition swap swap defaults 0 0")
1994 // util-linux-ng 2.13.1 does this:
1995 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
1996 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
1997 // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory)
1998 // write(2, "mount: mount point swap does not exist\n", 39) = 39
1999 // exit_group(32) = ?
2000#if 0
2001 // In case we want to simply skip swap partitions:
2002 l = parse_mount_options(mtcur->mnt_opts, NULL);
2003 if ((l & MOUNT_SWAP)
2004 // swap is bogus "fstype", parse_mount_options can't check fstypes
2005 || strcasecmp(mtcur->mnt_type, "swap") == 0
2006 ) {
2007 goto ret;
2008 }
2009#endif
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002010 if (nonroot) {
2011 // fstab must have "users" or "user"
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002012 l = parse_mount_options(mtcur->mnt_opts, NULL);
2013 if (!(l & MOUNT_USERS))
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002014 bb_error_msg_and_die(must_be_root);
2015 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002016
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002017 // Mount the last thing we found
2018 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2019 append_mount_options(&(mtcur->mnt_opts), cmdopts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002020 resolve_mount_spec(&mtpair->mnt_fsname);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002021 rc = singlemount(mtcur, 0);
2022 if (ENABLE_FEATURE_CLEAN_UP)
2023 free(mtcur->mnt_opts);
2024 }
2025
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002026 //ret:
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002027 if (ENABLE_FEATURE_CLEAN_UP)
2028 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002029 if (ENABLE_FEATURE_CLEAN_UP) {
2030 free(storage_path);
2031 free(cmdopts);
2032 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002033 return rc;
2034}