blob: 478dc2409b0e1c5f2cf8e6b8c48dfc8a885e0c99 [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
Denis Vlasenkode7684a2008-02-18 21:08:49 +000053#include "volume_id.h"
Denis Vlasenko6aa76962008-03-18 01:44:52 +000054#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +000055
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000056// Needed for nfs support only
Denis Vlasenko30a64cd2006-09-15 15:12:00 +000057#include <sys/utsname.h>
58#undef TRUE
59#undef FALSE
60#include <rpc/rpc.h>
61#include <rpc/pmap_prot.h>
62#include <rpc/pmap_clnt.h>
63
Denis Vlasenko2535f122007-09-15 13:28:30 +000064#ifndef MS_SILENT
65#define MS_SILENT (1 << 15)
66#endif
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000067// Grab more as needed from util-linux's mount/mount_constants.h
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +000068#ifndef MS_DIRSYNC
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000069#define MS_DIRSYNC 128 // Directory modifications are synchronous
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +000070#endif
71
Denis Vlasenko30a64cd2006-09-15 15:12:00 +000072
Denis Vlasenko908d6b72006-12-18 23:07:42 +000073#if defined(__dietlibc__)
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000074// 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
75// dietlibc-0.30 does not have implementation of getmntent_r()
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +000076static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000077 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
Denis Vlasenko908d6b72006-12-18 23:07:42 +000078{
Denis Vlasenko908d6b72006-12-18 23:07:42 +000079 struct mntent* ment = getmntent(stream);
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +000080 return memcpy(result, ment, sizeof(*ment));
Denis Vlasenko908d6b72006-12-18 23:07:42 +000081}
82#endif
83
84
Rob Landleydc0955b2006-03-14 18:16:25 +000085// Not real flags, but we want to be able to check for this.
Denis Vlasenko13c5a682006-10-16 22:39:51 +000086enum {
Denis Vlasenkoa4522c52008-03-17 08:46:43 +000087 MOUNT_USERS = (1 << 28) * ENABLE_DESKTOP,
88 MOUNT_NOAUTO = (1 << 29),
89 MOUNT_SWAP = (1 << 30),
Denis Vlasenko13c5a682006-10-16 22:39:51 +000090};
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +000091
92
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +000093#define OPTION_STR "o:t:rwanfvsiO:"
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +000094enum {
95 OPT_o = (1 << 0),
96 OPT_t = (1 << 1),
97 OPT_r = (1 << 2),
98 OPT_w = (1 << 3),
99 OPT_a = (1 << 4),
100 OPT_n = (1 << 5),
101 OPT_f = (1 << 6),
102 OPT_v = (1 << 7),
103 OPT_s = (1 << 8),
104 OPT_i = (1 << 9),
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +0000105 OPT_O = (1 << 10),
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000106};
107
108#if ENABLE_FEATURE_MTAB_SUPPORT
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200109#define USE_MTAB (!(option_mask32 & OPT_n))
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000110#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200111#define USE_MTAB 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000112#endif
113
114#if ENABLE_FEATURE_MOUNT_FAKE
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200115#define FAKE_IT (option_mask32 & OPT_f)
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000116#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200117#define FAKE_IT 0
118#endif
119
120#if ENABLE_FEATURE_MOUNT_HELPERS
121#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
122#else
123#define HELPERS_ALLOWED 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000124#endif
125
126
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000127// TODO: more "user" flag compatibility.
128// "user" option (from mount manpage):
129// Only the user that mounted a filesystem can unmount it again.
130// If any user should be able to unmount, then use users instead of user
131// in the fstab line. The owner option is similar to the user option,
132// with the restriction that the user must be the owner of the special file.
133// This may be useful e.g. for /dev/fd if a login script makes
134// the console user owner of this device.
Rob Landley3ba7bd12006-08-09 19:51:13 +0000135
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000136// Standard mount options (from -o options or --options),
137// with corresponding flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000138static const int32_t mount_options[] = {
Rob Landleye3781b72006-08-08 01:39:49 +0000139 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
Rob Landleydc0955b2006-03-14 18:16:25 +0000140
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000141 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000142 /* "loop" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000143 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000144
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000145 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000146 /* "defaults" */ 0,
147 /* "quiet" 0 - do not filter out, vfat wants to see it */
148 /* "noauto" */ MOUNT_NOAUTO,
149 /* "sw" */ MOUNT_SWAP,
150 /* "swap" */ MOUNT_SWAP,
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000151 IF_DESKTOP(/* "user" */ MOUNT_USERS,)
152 IF_DESKTOP(/* "users" */ MOUNT_USERS,)
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000153 /* "_netdev" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000154 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000155
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000156 IF_FEATURE_MOUNT_FLAGS(
Rob Landleye3781b72006-08-08 01:39:49 +0000157 // vfs flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000158 /* "nosuid" */ MS_NOSUID,
159 /* "suid" */ ~MS_NOSUID,
160 /* "dev" */ ~MS_NODEV,
161 /* "nodev" */ MS_NODEV,
162 /* "exec" */ ~MS_NOEXEC,
163 /* "noexec" */ MS_NOEXEC,
164 /* "sync" */ MS_SYNCHRONOUS,
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +0000165 /* "dirsync" */ MS_DIRSYNC,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000166 /* "async" */ ~MS_SYNCHRONOUS,
167 /* "atime" */ ~MS_NOATIME,
168 /* "noatime" */ MS_NOATIME,
169 /* "diratime" */ ~MS_NODIRATIME,
170 /* "nodiratime" */ MS_NODIRATIME,
Denis Vlasenko580ce2d2008-07-08 02:56:53 +0000171 /* "mand" */ MS_MANDLOCK,
172 /* "nomand" */ ~MS_MANDLOCK,
Bernhard Reutner-Fischerfb5902c2008-08-06 18:14:38 +0000173 /* "relatime" */ MS_RELATIME,
174 /* "norelatime" */ ~MS_RELATIME,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000175 /* "loud" */ ~MS_SILENT,
Eric Andersen9601a1c2006-03-20 18:07:50 +0000176
Rob Landleye3781b72006-08-08 01:39:49 +0000177 // action flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000178 /* "bind" */ MS_BIND,
179 /* "move" */ MS_MOVE,
180 /* "shared" */ MS_SHARED,
181 /* "slave" */ MS_SLAVE,
182 /* "private" */ MS_PRIVATE,
183 /* "unbindable" */ MS_UNBINDABLE,
184 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
185 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
186 /* "rprivate" */ MS_SLAVE|MS_RECURSIVE,
187 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
Rob Landleye3781b72006-08-08 01:39:49 +0000188 )
189
190 // Always understood.
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000191 /* "ro" */ MS_RDONLY, // vfs flag
192 /* "rw" */ ~MS_RDONLY, // vfs flag
193 /* "remount" */ MS_REMOUNT // action flag
Eric Andersencc8ed391999-10-05 16:24:54 +0000194};
195
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000196static const char mount_option_str[] =
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000197 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000198 "loop\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000199 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000200 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000201 "defaults\0"
202 // "quiet\0" - do not filter out, vfat wants to see it
203 "noauto\0"
204 "sw\0"
205 "swap\0"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000206 IF_DESKTOP("user\0")
207 IF_DESKTOP("users\0")
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000208 "_netdev\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000209 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000210 IF_FEATURE_MOUNT_FLAGS(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000211 // vfs flags
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000212 "nosuid\0"
213 "suid\0"
214 "dev\0"
215 "nodev\0"
216 "exec\0"
217 "noexec\0"
218 "sync\0"
219 "dirsync\0"
220 "async\0"
221 "atime\0"
222 "noatime\0"
223 "diratime\0"
224 "nodiratime\0"
225 "mand\0"
226 "nomand\0"
227 "relatime\0"
228 "norelatime\0"
229 "loud\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000230
231 // action flags
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000232 "bind\0"
233 "move\0"
234 "shared\0"
235 "slave\0"
236 "private\0"
237 "unbindable\0"
238 "rshared\0"
239 "rslave\0"
240 "rprivate\0"
241 "runbindable\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000242 )
243
244 // Always understood.
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000245 "ro\0" // vfs flag
246 "rw\0" // vfs flag
247 "remount\0" // action flag
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000248;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000249
Denis Vlasenkof732e962008-02-18 12:07:49 +0000250
251struct globals {
252#if ENABLE_FEATURE_MOUNT_NFS
253 smalluint nfs_mount_version;
254#endif
255#if ENABLE_FEATURE_MOUNT_VERBOSE
256 unsigned verbose;
257#endif
258 llist_t *fslist;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000259 char getmntent_buf[1];
Denis Vlasenkof732e962008-02-18 12:07:49 +0000260
261};
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000262enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
Denis Vlasenkof732e962008-02-18 12:07:49 +0000263#define G (*(struct globals*)&bb_common_bufsiz1)
264#define nfs_mount_version (G.nfs_mount_version)
Denis Vlasenkob4133682008-02-18 13:05:38 +0000265#if ENABLE_FEATURE_MOUNT_VERBOSE
Denis Vlasenkof732e962008-02-18 12:07:49 +0000266#define verbose (G.verbose )
Denis Vlasenkob4133682008-02-18 13:05:38 +0000267#else
268#define verbose 0
269#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000270#define fslist (G.fslist )
271#define getmntent_buf (G.getmntent_buf )
272
273
274#if ENABLE_FEATURE_MOUNT_VERBOSE
275static int verbose_mount(const char *source, const char *target,
276 const char *filesystemtype,
277 unsigned long mountflags, const void *data)
278{
279 int rc;
280
281 errno = 0;
282 rc = mount(source, target, filesystemtype, mountflags, data);
283 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000284 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000285 source, target, filesystemtype,
286 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000287 return rc;
288}
289#else
290#define verbose_mount(...) mount(__VA_ARGS__)
291#endif
292
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +0000293#if ENABLE_FEATURE_MOUNT_LABEL
294static void resolve_mount_spec(char **fsname)
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000295{
296 char *tmp = NULL;
297
298 if (!strncmp(*fsname, "UUID=", 5))
299 tmp = get_devname_from_uuid(*fsname + 5);
300 else if (!strncmp(*fsname, "LABEL=", 6))
301 tmp = get_devname_from_label(*fsname + 6);
302
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +0000303 if (tmp)
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000304 *fsname = tmp;
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000305}
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +0000306#else
307#define resolve_mount_spec(fsname) ((void)0)
308#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000309
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000310// Append mount options to string
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000311static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000312{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000313 if (*oldopts && **oldopts) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000314 // Do not insert options which are already there
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000315 while (newopts[0]) {
316 char *p;
317 int len = strlen(newopts);
318 p = strchr(newopts, ',');
319 if (p) len = p - newopts;
320 p = *oldopts;
321 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000322 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000323 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000324 goto skip;
325 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000326 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000327 p++;
328 }
329 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
330 free(*oldopts);
331 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000332 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000333 newopts += len;
334 while (newopts[0] == ',') newopts++;
335 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000336 } else {
337 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000338 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000339 }
340}
341
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000342// Use the mount_options list to parse options into flags.
343// Also return list of unrecognized options if unrecognized != NULL
Denis Vlasenkob4133682008-02-18 13:05:38 +0000344static long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000345{
Denis Vlasenkob4133682008-02-18 13:05:38 +0000346 long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000347
Rob Landley6a6798b2005-08-10 20:35:54 +0000348 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000349 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000350 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000351 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000352 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000353
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000354 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000355
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000356// FIXME: use hasmntopt()
Rob Landley6a6798b2005-08-10 20:35:54 +0000357 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000358 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000359 if (!strcasecmp(option_str, options)) {
360 long fl = mount_options[i];
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000361 if (fl < 0) flags &= fl;
Rob Landleydc0955b2006-03-14 18:16:25 +0000362 else flags |= fl;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000363 break;
364 }
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000365 option_str += strlen(option_str) + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000366 }
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000367 // If unrecognized not NULL, append unrecognized mount options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000368 if (unrecognized && i == ARRAY_SIZE(mount_options)) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000369 // Add it to strflags, to pass on to kernel
Rob Landleydc0955b2006-03-14 18:16:25 +0000370 i = *unrecognized ? strlen(*unrecognized) : 0;
Denis Vlasenkodeeed592008-07-08 05:14:36 +0000371 *unrecognized = xrealloc(*unrecognized, i + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000372
Rob Landley6a6798b2005-08-10 20:35:54 +0000373 // Comma separated if it's not the first one
Rob Landleydc0955b2006-03-14 18:16:25 +0000374 if (i) (*unrecognized)[i++] = ',';
375 strcpy((*unrecognized)+i, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000376 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000377
Denis Vlasenko2535f122007-09-15 13:28:30 +0000378 if (!comma)
379 break;
380 // Advance to next option
381 *comma = ',';
382 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000383 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000384
Rob Landleydc0955b2006-03-14 18:16:25 +0000385 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000386}
387
Rob Landleydc0955b2006-03-14 18:16:25 +0000388// Return a list of all block device backed filesystems
Rob Landleydc0955b2006-03-14 18:16:25 +0000389static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000390{
Denis Vlasenko87468852007-04-13 23:22:00 +0000391 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000392 "/etc/filesystems",
393 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000394 };
395 char *fs, *buf;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200396 llist_t *list = NULL;
Rob Landleydc0955b2006-03-14 18:16:25 +0000397 int i;
398 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000399
Denis Vlasenko87468852007-04-13 23:22:00 +0000400 for (i = 0; i < 2; i++) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000401 f = fopen_for_read(filesystems[i]);
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000402 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000403
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000404 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200405 if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
Denis Vlasenko372686b2006-10-12 22:42:33 +0000406 continue;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000407 fs = skip_whitespace(buf);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200408 if (*fs == '#' || *fs == '*' || !*fs)
409 continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000410
Denis Vlasenko372686b2006-10-12 22:42:33 +0000411 llist_add_to_end(&list, xstrdup(fs));
412 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000413 }
414 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
415 }
416
417 return list;
418}
419
Rob Landleydc0955b2006-03-14 18:16:25 +0000420#if ENABLE_FEATURE_CLEAN_UP
421static void delete_block_backed_filesystems(void)
422{
Rob Landleya6b5b602006-05-08 19:03:07 +0000423 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000424}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000425#else
426void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000427#endif
428
Rob Landleydc0955b2006-03-14 18:16:25 +0000429// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000430// NB: mp->xxx fields may be trashed on exit
Denis Vlasenkob4133682008-02-18 13:05:38 +0000431static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000432{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000433 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000434
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200435 if (FAKE_IT) {
Denis Vlasenkob4133682008-02-18 13:05:38 +0000436 if (verbose >= 2)
437 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
438 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
439 vfsflags, filteropts);
440 goto mtab;
441 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000442
Rob Landleydc0955b2006-03-14 18:16:25 +0000443 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000444 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000445 errno = 0;
446 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000447 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000448
449 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000450 // helper program mount.<mnt_type>
Denys Vlasenkoba986032009-09-15 23:00:09 +0200451 if (HELPERS_ALLOWED && rc && mp->mnt_type) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200452 char *args[8];
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000453 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000454 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000455 rc = 1;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200456 if (FAKE_IT)
457 args[rc++] = (char *)"-f";
458 if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
459 args[rc++] = (char *)"-n";
Denis Vlasenko5c329932009-04-12 12:16:21 +0000460 args[rc++] = mp->mnt_fsname;
461 args[rc++] = mp->mnt_dir;
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000462 if (filteropts) {
463 args[rc++] = (char *)"-o";
464 args[rc++] = filteropts;
465 }
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000466 args[rc] = NULL;
467 rc = wait4pid(spawn(args));
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000468 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000469 if (!rc)
470 break;
471 errno = errno_save;
472 }
473
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000474 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000475 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000476 if (!(vfsflags & MS_SILENT))
477 bb_error_msg("%s is write-protected, mounting read-only",
478 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000479 vfsflags |= MS_RDONLY;
480 }
481
Rob Landleydc0955b2006-03-14 18:16:25 +0000482 // Abort entirely if permission denied.
483
484 if (rc && errno == EPERM)
485 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
486
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000487 // If the mount was successful, and we're maintaining an old-style
488 // mtab file by hand, add the new entry to it now.
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000489 mtab:
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200490 if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000491 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000492 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000493 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000494 int i;
495
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000496 if (!mountTable) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000497 bb_error_msg("no %s", bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000498 goto ret;
499 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000500
501 // Add vfs string flags
502
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000503 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
504 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
505 append_mount_options(&(mp->mnt_opts), option_str);
506 option_str += strlen(option_str) + 1;
507 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000508
509 // Remove trailing / (if any) from directory we mounted on
510
Denis Vlasenko727ef942006-09-14 13:19:19 +0000511 i = strlen(mp->mnt_dir) - 1;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000512 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000513
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000514 // Convert to canonical pathnames as needed
Denis Vlasenko727ef942006-09-14 13:19:19 +0000515
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000516 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000517 fsname = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000518 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000519 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000520 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000521 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000522 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000523
524 // Write and close.
525
Denis Vlasenko727ef942006-09-14 13:19:19 +0000526 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000527 endmntent(mountTable);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000528 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000529 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000530 free(fsname);
531 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000532 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000533 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000534 return rc;
535}
536
Denis Vlasenko25098f72006-09-14 15:46:33 +0000537#if ENABLE_FEATURE_MOUNT_NFS
538
539/*
540 * Linux NFS mount
541 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
542 *
543 * Licensed under GPLv2, see file LICENSE in this tarball for details.
544 *
545 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
546 * numbers to be specified on the command line.
547 *
548 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
549 * Omit the call to connect() for Linux version 1.3.11 or later.
550 *
551 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
552 * Implemented the "bg", "fg" and "retry" mount options for NFS.
553 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000554 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000555 * - added Native Language Support
556 *
557 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
558 * plus NFSv3 stuff.
559 */
560
Denis Vlasenko25098f72006-09-14 15:46:33 +0000561/* This is just a warning of a common mistake. Possibly this should be a
562 * uclibc faq entry rather than in busybox... */
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000563#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000564#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
565#endif
566
567#define MOUNTPORT 635
568#define MNTPATHLEN 1024
569#define MNTNAMLEN 255
570#define FHSIZE 32
571#define FHSIZE3 64
572
573typedef char fhandle[FHSIZE];
574
575typedef struct {
576 unsigned int fhandle3_len;
577 char *fhandle3_val;
578} fhandle3;
579
580enum mountstat3 {
581 MNT_OK = 0,
582 MNT3ERR_PERM = 1,
583 MNT3ERR_NOENT = 2,
584 MNT3ERR_IO = 5,
585 MNT3ERR_ACCES = 13,
586 MNT3ERR_NOTDIR = 20,
587 MNT3ERR_INVAL = 22,
588 MNT3ERR_NAMETOOLONG = 63,
589 MNT3ERR_NOTSUPP = 10004,
590 MNT3ERR_SERVERFAULT = 10006,
591};
592typedef enum mountstat3 mountstat3;
593
594struct fhstatus {
595 unsigned int fhs_status;
596 union {
597 fhandle fhs_fhandle;
598 } fhstatus_u;
599};
600typedef struct fhstatus fhstatus;
601
602struct mountres3_ok {
603 fhandle3 fhandle;
604 struct {
605 unsigned int auth_flavours_len;
606 char *auth_flavours_val;
607 } auth_flavours;
608};
609typedef struct mountres3_ok mountres3_ok;
610
611struct mountres3 {
612 mountstat3 fhs_status;
613 union {
614 mountres3_ok mountinfo;
615 } mountres3_u;
616};
617typedef struct mountres3 mountres3;
618
619typedef char *dirpath;
620
621typedef char *name;
622
623typedef struct mountbody *mountlist;
624
625struct mountbody {
626 name ml_hostname;
627 dirpath ml_directory;
628 mountlist ml_next;
629};
630typedef struct mountbody mountbody;
631
632typedef struct groupnode *groups;
633
634struct groupnode {
635 name gr_name;
636 groups gr_next;
637};
638typedef struct groupnode groupnode;
639
640typedef struct exportnode *exports;
641
642struct exportnode {
643 dirpath ex_dir;
644 groups ex_groups;
645 exports ex_next;
646};
647typedef struct exportnode exportnode;
648
649struct ppathcnf {
650 int pc_link_max;
651 short pc_max_canon;
652 short pc_max_input;
653 short pc_name_max;
654 short pc_path_max;
655 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000656 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000657 char pc_xxx;
658 short pc_mask[2];
659};
660typedef struct ppathcnf ppathcnf;
661
662#define MOUNTPROG 100005
663#define MOUNTVERS 1
664
665#define MOUNTPROC_NULL 0
666#define MOUNTPROC_MNT 1
667#define MOUNTPROC_DUMP 2
668#define MOUNTPROC_UMNT 3
669#define MOUNTPROC_UMNTALL 4
670#define MOUNTPROC_EXPORT 5
671#define MOUNTPROC_EXPORTALL 6
672
673#define MOUNTVERS_POSIX 2
674
675#define MOUNTPROC_PATHCONF 7
676
677#define MOUNT_V3 3
678
679#define MOUNTPROC3_NULL 0
680#define MOUNTPROC3_MNT 1
681#define MOUNTPROC3_DUMP 2
682#define MOUNTPROC3_UMNT 3
683#define MOUNTPROC3_UMNTALL 4
684#define MOUNTPROC3_EXPORT 5
685
686enum {
687#ifndef NFS_FHSIZE
688 NFS_FHSIZE = 32,
689#endif
690#ifndef NFS_PORT
691 NFS_PORT = 2049
692#endif
693};
694
Denis Vlasenko25098f72006-09-14 15:46:33 +0000695/*
696 * We want to be able to compile mount on old kernels in such a way
697 * that the binary will work well on more recent kernels.
698 * Thus, if necessary we teach nfsmount.c the structure of new fields
699 * that will come later.
700 *
701 * Moreover, the new kernel includes conflict with glibc includes
702 * so it is easiest to ignore the kernel altogether (at compile time).
703 */
704
705struct nfs2_fh {
706 char data[32];
707};
708struct nfs3_fh {
709 unsigned short size;
710 unsigned char data[64];
711};
712
713struct nfs_mount_data {
714 int version; /* 1 */
715 int fd; /* 1 */
716 struct nfs2_fh old_root; /* 1 */
717 int flags; /* 1 */
718 int rsize; /* 1 */
719 int wsize; /* 1 */
720 int timeo; /* 1 */
721 int retrans; /* 1 */
722 int acregmin; /* 1 */
723 int acregmax; /* 1 */
724 int acdirmin; /* 1 */
725 int acdirmax; /* 1 */
726 struct sockaddr_in addr; /* 1 */
727 char hostname[256]; /* 1 */
728 int namlen; /* 2 */
729 unsigned int bsize; /* 3 */
730 struct nfs3_fh root; /* 4 */
731};
732
733/* bits in the flags field */
734enum {
735 NFS_MOUNT_SOFT = 0x0001, /* 1 */
736 NFS_MOUNT_INTR = 0x0002, /* 1 */
737 NFS_MOUNT_SECURE = 0x0004, /* 1 */
738 NFS_MOUNT_POSIX = 0x0008, /* 1 */
739 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
740 NFS_MOUNT_NOAC = 0x0020, /* 1 */
741 NFS_MOUNT_TCP = 0x0040, /* 2 */
742 NFS_MOUNT_VER3 = 0x0080, /* 3 */
743 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000744 NFS_MOUNT_NONLM = 0x0200, /* 3 */
745 NFS_MOUNT_NORDIRPLUS = 0x4000
Denis Vlasenko25098f72006-09-14 15:46:33 +0000746};
747
748
749/*
750 * We need to translate between nfs status return values and
751 * the local errno values which may not be the same.
752 *
753 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
754 * "after #include <errno.h> the symbol errno is reserved for any use,
755 * it cannot even be used as a struct tag or field name".
756 */
757
758#ifndef EDQUOT
759#define EDQUOT ENOSPC
760#endif
761
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000762/* Convert each NFSERR_BLAH into EBLAH */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000763static const struct {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000764 short stat;
765 short errnum;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000766} nfs_errtbl[] = {
767 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
768 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
769 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
770 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
771};
Denis Vlasenko25098f72006-09-14 15:46:33 +0000772static char *nfs_strerror(int status)
773{
774 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000775
776 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
777 if (nfs_errtbl[i].stat == status)
778 return strerror(nfs_errtbl[i].errnum);
779 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000780 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000781}
782
783static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
784{
785 if (!xdr_opaque(xdrs, objp, FHSIZE))
786 return FALSE;
787 return TRUE;
788}
789
790static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
791{
792 if (!xdr_u_int(xdrs, &objp->fhs_status))
793 return FALSE;
794 switch (objp->fhs_status) {
795 case 0:
796 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
797 return FALSE;
798 break;
799 default:
800 break;
801 }
802 return TRUE;
803}
804
805static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
806{
807 if (!xdr_string(xdrs, objp, MNTPATHLEN))
808 return FALSE;
809 return TRUE;
810}
811
812static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
813{
814 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
815 return FALSE;
816 return TRUE;
817}
818
819static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
820{
821 if (!xdr_fhandle3(xdrs, &objp->fhandle))
822 return FALSE;
823 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
Denys Vlasenko810b7162009-05-13 23:48:59 +0200824 sizeof(int), (xdrproc_t) xdr_int))
Denis Vlasenko25098f72006-09-14 15:46:33 +0000825 return FALSE;
826 return TRUE;
827}
828
829static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
830{
831 if (!xdr_enum(xdrs, (enum_t *) objp))
832 return FALSE;
833 return TRUE;
834}
835
836static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
837{
838 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
839 return FALSE;
840 switch (objp->fhs_status) {
841 case MNT_OK:
842 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
843 return FALSE;
844 break;
845 default:
846 break;
847 }
848 return TRUE;
849}
850
851#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
852
Denis Vlasenko25098f72006-09-14 15:46:33 +0000853/*
854 * Unfortunately, the kernel prints annoying console messages
855 * in case of an unexpected nfs mount version (instead of
856 * just returning some error). Therefore we'll have to try
857 * and figure out what version the kernel expects.
858 *
859 * Variables:
860 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
861 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
862 * nfs_mount_version: version this source and running kernel can handle
863 */
864static void
865find_kernel_nfs_mount_version(void)
866{
Denis Vlasenkob9256052007-09-28 10:29:17 +0000867 int kernel_version;
868
869 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000870 return;
871
872 nfs_mount_version = 4; /* default */
873
874 kernel_version = get_linux_version_code();
875 if (kernel_version) {
876 if (kernel_version < KERNEL_VERSION(2,1,32))
877 nfs_mount_version = 1;
878 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
879 (kernel_version >= KERNEL_VERSION(2,3,0) &&
880 kernel_version < KERNEL_VERSION(2,3,99)))
881 nfs_mount_version = 3;
882 /* else v4 since 2.3.99pre4 */
883 }
884}
885
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +0000886static void
Denis Vlasenkob9256052007-09-28 10:29:17 +0000887get_mountport(struct pmap *pm_mnt,
888 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +0000889 long unsigned prog,
890 long unsigned version,
891 long unsigned proto,
892 long unsigned port)
893{
894 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000895
896 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +0000897/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
898 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000899 pmap = pmap_getmaps(server_addr);
900
901 if (version > MAX_NFSPROT)
902 version = MAX_NFSPROT;
903 if (!prog)
904 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000905 pm_mnt->pm_prog = prog;
906 pm_mnt->pm_vers = version;
907 pm_mnt->pm_prot = proto;
908 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000909
Denis Vlasenko25098f72006-09-14 15:46:33 +0000910 while (pmap) {
911 if (pmap->pml_map.pm_prog != prog)
912 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000913 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000914 goto next;
915 if (version > 2 && pmap->pml_map.pm_vers != version)
916 goto next;
917 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
918 goto next;
919 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
Denis Vlasenkob9256052007-09-28 10:29:17 +0000920 (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto) ||
Denis Vlasenko25098f72006-09-14 15:46:33 +0000921 (port && pmap->pml_map.pm_port != port))
922 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000923 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
924 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +0000925 pmap = pmap->pml_next;
926 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000927 if (!pm_mnt->pm_vers)
928 pm_mnt->pm_vers = MOUNTVERS;
929 if (!pm_mnt->pm_port)
930 pm_mnt->pm_port = MOUNTPORT;
931 if (!pm_mnt->pm_prot)
932 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000933}
934
Denis Vlasenkof0000652007-09-04 18:30:26 +0000935#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +0000936static int daemonize(void)
937{
Denis Vlasenko25098f72006-09-14 15:46:33 +0000938 int pid = fork();
939 if (pid < 0) /* error */
940 return -errno;
941 if (pid > 0) /* parent */
942 return 0;
943 /* child */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000944 close(0);
945 xopen(bb_dev_null, O_RDWR);
946 xdup2(0, 1);
947 xdup2(0, 2);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000948 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +0000949 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000950 logmode = LOGMODE_SYSLOG;
951 return 1;
952}
Denis Vlasenkof0000652007-09-04 18:30:26 +0000953#else
954static inline int daemonize(void) { return -ENOSYS; }
955#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000956
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000957/* TODO */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000958static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000959{
960 return 0;
961}
962
963/* RPC strerror analogs are terminally idiotic:
964 * *mandatory* prefix and \n at end.
965 * This hopefully helps. Usage:
966 * error_msg_rpc(clnt_*error*(" ")) */
967static void error_msg_rpc(const char *msg)
968{
Denis Vlasenko23514fe2006-09-19 14:07:52 +0000969 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000970 while (msg[0] == ' ' || msg[0] == ':') msg++;
971 len = strlen(msg);
972 while (len && msg[len-1] == '\n') len--;
973 bb_error_msg("%.*s", len, msg);
974}
975
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000976/* NB: mp->xxx fields may be trashed on exit */
Denys Vlasenko810b7162009-05-13 23:48:59 +0200977static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000978{
979 CLIENT *mclient;
980 char *hostname;
981 char *pathname;
982 char *mounthost;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +0200983 /* prior to 2.6.23, kernel took NFS options in a form of this struct
984 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
985 * then data pointer is interpreted as a string. */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000986 struct nfs_mount_data data;
987 char *opt;
988 struct hostent *hp;
989 struct sockaddr_in server_addr;
990 struct sockaddr_in mount_server_addr;
991 int msock, fsock;
992 union {
993 struct fhstatus nfsv2;
994 struct mountres3 nfsv3;
995 } status;
996 int daemonized;
997 char *s;
998 int port;
999 int mountport;
1000 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001001#if BB_MMU
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001002 smallint bg = 0;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001003#else
1004 enum { bg = 0 };
1005#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001006 int retry;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001007 int mountprog;
1008 int mountvers;
1009 int nfsprog;
1010 int nfsvers;
1011 int retval;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001012 /* these all are one-bit really. gcc 4.3.1 likes this combination: */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001013 smallint tcp;
1014 smallint soft;
1015 int intr;
1016 int posix;
1017 int nocto;
1018 int noac;
1019 int nordirplus;
1020 int nolock;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001021
1022 find_kernel_nfs_mount_version();
1023
1024 daemonized = 0;
1025 mounthost = NULL;
1026 retval = ETIMEDOUT;
1027 msock = fsock = -1;
1028 mclient = NULL;
1029
1030 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1031
1032 filteropts = xstrdup(filteropts); /* going to trash it later... */
1033
1034 hostname = xstrdup(mp->mnt_fsname);
1035 /* mount_main() guarantees that ':' is there */
1036 s = strchr(hostname, ':');
1037 pathname = s + 1;
1038 *s = '\0';
1039 /* Ignore all but first hostname in replicated mounts
1040 until they can be fully supported. (mack@sgi.com) */
1041 s = strchr(hostname, ',');
1042 if (s) {
1043 *s = '\0';
1044 bb_error_msg("warning: multiple hostnames not supported");
1045 }
1046
1047 server_addr.sin_family = AF_INET;
1048 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1049 hp = gethostbyname(hostname);
1050 if (hp == NULL) {
1051 bb_herror_msg("%s", hostname);
1052 goto fail;
1053 }
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001054 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001055 bb_error_msg("got bad hp->h_length");
1056 hp->h_length = sizeof(struct in_addr);
1057 }
1058 memcpy(&server_addr.sin_addr,
1059 hp->h_addr, hp->h_length);
1060 }
1061
1062 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1063
1064 /* add IP address to mtab options for use when unmounting */
1065
1066 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1067 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1068 } else {
1069 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1070 mp->mnt_opts[0] ? "," : "",
1071 inet_ntoa(server_addr.sin_addr));
1072 free(mp->mnt_opts);
1073 mp->mnt_opts = tmp;
1074 }
1075
1076 /* Set default options.
1077 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1078 * let the kernel decide.
1079 * timeo is filled in after we know whether it'll be TCP or UDP. */
1080 memset(&data, 0, sizeof(data));
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001081 data.retrans = 3;
1082 data.acregmin = 3;
1083 data.acregmax = 60;
1084 data.acdirmin = 30;
1085 data.acdirmax = 60;
1086 data.namlen = NAME_MAX;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001087
Denis Vlasenko25098f72006-09-14 15:46:33 +00001088 soft = 0;
1089 intr = 0;
1090 posix = 0;
1091 nocto = 0;
1092 nolock = 0;
1093 noac = 0;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001094 nordirplus = 0;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001095 retry = 10000; /* 10000 minutes ~ 1 week */
1096 tcp = 0;
1097
1098 mountprog = MOUNTPROG;
1099 mountvers = 0;
1100 port = 0;
1101 mountport = 0;
1102 nfsprog = 100003;
1103 nfsvers = 0;
1104
1105 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001106 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001107 char *opteq = strchr(opt, '=');
1108 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001109 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001110 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001111 /* 0 */ "rsize\0"
1112 /* 1 */ "wsize\0"
1113 /* 2 */ "timeo\0"
1114 /* 3 */ "retrans\0"
1115 /* 4 */ "acregmin\0"
1116 /* 5 */ "acregmax\0"
1117 /* 6 */ "acdirmin\0"
1118 /* 7 */ "acdirmax\0"
1119 /* 8 */ "actimeo\0"
1120 /* 9 */ "retry\0"
1121 /* 10 */ "port\0"
1122 /* 11 */ "mountport\0"
1123 /* 12 */ "mounthost\0"
1124 /* 13 */ "mountprog\0"
1125 /* 14 */ "mountvers\0"
1126 /* 15 */ "nfsprog\0"
1127 /* 16 */ "nfsvers\0"
1128 /* 17 */ "vers\0"
1129 /* 18 */ "proto\0"
1130 /* 19 */ "namlen\0"
1131 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001132
1133 *opteq++ = '\0';
1134 idx = index_in_strings(options, opt);
1135 switch (idx) {
1136 case 12: // "mounthost"
1137 mounthost = xstrndup(opteq,
1138 strcspn(opteq, " \t\n\r,"));
1139 continue;
1140 case 18: // "proto"
1141 if (!strncmp(opteq, "tcp", 3))
1142 tcp = 1;
1143 else if (!strncmp(opteq, "udp", 3))
1144 tcp = 0;
1145 else
1146 bb_error_msg("warning: unrecognized proto= option");
1147 continue;
1148 case 20: // "addr" - ignore
1149 continue;
1150 }
1151
1152 val = xatoi_u(opteq);
1153 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001154 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001155 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001156 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001157 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001158 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001159 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001160 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001161 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001162 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001163 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001164 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001165 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001166 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001167 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001168 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001169 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001170 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001171 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001172 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001173 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001174 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001175 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001176 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001177 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001178 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001179 data.acregmin = val;
1180 data.acregmax = val;
1181 data.acdirmin = val;
1182 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001183 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001184 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001185 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001186 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001187 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001188 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001189 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001190 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001191 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001192 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001193 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001194 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001195 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001196 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001197 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001198 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001199 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001200 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001201 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001202 case 16: // "nfsvers"
1203 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001204 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001205 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001206 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001207 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001208 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001209 //else
1210 // bb_error_msg("warning: option namlen is not supported\n");
1211 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001212 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001213 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1214 goto fail;
1215 }
1216 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001217 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001218 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001219 "bg\0"
1220 "fg\0"
1221 "soft\0"
1222 "hard\0"
1223 "intr\0"
1224 "posix\0"
1225 "cto\0"
1226 "ac\0"
1227 "tcp\0"
1228 "udp\0"
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001229 "lock\0"
1230 "rdirplus\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001231 int val = 1;
1232 if (!strncmp(opt, "no", 2)) {
1233 val = 0;
1234 opt += 2;
1235 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001236 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001237 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001238#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001239 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001240#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001241 break;
1242 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001243#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001244 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001245#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001246 break;
1247 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001248 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001249 break;
1250 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001251 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001252 break;
1253 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001254 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001255 break;
1256 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001257 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001258 break;
1259 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001260 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001261 break;
1262 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001263 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001264 break;
1265 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001266 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001267 break;
1268 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001269 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001270 break;
1271 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001272 if (nfs_mount_version >= 3)
1273 nolock = !val;
1274 else
1275 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001276 break;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001277 case 11: //rdirplus
1278 nordirplus = !val;
1279 break;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001280 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001281 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1282 goto fail;
1283 }
1284 }
1285 }
1286 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1287
1288 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1289 | (intr ? NFS_MOUNT_INTR : 0)
1290 | (posix ? NFS_MOUNT_POSIX : 0)
1291 | (nocto ? NFS_MOUNT_NOCTO : 0)
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001292 | (noac ? NFS_MOUNT_NOAC : 0)
1293 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001294 if (nfs_mount_version >= 2)
1295 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1296 if (nfs_mount_version >= 3)
1297 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1298 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1299 bb_error_msg("NFSv%d not supported", nfsvers);
1300 goto fail;
1301 }
1302 if (nfsvers && !mountvers)
1303 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1304 if (nfsvers && nfsvers < mountvers) {
1305 mountvers = nfsvers;
1306 }
1307
1308 /* Adjust options if none specified */
1309 if (!data.timeo)
1310 data.timeo = tcp ? 70 : 7;
1311
Denis Vlasenko25098f72006-09-14 15:46:33 +00001312 data.version = nfs_mount_version;
1313
1314 if (vfsflags & MS_REMOUNT)
1315 goto do_mount;
1316
1317 /*
1318 * If the previous mount operation on the same host was
1319 * backgrounded, and the "bg" for this mount is also set,
1320 * give up immediately, to avoid the initial timeout.
1321 */
1322 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001323 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001324 if (daemonized <= 0) { /* parent or error */
1325 retval = -daemonized;
1326 goto ret;
1327 }
1328 }
1329
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001330 /* Create mount daemon client */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001331 /* See if the nfs host = mount host. */
1332 if (mounthost) {
1333 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1334 mount_server_addr.sin_family = AF_INET;
1335 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1336 } else {
1337 hp = gethostbyname(mounthost);
1338 if (hp == NULL) {
1339 bb_herror_msg("%s", mounthost);
1340 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001341 }
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001342 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
1343 bb_error_msg("got bad hp->h_length");
1344 hp->h_length = sizeof(struct in_addr);
1345 }
1346 mount_server_addr.sin_family = AF_INET;
1347 memcpy(&mount_server_addr.sin_addr,
1348 hp->h_addr, hp->h_length);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001349 }
1350 }
1351
1352 /*
1353 * The following loop implements the mount retries. When the mount
1354 * times out, and the "bg" option is set, we background ourself
1355 * and continue trying.
1356 *
1357 * The case where the mount point is not present and the "bg"
1358 * option is set, is treated as a timeout. This is done to
1359 * support nested mounts.
1360 *
1361 * The "retry" count specified by the user is the number of
1362 * minutes to retry before giving up.
1363 */
1364 {
1365 struct timeval total_timeout;
1366 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001367 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001368 time_t t;
1369 time_t prevt;
1370 time_t timeout;
1371
1372 retry_timeout.tv_sec = 3;
1373 retry_timeout.tv_usec = 0;
1374 total_timeout.tv_sec = 20;
1375 total_timeout.tv_usec = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001376/* FIXME: use monotonic()? */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001377 timeout = time(NULL) + 60 * retry;
1378 prevt = 0;
1379 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001380 retry:
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001381 /* Be careful not to use too many CPU cycles */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001382 if (t - prevt < 30)
1383 sleep(30);
1384
Denis Vlasenkob9256052007-09-28 10:29:17 +00001385 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001386 mountprog,
1387 mountvers,
1388 proto,
1389 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001390 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001391
1392 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001393 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001394 msock = RPC_ANYSOCK;
1395
Denis Vlasenkob9256052007-09-28 10:29:17 +00001396 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001397 case IPPROTO_UDP:
1398 mclient = clntudp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001399 pm_mnt.pm_prog,
1400 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001401 retry_timeout,
1402 &msock);
1403 if (mclient)
1404 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001405 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001406 msock = RPC_ANYSOCK;
1407 case IPPROTO_TCP:
1408 mclient = clnttcp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001409 pm_mnt.pm_prog,
1410 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001411 &msock, 0, 0);
1412 break;
1413 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001414 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001415 }
1416 if (!mclient) {
1417 if (!daemonized && prevt == 0)
1418 error_msg_rpc(clnt_spcreateerror(" "));
1419 } else {
1420 enum clnt_stat clnt_stat;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001421
1422 /* Try to mount hostname:pathname */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001423 mclient->cl_auth = authunix_create_default();
1424
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001425 /* Make pointers in xdr_mountres3 NULL so
Denis Vlasenko25098f72006-09-14 15:46:33 +00001426 * that xdr_array allocates memory for us
1427 */
1428 memset(&status, 0, sizeof(status));
1429
Denis Vlasenkob9256052007-09-28 10:29:17 +00001430 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001431 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1432 (xdrproc_t) xdr_dirpath,
1433 (caddr_t) &pathname,
1434 (xdrproc_t) xdr_mountres3,
1435 (caddr_t) &status,
1436 total_timeout);
1437 else
1438 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1439 (xdrproc_t) xdr_dirpath,
1440 (caddr_t) &pathname,
1441 (xdrproc_t) xdr_fhstatus,
1442 (caddr_t) &status,
1443 total_timeout);
1444
1445 if (clnt_stat == RPC_SUCCESS)
1446 goto prepare_kernel_data; /* we're done */
1447 if (errno != ECONNREFUSED) {
1448 error_msg_rpc(clnt_sperror(mclient, " "));
1449 goto fail; /* don't retry */
1450 }
1451 /* Connection refused */
1452 if (!daemonized && prevt == 0) /* print just once */
1453 error_msg_rpc(clnt_sperror(mclient, " "));
1454 auth_destroy(mclient->cl_auth);
1455 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001456 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001457 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001458 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001459 }
1460
1461 /* Timeout. We are going to retry... maybe */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001462 if (!bg)
1463 goto fail;
1464 if (!daemonized) {
1465 daemonized = daemonize();
1466 if (daemonized <= 0) { /* parent or error */
1467 retval = -daemonized;
1468 goto ret;
1469 }
1470 }
1471 prevt = t;
1472 t = time(NULL);
1473 if (t >= timeout)
1474 /* TODO error message */
1475 goto fail;
1476
1477 goto retry;
1478 }
1479
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001480 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001481
1482 if (nfsvers == 2) {
1483 if (status.nfsv2.fhs_status != 0) {
1484 bb_error_msg("%s:%s failed, reason given by server: %s",
1485 hostname, pathname,
1486 nfs_strerror(status.nfsv2.fhs_status));
1487 goto fail;
1488 }
1489 memcpy(data.root.data,
1490 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1491 NFS_FHSIZE);
1492 data.root.size = NFS_FHSIZE;
1493 memcpy(data.old_root.data,
1494 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1495 NFS_FHSIZE);
1496 } else {
1497 fhandle3 *my_fhandle;
1498 if (status.nfsv3.fhs_status != 0) {
1499 bb_error_msg("%s:%s failed, reason given by server: %s",
1500 hostname, pathname,
1501 nfs_strerror(status.nfsv3.fhs_status));
1502 goto fail;
1503 }
1504 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1505 memset(data.old_root.data, 0, NFS_FHSIZE);
1506 memset(&data.root, 0, sizeof(data.root));
1507 data.root.size = my_fhandle->fhandle3_len;
1508 memcpy(data.root.data,
1509 (char *) my_fhandle->fhandle3_val,
1510 my_fhandle->fhandle3_len);
1511
1512 data.flags |= NFS_MOUNT_VER3;
1513 }
1514
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001515 /* Create nfs socket for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001516 if (tcp) {
1517 if (nfs_mount_version < 3) {
1518 bb_error_msg("NFS over TCP is not supported");
1519 goto fail;
1520 }
1521 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1522 } else
1523 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1524 if (fsock < 0) {
1525 bb_perror_msg("nfs socket");
1526 goto fail;
1527 }
1528 if (bindresvport(fsock, 0) < 0) {
1529 bb_perror_msg("nfs bindresvport");
1530 goto fail;
1531 }
1532 if (port == 0) {
1533 server_addr.sin_port = PMAPPORT;
1534 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1535 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1536 if (port == 0)
1537 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001538 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001539 server_addr.sin_port = htons(port);
1540
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001541 /* Prepare data structure for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001542 data.fd = fsock;
1543 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1544 strncpy(data.hostname, hostname, sizeof(data.hostname));
1545
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001546 /* Clean up */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001547 auth_destroy(mclient->cl_auth);
1548 clnt_destroy(mclient);
1549 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001550 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001551
1552 if (bg) {
1553 /* We must wait until mount directory is available */
1554 struct stat statbuf;
1555 int delay = 1;
1556 while (stat(mp->mnt_dir, &statbuf) == -1) {
1557 if (!daemonized) {
1558 daemonized = daemonize();
1559 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001560/* FIXME: parent doesn't close fsock - ??! */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001561 retval = -daemonized;
1562 goto ret;
1563 }
1564 }
1565 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1566 delay *= 2;
1567 if (delay > 30)
1568 delay = 30;
1569 }
1570 }
1571
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001572 /* Perform actual mount */
1573 do_mount:
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001574 mp->mnt_type = (char*)"nfs";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001575 retval = mount_it_now(mp, vfsflags, (char*)&data);
1576 goto ret;
1577
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001578 /* Abort */
1579 fail:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001580 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001581 if (mclient) {
1582 auth_destroy(mclient->cl_auth);
1583 clnt_destroy(mclient);
1584 }
1585 close(msock);
1586 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001587 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001588 close(fsock);
1589
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001590 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001591 free(hostname);
1592 free(mounthost);
1593 free(filteropts);
1594 return retval;
1595}
1596
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001597#else // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001598
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001599// Never called. Call should be optimized out.
Denis Vlasenkob4133682008-02-18 13:05:38 +00001600int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001601
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001602#endif // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001603
1604// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1605// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001606// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001607static int singlemount(struct mntent *mp, int ignore_busy)
1608{
Denis Vlasenkob4133682008-02-18 13:05:38 +00001609 int rc = -1;
1610 long vfsflags;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001611 char *loopFile = NULL, *filteropts = NULL;
1612 llist_t *fl = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001613 struct stat st;
1614
1615 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1616
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001617 // Treat fstype "auto" as unspecified
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001618 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1619 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001620
Denis Vlasenko2535f122007-09-15 13:28:30 +00001621 // Might this be a virtual filesystem?
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001622 if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1623 char *args[35];
1624 char *s;
1625 int n;
1626 // fsname: "cmd#arg1#arg2..."
1627 // WARNING: allows execution of arbitrary commands!
1628 // Try "mount 'sh#-c#sh' bogus_dir".
1629 // It is safe ONLY because non-root
1630 // cannot use two-argument mount command
1631 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1632 // "mount: can't find sh#-c#sh in /etc/fstab"
1633 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1634
1635 s = mp->mnt_fsname;
1636 n = 0;
1637 args[n++] = s;
1638 while (*s && n < 35 - 2) {
1639 if (*s++ == '#' && *s != '#') {
1640 s[-1] = '\0';
1641 args[n++] = s;
Denis Vlasenko2535f122007-09-15 13:28:30 +00001642 }
1643 }
Denis Vlasenko2535f122007-09-15 13:28:30 +00001644 args[n++] = mp->mnt_dir;
1645 args[n] = NULL;
1646 rc = wait4pid(xspawn(args));
1647 goto report_error;
1648 }
1649
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001650 // Might this be an CIFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001651 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001652 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1653 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1654 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001655 ) {
Denis Vlasenko5c329932009-04-12 12:16:21 +00001656#if 0 /* reported to break things */
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001657 len_and_sockaddr *lsa;
1658 char *ip, *dotted;
1659 char *s;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001660
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001661 // Replace '/' with '\' and verify that unc points to "//server/share".
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001662 for (s = mp->mnt_fsname; *s; ++s)
1663 if (*s == '/') *s = '\\';
1664
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001665 // Get server IP
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001666 s = strrchr(mp->mnt_fsname, '\\');
Denis Vlasenko5c329932009-04-12 12:16:21 +00001667 if (s <= mp->mnt_fsname+1)
1668 goto report_error;
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001669 *s = '\0';
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001670 lsa = host2sockaddr(mp->mnt_fsname+2, 0);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001671 *s = '\\';
Denis Vlasenko5c329932009-04-12 12:16:21 +00001672 if (!lsa)
1673 goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001674
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001675 // Insert ip=... option into string flags.
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001676 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001677 ip = xasprintf("ip=%s", dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001678 parse_mount_options(ip, &filteropts);
1679
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001680 // Compose new unc '\\server-ip\share'
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001681 // (s => slash after hostname)
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001682 mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
Denis Vlasenko5c329932009-04-12 12:16:21 +00001683#endif
1684 // Lock is required [why?]
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001685 vfsflags |= MS_MANDLOCK;
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001686 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001687 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko5c329932009-04-12 12:16:21 +00001688#if 0
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001689 if (ENABLE_FEATURE_CLEAN_UP) {
1690 free(mp->mnt_fsname);
1691 free(ip);
1692 free(dotted);
1693 free(lsa);
1694 }
Denis Vlasenko5c329932009-04-12 12:16:21 +00001695#endif
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001696 goto report_error;
1697 }
1698
1699 // Might this be an NFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001700 if (ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001701 && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001702 && strchr(mp->mnt_fsname, ':') != NULL
1703 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001704 rc = nfsmount(mp, vfsflags, filteropts);
1705 goto report_error;
1706 }
1707
1708 // Look at the file. (Not found isn't a failure for remount, or for
1709 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001710 // (We use stat, not lstat, in order to allow
1711 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001712 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001713 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1714 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001715 // Do we need to allocate a loopback device for it?
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001716 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1717 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001718 mp->mnt_fsname = NULL; // will receive malloced loop dev name
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001719 if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) {
1720 if (errno == EPERM || errno == EACCES)
1721 bb_error_msg(bb_msg_perm_denied_are_you_root);
1722 else
1723 bb_perror_msg("cannot setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001724 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001725 }
1726
1727 // Autodetect bind mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001728 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1729 vfsflags |= MS_BIND;
1730 }
1731
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001732 // If we know the fstype (or don't need to), jump straight
1733 // to the actual mount.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001734 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1735 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001736 else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001737 // Loop through filesystem types until mount succeeds
1738 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001739
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001740 // Initialize list of block backed filesystems. This has to be
1741 // done here so that during "mount -a", mounts after /proc shows up
1742 // can autodetect.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001743 if (!fslist) {
1744 fslist = get_block_backed_filesystems();
1745 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1746 atexit(delete_block_backed_filesystems);
1747 }
1748
1749 for (fl = fslist; fl; fl = fl->link) {
1750 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001751 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001752 if (!rc)
1753 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001754 }
1755 }
1756
1757 // If mount failed, clean up loop file (if any).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001758 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1759 del_loop(mp->mnt_fsname);
1760 if (ENABLE_FEATURE_CLEAN_UP) {
1761 free(loopFile);
1762 free(mp->mnt_fsname);
1763 }
1764 }
1765
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001766 report_error:
1767 if (ENABLE_FEATURE_CLEAN_UP)
1768 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001769
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001770 if (errno == EBUSY && ignore_busy)
1771 return 0;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001772 if (rc < 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001773 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001774 return rc;
1775}
1776
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001777/* -O support
1778 * Unlike -t, -O should interpret "no" prefix differently:
1779 * -t noa,b,c = -t no(a,b,c) = mount all except fs'es with types a,b, and c
1780 * -O noa,b,c = -O noa,b,c = mount all with without option a,
1781 * or with option b or c.
1782 * But for now we do not support -O a,b,c at all (only -O a).
1783 *
1784 * Another difference from -t support (match_fstype) is that
1785 * we need to examine the _list_ of options in fsopt, not just a string.
1786 */
1787static int match_opt(const char *fs_opt, const char *O_opt)
1788{
1789 int match = 1;
1790 int len;
1791
1792 if (!O_opt)
1793 return match;
1794
1795 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
1796 match--;
1797 O_opt += 2;
1798 }
1799
1800 len = strlen(O_opt);
1801 while (1) {
1802 if (strncmp(fs_opt, O_opt, len) == 0
1803 && (fs_opt[len] == '\0' || fs_opt[len] == ',')
1804 ) {
1805 return match;
1806 }
1807 fs_opt = strchr(fs_opt, ',');
1808 if (!fs_opt)
1809 break;
1810 fs_opt++;
1811 }
1812
1813 return !match;
1814}
1815
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001816// Parse options, if necessary parse fstab/mtab, and call singlemount for
1817// each directory to be mounted.
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001818static const char must_be_root[] ALIGN1 = "you must be root";
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001819
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001820int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001821int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001822{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001823 char *cmdopts = xzalloc(1);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001824 char *fstype = NULL;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001825 char *O_optmatch = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001826 char *storage_path;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001827 llist_t *lst_o = NULL;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001828 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001829 FILE *fstab;
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001830 int i, j, rc = 0;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001831 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001832 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001833 IF_NOT_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001834
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001835 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00001836
Denis Vlasenkof732e962008-02-18 12:07:49 +00001837 // Parse long options, like --bind and --move. Note that -o option
1838 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001839 for (i = j = 1; argv[i]; i++) {
1840 if (argv[i][0] == '-' && argv[i][1] == '-')
1841 append_mount_options(&cmdopts, argv[i] + 2);
1842 else
1843 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001844 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001845 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001846
1847 // Parse remaining options
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001848 // Max 2 params; -o is a list, -v is a counter
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001849 opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001850 opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001851 IF_FEATURE_MOUNT_VERBOSE(, &verbose));
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001852 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00001853 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
1854 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001855 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001856
1857 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001858 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00001859 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001860 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1861
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001862 if (!mountTable)
1863 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001864
Denis Vlasenko2535f122007-09-15 13:28:30 +00001865 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001866 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001867 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001868 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00001869 // util-linux 2.12a happily shows rootfs...
1870 //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001871
1872 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1873 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1874 mtpair->mnt_dir, mtpair->mnt_type,
1875 mtpair->mnt_opts);
1876 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001877 if (ENABLE_FEATURE_CLEAN_UP)
1878 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001879 return EXIT_SUCCESS;
1880 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001881 storage_path = NULL;
1882 } else {
1883 // When we have two arguments, the second is the directory and we can
1884 // skip looking at fstab entirely. We can always abspath() the directory
1885 // argument when we get it.
1886 if (argv[1]) {
1887 if (nonroot)
1888 bb_error_msg_and_die(must_be_root);
1889 mtpair->mnt_fsname = argv[0];
1890 mtpair->mnt_dir = argv[1];
1891 mtpair->mnt_type = fstype;
1892 mtpair->mnt_opts = cmdopts;
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00001893 resolve_mount_spec(&mtpair->mnt_fsname);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001894 rc = singlemount(mtpair, 0);
1895 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00001896 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001897 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001898 }
1899
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001900 // Past this point, we are handling either "mount -a [opts]"
1901 // or "mount [opts] single_param"
1902
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001903 i = parse_mount_options(cmdopts, NULL); // FIXME: should be "long", not "int"
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001904 if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
1905 bb_error_msg_and_die(must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00001906
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001907 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001908 if (ENABLE_FEATURE_MOUNT_FLAGS
1909 && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
1910 ) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001911 // verbose_mount(source, target, type, flags, data)
1912 rc = verbose_mount("", argv[0], "", i, "");
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001913 if (rc)
1914 bb_simple_perror_msg_and_die(argv[0]);
1915 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001916 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001917
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001918 // Open either fstab or mtab
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001919 fstabname = "/etc/fstab";
1920 if (i & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001921 // WARNING. I am not sure this matches util-linux's
1922 // behavior. It's possible util-linux does not
1923 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001924 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001925 }
1926 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001927 if (!fstab)
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001928 bb_perror_msg_and_die("cannot read %s", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001929
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001930 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00001931 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001932 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001933 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001934
1935 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00001936 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001937 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001938 GETMNTENT_BUFSIZE/2)
1939 ) { // End of fstab/mtab is reached
1940 mtcur = mtother; // the thing we found last time
1941 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001942 }
1943
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001944 // If we're trying to mount something specific and this isn't it,
1945 // skip it. Note we must match the exact text in fstab (ala
1946 // "proc") or a full path from root
1947 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001948
1949 // Is this what we're looking for?
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001950 if (strcmp(argv[0], mtcur->mnt_fsname) &&
1951 strcmp(storage_path, mtcur->mnt_fsname) &&
1952 strcmp(argv[0], mtcur->mnt_dir) &&
1953 strcmp(storage_path, mtcur->mnt_dir)) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001954
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001955 // Remember this entry. Something later may have
1956 // overmounted it, and we want the _last_ match.
1957 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001958
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001959 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001960 } else {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001961 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001962 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001963 if (nonroot)
1964 bb_error_msg_and_die(must_be_root);
1965
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001966 // Does type match? (NULL matches always)
1967 if (!match_fstype(mtcur, fstype))
1968 continue;
1969
1970 // Skip noauto and swap anyway.
1971 if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
1972 // swap is bogus "fstype", parse_mount_options can't check fstypes
1973 || strcasecmp(mtcur->mnt_type, "swap") == 0
1974 ) {
1975 continue;
1976 }
1977
1978 // Does (at least one) option match?
1979 // (NULL matches always)
1980 if (!match_opt(mtcur->mnt_opts, O_optmatch))
1981 continue;
1982
1983 resolve_mount_spec(&mtcur->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001984
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001985 // NFS mounts want this to be xrealloc-able
1986 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00001987
1988 // Mount this thing
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001989 if (singlemount(mtcur, 1)) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001990 // Count number of failed mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001991 rc++;
1992 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001993 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001994 }
1995 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001996
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001997 // End of fstab/mtab is reached.
1998 // Were we looking for something specific?
1999 if (argv[0]) {
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002000 long l;
2001
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002002 // If we didn't find anything, complain
2003 if (!mtcur->mnt_fsname)
2004 bb_error_msg_and_die("can't find %s in %s",
2005 argv[0], fstabname);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002006
2007 // What happens when we try to "mount swap_partition"?
2008 // (fstab containts "swap_partition swap swap defaults 0 0")
2009 // util-linux-ng 2.13.1 does this:
2010 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2011 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2012 // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory)
2013 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2014 // exit_group(32) = ?
2015#if 0
2016 // In case we want to simply skip swap partitions:
2017 l = parse_mount_options(mtcur->mnt_opts, NULL);
2018 if ((l & MOUNT_SWAP)
2019 // swap is bogus "fstype", parse_mount_options can't check fstypes
2020 || strcasecmp(mtcur->mnt_type, "swap") == 0
2021 ) {
2022 goto ret;
2023 }
2024#endif
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002025 if (nonroot) {
2026 // fstab must have "users" or "user"
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002027 l = parse_mount_options(mtcur->mnt_opts, NULL);
2028 if (!(l & MOUNT_USERS))
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002029 bb_error_msg_and_die(must_be_root);
2030 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002031
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002032 // Mount the last thing we found
2033 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2034 append_mount_options(&(mtcur->mnt_opts), cmdopts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002035 resolve_mount_spec(&mtpair->mnt_fsname);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002036 rc = singlemount(mtcur, 0);
2037 if (ENABLE_FEATURE_CLEAN_UP)
2038 free(mtcur->mnt_opts);
2039 }
2040
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002041 //ret:
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002042 if (ENABLE_FEATURE_CLEAN_UP)
2043 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002044 if (ENABLE_FEATURE_CLEAN_UP) {
2045 free(storage_path);
2046 free(cmdopts);
2047 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002048 return rc;
2049}