blob: 30037f9ab08b099d2be4b276feea8504ce0e5940 [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>
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +020022#ifndef MS_UNION
23# define MS_UNION (1 << 8)
24#endif
Denys Vlasenkoda49f582009-07-08 02:58:38 +020025#ifndef MS_BIND
26# define MS_BIND (1 << 12)
27#endif
28#ifndef MS_MOVE
29# define MS_MOVE (1 << 13)
30#endif
31#ifndef MS_RECURSIVE
32# define MS_RECURSIVE (1 << 14)
33#endif
34#ifndef MS_SILENT
35# define MS_SILENT (1 << 15)
36#endif
37/* The shared subtree stuff, which went in around 2.6.15. */
38#ifndef MS_UNBINDABLE
39# define MS_UNBINDABLE (1 << 17)
40#endif
41#ifndef MS_PRIVATE
42# define MS_PRIVATE (1 << 18)
43#endif
44#ifndef MS_SLAVE
45# define MS_SLAVE (1 << 19)
46#endif
47#ifndef MS_SHARED
48# define MS_SHARED (1 << 20)
49#endif
50#ifndef MS_RELATIME
51# define MS_RELATIME (1 << 21)
52#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +000053#include "libbb.h"
Eric Andersenbd22ed82000-07-08 18:55:24 +000054
Denis Vlasenko6aa76962008-03-18 01:44:52 +000055#if ENABLE_FEATURE_MOUNT_LABEL
Natanael Copa9aff2992009-09-20 04:28:22 +020056# include "volume_id.h"
57#else
58# define resolve_mount_spec(fsname) ((void)0)
Denis Vlasenko6aa76962008-03-18 01:44:52 +000059#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +000060
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000061// Needed for nfs support only
Denis Vlasenko30a64cd2006-09-15 15:12:00 +000062#include <sys/utsname.h>
63#undef TRUE
64#undef FALSE
65#include <rpc/rpc.h>
66#include <rpc/pmap_prot.h>
67#include <rpc/pmap_clnt.h>
68
Denis Vlasenko2535f122007-09-15 13:28:30 +000069#ifndef MS_SILENT
70#define MS_SILENT (1 << 15)
71#endif
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000072// Grab more as needed from util-linux's mount/mount_constants.h
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +000073#ifndef MS_DIRSYNC
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000074#define MS_DIRSYNC 128 // Directory modifications are synchronous
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +000075#endif
76
Denis Vlasenko30a64cd2006-09-15 15:12:00 +000077
Denis Vlasenko908d6b72006-12-18 23:07:42 +000078#if defined(__dietlibc__)
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000079// 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
80// dietlibc-0.30 does not have implementation of getmntent_r()
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +000081static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000082 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
Denis Vlasenko908d6b72006-12-18 23:07:42 +000083{
Denis Vlasenko908d6b72006-12-18 23:07:42 +000084 struct mntent* ment = getmntent(stream);
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +000085 return memcpy(result, ment, sizeof(*ment));
Denis Vlasenko908d6b72006-12-18 23:07:42 +000086}
87#endif
88
89
Rob Landleydc0955b2006-03-14 18:16:25 +000090// Not real flags, but we want to be able to check for this.
Denis Vlasenko13c5a682006-10-16 22:39:51 +000091enum {
Denis Vlasenkoa4522c52008-03-17 08:46:43 +000092 MOUNT_USERS = (1 << 28) * ENABLE_DESKTOP,
93 MOUNT_NOAUTO = (1 << 29),
94 MOUNT_SWAP = (1 << 30),
Denis Vlasenko13c5a682006-10-16 22:39:51 +000095};
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +000096
97
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +000098#define OPTION_STR "o:t:rwanfvsiO:"
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +000099enum {
100 OPT_o = (1 << 0),
101 OPT_t = (1 << 1),
102 OPT_r = (1 << 2),
103 OPT_w = (1 << 3),
104 OPT_a = (1 << 4),
105 OPT_n = (1 << 5),
106 OPT_f = (1 << 6),
107 OPT_v = (1 << 7),
108 OPT_s = (1 << 8),
109 OPT_i = (1 << 9),
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +0000110 OPT_O = (1 << 10),
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000111};
112
113#if ENABLE_FEATURE_MTAB_SUPPORT
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200114#define USE_MTAB (!(option_mask32 & OPT_n))
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000115#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200116#define USE_MTAB 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000117#endif
118
119#if ENABLE_FEATURE_MOUNT_FAKE
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200120#define FAKE_IT (option_mask32 & OPT_f)
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000121#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200122#define FAKE_IT 0
123#endif
124
125#if ENABLE_FEATURE_MOUNT_HELPERS
126#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
127#else
128#define HELPERS_ALLOWED 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000129#endif
130
131
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000132// TODO: more "user" flag compatibility.
133// "user" option (from mount manpage):
134// Only the user that mounted a filesystem can unmount it again.
135// If any user should be able to unmount, then use users instead of user
136// in the fstab line. The owner option is similar to the user option,
137// with the restriction that the user must be the owner of the special file.
138// This may be useful e.g. for /dev/fd if a login script makes
139// the console user owner of this device.
Rob Landley3ba7bd12006-08-09 19:51:13 +0000140
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000141// Standard mount options (from -o options or --options),
142// with corresponding flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000143static const int32_t mount_options[] = {
Rob Landleye3781b72006-08-08 01:39:49 +0000144 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
Rob Landleydc0955b2006-03-14 18:16:25 +0000145
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000146 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000147 /* "loop" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000148 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000149
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000150 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000151 /* "defaults" */ 0,
152 /* "quiet" 0 - do not filter out, vfat wants to see it */
153 /* "noauto" */ MOUNT_NOAUTO,
154 /* "sw" */ MOUNT_SWAP,
155 /* "swap" */ MOUNT_SWAP,
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000156 IF_DESKTOP(/* "user" */ MOUNT_USERS,)
157 IF_DESKTOP(/* "users" */ MOUNT_USERS,)
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000158 /* "_netdev" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000159 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000160
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000161 IF_FEATURE_MOUNT_FLAGS(
Rob Landleye3781b72006-08-08 01:39:49 +0000162 // vfs flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000163 /* "nosuid" */ MS_NOSUID,
164 /* "suid" */ ~MS_NOSUID,
165 /* "dev" */ ~MS_NODEV,
166 /* "nodev" */ MS_NODEV,
167 /* "exec" */ ~MS_NOEXEC,
168 /* "noexec" */ MS_NOEXEC,
169 /* "sync" */ MS_SYNCHRONOUS,
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +0000170 /* "dirsync" */ MS_DIRSYNC,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000171 /* "async" */ ~MS_SYNCHRONOUS,
172 /* "atime" */ ~MS_NOATIME,
173 /* "noatime" */ MS_NOATIME,
174 /* "diratime" */ ~MS_NODIRATIME,
175 /* "nodiratime" */ MS_NODIRATIME,
Denis Vlasenko580ce2d2008-07-08 02:56:53 +0000176 /* "mand" */ MS_MANDLOCK,
177 /* "nomand" */ ~MS_MANDLOCK,
Bernhard Reutner-Fischerfb5902c2008-08-06 18:14:38 +0000178 /* "relatime" */ MS_RELATIME,
179 /* "norelatime" */ ~MS_RELATIME,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000180 /* "loud" */ ~MS_SILENT,
Eric Andersen9601a1c2006-03-20 18:07:50 +0000181
Rob Landleye3781b72006-08-08 01:39:49 +0000182 // action flags
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200183 /* "union" */ MS_UNION,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000184 /* "bind" */ MS_BIND,
185 /* "move" */ MS_MOVE,
186 /* "shared" */ MS_SHARED,
187 /* "slave" */ MS_SLAVE,
188 /* "private" */ MS_PRIVATE,
189 /* "unbindable" */ MS_UNBINDABLE,
190 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
191 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
192 /* "rprivate" */ MS_SLAVE|MS_RECURSIVE,
193 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
Rob Landleye3781b72006-08-08 01:39:49 +0000194 )
195
196 // Always understood.
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000197 /* "ro" */ MS_RDONLY, // vfs flag
198 /* "rw" */ ~MS_RDONLY, // vfs flag
199 /* "remount" */ MS_REMOUNT // action flag
Eric Andersencc8ed391999-10-05 16:24:54 +0000200};
201
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000202static const char mount_option_str[] =
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000203 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000204 "loop\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000205 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000206 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000207 "defaults\0"
208 // "quiet\0" - do not filter out, vfat wants to see it
209 "noauto\0"
210 "sw\0"
211 "swap\0"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000212 IF_DESKTOP("user\0")
213 IF_DESKTOP("users\0")
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000214 "_netdev\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000215 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000216 IF_FEATURE_MOUNT_FLAGS(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000217 // vfs flags
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000218 "nosuid\0"
219 "suid\0"
220 "dev\0"
221 "nodev\0"
222 "exec\0"
223 "noexec\0"
224 "sync\0"
225 "dirsync\0"
226 "async\0"
227 "atime\0"
228 "noatime\0"
229 "diratime\0"
230 "nodiratime\0"
231 "mand\0"
232 "nomand\0"
233 "relatime\0"
234 "norelatime\0"
235 "loud\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000236
237 // action flags
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200238 "union\0"
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000239 "bind\0"
240 "move\0"
241 "shared\0"
242 "slave\0"
243 "private\0"
244 "unbindable\0"
245 "rshared\0"
246 "rslave\0"
247 "rprivate\0"
248 "runbindable\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000249 )
250
251 // Always understood.
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000252 "ro\0" // vfs flag
253 "rw\0" // vfs flag
254 "remount\0" // action flag
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000255;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000256
Denis Vlasenkof732e962008-02-18 12:07:49 +0000257
258struct globals {
259#if ENABLE_FEATURE_MOUNT_NFS
260 smalluint nfs_mount_version;
261#endif
262#if ENABLE_FEATURE_MOUNT_VERBOSE
263 unsigned verbose;
264#endif
265 llist_t *fslist;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000266 char getmntent_buf[1];
Denis Vlasenkof732e962008-02-18 12:07:49 +0000267
268};
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000269enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
Denis Vlasenkof732e962008-02-18 12:07:49 +0000270#define G (*(struct globals*)&bb_common_bufsiz1)
271#define nfs_mount_version (G.nfs_mount_version)
Denis Vlasenkob4133682008-02-18 13:05:38 +0000272#if ENABLE_FEATURE_MOUNT_VERBOSE
Denis Vlasenkof732e962008-02-18 12:07:49 +0000273#define verbose (G.verbose )
Denis Vlasenkob4133682008-02-18 13:05:38 +0000274#else
275#define verbose 0
276#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000277#define fslist (G.fslist )
278#define getmntent_buf (G.getmntent_buf )
279
280
281#if ENABLE_FEATURE_MOUNT_VERBOSE
282static int verbose_mount(const char *source, const char *target,
283 const char *filesystemtype,
284 unsigned long mountflags, const void *data)
285{
286 int rc;
287
288 errno = 0;
289 rc = mount(source, target, filesystemtype, mountflags, data);
290 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000291 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000292 source, target, filesystemtype,
293 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000294 return rc;
295}
296#else
297#define verbose_mount(...) mount(__VA_ARGS__)
298#endif
299
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000300// Append mount options to string
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000301static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000302{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000303 if (*oldopts && **oldopts) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000304 // Do not insert options which are already there
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000305 while (newopts[0]) {
306 char *p;
307 int len = strlen(newopts);
308 p = strchr(newopts, ',');
309 if (p) len = p - newopts;
310 p = *oldopts;
311 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000312 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000313 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000314 goto skip;
315 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000316 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000317 p++;
318 }
319 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
320 free(*oldopts);
321 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000322 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000323 newopts += len;
324 while (newopts[0] == ',') newopts++;
325 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000326 } else {
327 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000328 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000329 }
330}
331
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000332// Use the mount_options list to parse options into flags.
333// Also return list of unrecognized options if unrecognized != NULL
Denis Vlasenkob4133682008-02-18 13:05:38 +0000334static long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000335{
Denis Vlasenkob4133682008-02-18 13:05:38 +0000336 long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000337
Rob Landley6a6798b2005-08-10 20:35:54 +0000338 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000339 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000340 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000341 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000342 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000343
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000344 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000345
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000346// FIXME: use hasmntopt()
Rob Landley6a6798b2005-08-10 20:35:54 +0000347 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000348 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000349 if (!strcasecmp(option_str, options)) {
350 long fl = mount_options[i];
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000351 if (fl < 0) flags &= fl;
Rob Landleydc0955b2006-03-14 18:16:25 +0000352 else flags |= fl;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000353 break;
354 }
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000355 option_str += strlen(option_str) + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000356 }
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000357 // If unrecognized not NULL, append unrecognized mount options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000358 if (unrecognized && i == ARRAY_SIZE(mount_options)) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000359 // Add it to strflags, to pass on to kernel
Rob Landleydc0955b2006-03-14 18:16:25 +0000360 i = *unrecognized ? strlen(*unrecognized) : 0;
Denis Vlasenkodeeed592008-07-08 05:14:36 +0000361 *unrecognized = xrealloc(*unrecognized, i + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000362
Rob Landley6a6798b2005-08-10 20:35:54 +0000363 // Comma separated if it's not the first one
Rob Landleydc0955b2006-03-14 18:16:25 +0000364 if (i) (*unrecognized)[i++] = ',';
365 strcpy((*unrecognized)+i, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000366 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000367
Denis Vlasenko2535f122007-09-15 13:28:30 +0000368 if (!comma)
369 break;
370 // Advance to next option
371 *comma = ',';
372 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000373 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000374
Rob Landleydc0955b2006-03-14 18:16:25 +0000375 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000376}
377
Rob Landleydc0955b2006-03-14 18:16:25 +0000378// Return a list of all block device backed filesystems
Rob Landleydc0955b2006-03-14 18:16:25 +0000379static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000380{
Denis Vlasenko87468852007-04-13 23:22:00 +0000381 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000382 "/etc/filesystems",
383 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000384 };
385 char *fs, *buf;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200386 llist_t *list = NULL;
Rob Landleydc0955b2006-03-14 18:16:25 +0000387 int i;
388 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000389
Denis Vlasenko87468852007-04-13 23:22:00 +0000390 for (i = 0; i < 2; i++) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000391 f = fopen_for_read(filesystems[i]);
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000392 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000393
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000394 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200395 if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
Denis Vlasenko372686b2006-10-12 22:42:33 +0000396 continue;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000397 fs = skip_whitespace(buf);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200398 if (*fs == '#' || *fs == '*' || !*fs)
399 continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000400
Denis Vlasenko372686b2006-10-12 22:42:33 +0000401 llist_add_to_end(&list, xstrdup(fs));
402 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000403 }
404 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
405 }
406
407 return list;
408}
409
Rob Landleydc0955b2006-03-14 18:16:25 +0000410#if ENABLE_FEATURE_CLEAN_UP
411static void delete_block_backed_filesystems(void)
412{
Rob Landleya6b5b602006-05-08 19:03:07 +0000413 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000414}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000415#else
416void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000417#endif
418
Rob Landleydc0955b2006-03-14 18:16:25 +0000419// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000420// NB: mp->xxx fields may be trashed on exit
Denis Vlasenkob4133682008-02-18 13:05:38 +0000421static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000422{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000423 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000424
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200425 if (FAKE_IT) {
Denis Vlasenkob4133682008-02-18 13:05:38 +0000426 if (verbose >= 2)
427 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
428 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
429 vfsflags, filteropts);
430 goto mtab;
431 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000432
Rob Landleydc0955b2006-03-14 18:16:25 +0000433 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000434 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000435 errno = 0;
436 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000437 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000438
439 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000440 // helper program mount.<mnt_type>
Denys Vlasenkoba986032009-09-15 23:00:09 +0200441 if (HELPERS_ALLOWED && rc && mp->mnt_type) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200442 char *args[8];
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000443 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000444 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000445 rc = 1;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200446 if (FAKE_IT)
447 args[rc++] = (char *)"-f";
448 if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
449 args[rc++] = (char *)"-n";
Denis Vlasenko5c329932009-04-12 12:16:21 +0000450 args[rc++] = mp->mnt_fsname;
451 args[rc++] = mp->mnt_dir;
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000452 if (filteropts) {
453 args[rc++] = (char *)"-o";
454 args[rc++] = filteropts;
455 }
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000456 args[rc] = NULL;
457 rc = wait4pid(spawn(args));
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000458 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000459 if (!rc)
460 break;
461 errno = errno_save;
462 }
463
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000464 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000465 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000466 if (!(vfsflags & MS_SILENT))
467 bb_error_msg("%s is write-protected, mounting read-only",
468 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000469 vfsflags |= MS_RDONLY;
470 }
471
Rob Landleydc0955b2006-03-14 18:16:25 +0000472 // Abort entirely if permission denied.
473
474 if (rc && errno == EPERM)
475 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
476
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000477 // If the mount was successful, and we're maintaining an old-style
478 // mtab file by hand, add the new entry to it now.
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000479 mtab:
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200480 if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000481 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000482 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000483 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000484 int i;
485
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000486 if (!mountTable) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000487 bb_error_msg("no %s", bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000488 goto ret;
489 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000490
491 // Add vfs string flags
492
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000493 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
494 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
495 append_mount_options(&(mp->mnt_opts), option_str);
496 option_str += strlen(option_str) + 1;
497 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000498
499 // Remove trailing / (if any) from directory we mounted on
500
Denis Vlasenko727ef942006-09-14 13:19:19 +0000501 i = strlen(mp->mnt_dir) - 1;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000502 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000503
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000504 // Convert to canonical pathnames as needed
Denis Vlasenko727ef942006-09-14 13:19:19 +0000505
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000506 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000507 fsname = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000508 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000509 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000510 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000511 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000512 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000513
514 // Write and close.
515
Denis Vlasenko727ef942006-09-14 13:19:19 +0000516 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000517 endmntent(mountTable);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000518 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000519 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000520 free(fsname);
521 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000522 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000523 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000524 return rc;
525}
526
Denis Vlasenko25098f72006-09-14 15:46:33 +0000527#if ENABLE_FEATURE_MOUNT_NFS
528
529/*
530 * Linux NFS mount
531 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
532 *
533 * Licensed under GPLv2, see file LICENSE in this tarball for details.
534 *
535 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
536 * numbers to be specified on the command line.
537 *
538 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
539 * Omit the call to connect() for Linux version 1.3.11 or later.
540 *
541 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
542 * Implemented the "bg", "fg" and "retry" mount options for NFS.
543 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000544 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000545 * - added Native Language Support
546 *
547 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
548 * plus NFSv3 stuff.
549 */
550
Denis Vlasenko25098f72006-09-14 15:46:33 +0000551/* This is just a warning of a common mistake. Possibly this should be a
552 * uclibc faq entry rather than in busybox... */
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000553#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000554#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
555#endif
556
557#define MOUNTPORT 635
558#define MNTPATHLEN 1024
559#define MNTNAMLEN 255
560#define FHSIZE 32
561#define FHSIZE3 64
562
563typedef char fhandle[FHSIZE];
564
565typedef struct {
566 unsigned int fhandle3_len;
567 char *fhandle3_val;
568} fhandle3;
569
570enum mountstat3 {
571 MNT_OK = 0,
572 MNT3ERR_PERM = 1,
573 MNT3ERR_NOENT = 2,
574 MNT3ERR_IO = 5,
575 MNT3ERR_ACCES = 13,
576 MNT3ERR_NOTDIR = 20,
577 MNT3ERR_INVAL = 22,
578 MNT3ERR_NAMETOOLONG = 63,
579 MNT3ERR_NOTSUPP = 10004,
580 MNT3ERR_SERVERFAULT = 10006,
581};
582typedef enum mountstat3 mountstat3;
583
584struct fhstatus {
585 unsigned int fhs_status;
586 union {
587 fhandle fhs_fhandle;
588 } fhstatus_u;
589};
590typedef struct fhstatus fhstatus;
591
592struct mountres3_ok {
593 fhandle3 fhandle;
594 struct {
595 unsigned int auth_flavours_len;
596 char *auth_flavours_val;
597 } auth_flavours;
598};
599typedef struct mountres3_ok mountres3_ok;
600
601struct mountres3 {
602 mountstat3 fhs_status;
603 union {
604 mountres3_ok mountinfo;
605 } mountres3_u;
606};
607typedef struct mountres3 mountres3;
608
609typedef char *dirpath;
610
611typedef char *name;
612
613typedef struct mountbody *mountlist;
614
615struct mountbody {
616 name ml_hostname;
617 dirpath ml_directory;
618 mountlist ml_next;
619};
620typedef struct mountbody mountbody;
621
622typedef struct groupnode *groups;
623
624struct groupnode {
625 name gr_name;
626 groups gr_next;
627};
628typedef struct groupnode groupnode;
629
630typedef struct exportnode *exports;
631
632struct exportnode {
633 dirpath ex_dir;
634 groups ex_groups;
635 exports ex_next;
636};
637typedef struct exportnode exportnode;
638
639struct ppathcnf {
640 int pc_link_max;
641 short pc_max_canon;
642 short pc_max_input;
643 short pc_name_max;
644 short pc_path_max;
645 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000646 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000647 char pc_xxx;
648 short pc_mask[2];
649};
650typedef struct ppathcnf ppathcnf;
651
652#define MOUNTPROG 100005
653#define MOUNTVERS 1
654
655#define MOUNTPROC_NULL 0
656#define MOUNTPROC_MNT 1
657#define MOUNTPROC_DUMP 2
658#define MOUNTPROC_UMNT 3
659#define MOUNTPROC_UMNTALL 4
660#define MOUNTPROC_EXPORT 5
661#define MOUNTPROC_EXPORTALL 6
662
663#define MOUNTVERS_POSIX 2
664
665#define MOUNTPROC_PATHCONF 7
666
667#define MOUNT_V3 3
668
669#define MOUNTPROC3_NULL 0
670#define MOUNTPROC3_MNT 1
671#define MOUNTPROC3_DUMP 2
672#define MOUNTPROC3_UMNT 3
673#define MOUNTPROC3_UMNTALL 4
674#define MOUNTPROC3_EXPORT 5
675
676enum {
677#ifndef NFS_FHSIZE
678 NFS_FHSIZE = 32,
679#endif
680#ifndef NFS_PORT
681 NFS_PORT = 2049
682#endif
683};
684
Denis Vlasenko25098f72006-09-14 15:46:33 +0000685/*
686 * We want to be able to compile mount on old kernels in such a way
687 * that the binary will work well on more recent kernels.
688 * Thus, if necessary we teach nfsmount.c the structure of new fields
689 * that will come later.
690 *
691 * Moreover, the new kernel includes conflict with glibc includes
692 * so it is easiest to ignore the kernel altogether (at compile time).
693 */
694
695struct nfs2_fh {
696 char data[32];
697};
698struct nfs3_fh {
699 unsigned short size;
700 unsigned char data[64];
701};
702
703struct nfs_mount_data {
704 int version; /* 1 */
705 int fd; /* 1 */
706 struct nfs2_fh old_root; /* 1 */
707 int flags; /* 1 */
708 int rsize; /* 1 */
709 int wsize; /* 1 */
710 int timeo; /* 1 */
711 int retrans; /* 1 */
712 int acregmin; /* 1 */
713 int acregmax; /* 1 */
714 int acdirmin; /* 1 */
715 int acdirmax; /* 1 */
716 struct sockaddr_in addr; /* 1 */
717 char hostname[256]; /* 1 */
718 int namlen; /* 2 */
719 unsigned int bsize; /* 3 */
720 struct nfs3_fh root; /* 4 */
721};
722
723/* bits in the flags field */
724enum {
725 NFS_MOUNT_SOFT = 0x0001, /* 1 */
726 NFS_MOUNT_INTR = 0x0002, /* 1 */
727 NFS_MOUNT_SECURE = 0x0004, /* 1 */
728 NFS_MOUNT_POSIX = 0x0008, /* 1 */
729 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
730 NFS_MOUNT_NOAC = 0x0020, /* 1 */
731 NFS_MOUNT_TCP = 0x0040, /* 2 */
732 NFS_MOUNT_VER3 = 0x0080, /* 3 */
733 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000734 NFS_MOUNT_NONLM = 0x0200, /* 3 */
735 NFS_MOUNT_NORDIRPLUS = 0x4000
Denis Vlasenko25098f72006-09-14 15:46:33 +0000736};
737
738
739/*
740 * We need to translate between nfs status return values and
741 * the local errno values which may not be the same.
742 *
743 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
744 * "after #include <errno.h> the symbol errno is reserved for any use,
745 * it cannot even be used as a struct tag or field name".
746 */
747
748#ifndef EDQUOT
749#define EDQUOT ENOSPC
750#endif
751
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000752/* Convert each NFSERR_BLAH into EBLAH */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000753static const struct {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000754 short stat;
755 short errnum;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000756} nfs_errtbl[] = {
757 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
758 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
759 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
760 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
761};
Denis Vlasenko25098f72006-09-14 15:46:33 +0000762static char *nfs_strerror(int status)
763{
764 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000765
766 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
767 if (nfs_errtbl[i].stat == status)
768 return strerror(nfs_errtbl[i].errnum);
769 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000770 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000771}
772
773static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
774{
775 if (!xdr_opaque(xdrs, objp, FHSIZE))
776 return FALSE;
777 return TRUE;
778}
779
780static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
781{
782 if (!xdr_u_int(xdrs, &objp->fhs_status))
783 return FALSE;
784 switch (objp->fhs_status) {
785 case 0:
786 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
787 return FALSE;
788 break;
789 default:
790 break;
791 }
792 return TRUE;
793}
794
795static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
796{
797 if (!xdr_string(xdrs, objp, MNTPATHLEN))
798 return FALSE;
799 return TRUE;
800}
801
802static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
803{
804 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
805 return FALSE;
806 return TRUE;
807}
808
809static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
810{
811 if (!xdr_fhandle3(xdrs, &objp->fhandle))
812 return FALSE;
813 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
Denys Vlasenko810b7162009-05-13 23:48:59 +0200814 sizeof(int), (xdrproc_t) xdr_int))
Denis Vlasenko25098f72006-09-14 15:46:33 +0000815 return FALSE;
816 return TRUE;
817}
818
819static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
820{
821 if (!xdr_enum(xdrs, (enum_t *) objp))
822 return FALSE;
823 return TRUE;
824}
825
826static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
827{
828 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
829 return FALSE;
830 switch (objp->fhs_status) {
831 case MNT_OK:
832 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
833 return FALSE;
834 break;
835 default:
836 break;
837 }
838 return TRUE;
839}
840
841#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
842
Denis Vlasenko25098f72006-09-14 15:46:33 +0000843/*
844 * Unfortunately, the kernel prints annoying console messages
845 * in case of an unexpected nfs mount version (instead of
846 * just returning some error). Therefore we'll have to try
847 * and figure out what version the kernel expects.
848 *
849 * Variables:
850 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
851 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
852 * nfs_mount_version: version this source and running kernel can handle
853 */
854static void
855find_kernel_nfs_mount_version(void)
856{
Denis Vlasenkob9256052007-09-28 10:29:17 +0000857 int kernel_version;
858
859 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000860 return;
861
862 nfs_mount_version = 4; /* default */
863
864 kernel_version = get_linux_version_code();
865 if (kernel_version) {
866 if (kernel_version < KERNEL_VERSION(2,1,32))
867 nfs_mount_version = 1;
868 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
869 (kernel_version >= KERNEL_VERSION(2,3,0) &&
870 kernel_version < KERNEL_VERSION(2,3,99)))
871 nfs_mount_version = 3;
872 /* else v4 since 2.3.99pre4 */
873 }
874}
875
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +0000876static void
Denis Vlasenkob9256052007-09-28 10:29:17 +0000877get_mountport(struct pmap *pm_mnt,
878 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +0000879 long unsigned prog,
880 long unsigned version,
881 long unsigned proto,
882 long unsigned port)
883{
884 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000885
886 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +0000887/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
888 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000889 pmap = pmap_getmaps(server_addr);
890
891 if (version > MAX_NFSPROT)
892 version = MAX_NFSPROT;
893 if (!prog)
894 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000895 pm_mnt->pm_prog = prog;
896 pm_mnt->pm_vers = version;
897 pm_mnt->pm_prot = proto;
898 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000899
Denis Vlasenko25098f72006-09-14 15:46:33 +0000900 while (pmap) {
901 if (pmap->pml_map.pm_prog != prog)
902 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000903 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000904 goto next;
905 if (version > 2 && pmap->pml_map.pm_vers != version)
906 goto next;
907 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
908 goto next;
909 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
Denis Vlasenkob9256052007-09-28 10:29:17 +0000910 (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto) ||
Denis Vlasenko25098f72006-09-14 15:46:33 +0000911 (port && pmap->pml_map.pm_port != port))
912 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000913 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
914 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +0000915 pmap = pmap->pml_next;
916 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000917 if (!pm_mnt->pm_vers)
918 pm_mnt->pm_vers = MOUNTVERS;
919 if (!pm_mnt->pm_port)
920 pm_mnt->pm_port = MOUNTPORT;
921 if (!pm_mnt->pm_prot)
922 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000923}
924
Denis Vlasenkof0000652007-09-04 18:30:26 +0000925#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +0000926static int daemonize(void)
927{
Denis Vlasenko25098f72006-09-14 15:46:33 +0000928 int pid = fork();
929 if (pid < 0) /* error */
930 return -errno;
931 if (pid > 0) /* parent */
932 return 0;
933 /* child */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000934 close(0);
935 xopen(bb_dev_null, O_RDWR);
936 xdup2(0, 1);
937 xdup2(0, 2);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000938 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +0000939 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000940 logmode = LOGMODE_SYSLOG;
941 return 1;
942}
Denis Vlasenkof0000652007-09-04 18:30:26 +0000943#else
944static inline int daemonize(void) { return -ENOSYS; }
945#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000946
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000947/* TODO */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000948static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000949{
950 return 0;
951}
952
953/* RPC strerror analogs are terminally idiotic:
954 * *mandatory* prefix and \n at end.
955 * This hopefully helps. Usage:
956 * error_msg_rpc(clnt_*error*(" ")) */
957static void error_msg_rpc(const char *msg)
958{
Denis Vlasenko23514fe2006-09-19 14:07:52 +0000959 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000960 while (msg[0] == ' ' || msg[0] == ':') msg++;
961 len = strlen(msg);
962 while (len && msg[len-1] == '\n') len--;
963 bb_error_msg("%.*s", len, msg);
964}
965
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000966/* NB: mp->xxx fields may be trashed on exit */
Denys Vlasenko810b7162009-05-13 23:48:59 +0200967static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000968{
969 CLIENT *mclient;
970 char *hostname;
971 char *pathname;
972 char *mounthost;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +0200973 /* prior to 2.6.23, kernel took NFS options in a form of this struct
974 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
975 * then data pointer is interpreted as a string. */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000976 struct nfs_mount_data data;
977 char *opt;
978 struct hostent *hp;
979 struct sockaddr_in server_addr;
980 struct sockaddr_in mount_server_addr;
981 int msock, fsock;
982 union {
983 struct fhstatus nfsv2;
984 struct mountres3 nfsv3;
985 } status;
986 int daemonized;
987 char *s;
988 int port;
989 int mountport;
990 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +0000991#if BB_MMU
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000992 smallint bg = 0;
Denis Vlasenkof0000652007-09-04 18:30:26 +0000993#else
994 enum { bg = 0 };
995#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000996 int retry;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000997 int mountprog;
998 int mountvers;
999 int nfsprog;
1000 int nfsvers;
1001 int retval;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001002 /* these all are one-bit really. gcc 4.3.1 likes this combination: */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001003 smallint tcp;
1004 smallint soft;
1005 int intr;
1006 int posix;
1007 int nocto;
1008 int noac;
1009 int nordirplus;
1010 int nolock;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001011
1012 find_kernel_nfs_mount_version();
1013
1014 daemonized = 0;
1015 mounthost = NULL;
1016 retval = ETIMEDOUT;
1017 msock = fsock = -1;
1018 mclient = NULL;
1019
1020 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1021
1022 filteropts = xstrdup(filteropts); /* going to trash it later... */
1023
1024 hostname = xstrdup(mp->mnt_fsname);
1025 /* mount_main() guarantees that ':' is there */
1026 s = strchr(hostname, ':');
1027 pathname = s + 1;
1028 *s = '\0';
1029 /* Ignore all but first hostname in replicated mounts
1030 until they can be fully supported. (mack@sgi.com) */
1031 s = strchr(hostname, ',');
1032 if (s) {
1033 *s = '\0';
1034 bb_error_msg("warning: multiple hostnames not supported");
1035 }
1036
1037 server_addr.sin_family = AF_INET;
1038 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1039 hp = gethostbyname(hostname);
1040 if (hp == NULL) {
1041 bb_herror_msg("%s", hostname);
1042 goto fail;
1043 }
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001044 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001045 bb_error_msg("got bad hp->h_length");
1046 hp->h_length = sizeof(struct in_addr);
1047 }
1048 memcpy(&server_addr.sin_addr,
1049 hp->h_addr, hp->h_length);
1050 }
1051
1052 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1053
1054 /* add IP address to mtab options for use when unmounting */
1055
1056 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1057 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1058 } else {
1059 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1060 mp->mnt_opts[0] ? "," : "",
1061 inet_ntoa(server_addr.sin_addr));
1062 free(mp->mnt_opts);
1063 mp->mnt_opts = tmp;
1064 }
1065
1066 /* Set default options.
1067 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1068 * let the kernel decide.
1069 * timeo is filled in after we know whether it'll be TCP or UDP. */
1070 memset(&data, 0, sizeof(data));
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001071 data.retrans = 3;
1072 data.acregmin = 3;
1073 data.acregmax = 60;
1074 data.acdirmin = 30;
1075 data.acdirmax = 60;
1076 data.namlen = NAME_MAX;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001077
Denis Vlasenko25098f72006-09-14 15:46:33 +00001078 soft = 0;
1079 intr = 0;
1080 posix = 0;
1081 nocto = 0;
1082 nolock = 0;
1083 noac = 0;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001084 nordirplus = 0;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001085 retry = 10000; /* 10000 minutes ~ 1 week */
1086 tcp = 0;
1087
1088 mountprog = MOUNTPROG;
1089 mountvers = 0;
1090 port = 0;
1091 mountport = 0;
1092 nfsprog = 100003;
1093 nfsvers = 0;
1094
1095 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001096 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001097 char *opteq = strchr(opt, '=');
1098 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001099 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001100 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001101 /* 0 */ "rsize\0"
1102 /* 1 */ "wsize\0"
1103 /* 2 */ "timeo\0"
1104 /* 3 */ "retrans\0"
1105 /* 4 */ "acregmin\0"
1106 /* 5 */ "acregmax\0"
1107 /* 6 */ "acdirmin\0"
1108 /* 7 */ "acdirmax\0"
1109 /* 8 */ "actimeo\0"
1110 /* 9 */ "retry\0"
1111 /* 10 */ "port\0"
1112 /* 11 */ "mountport\0"
1113 /* 12 */ "mounthost\0"
1114 /* 13 */ "mountprog\0"
1115 /* 14 */ "mountvers\0"
1116 /* 15 */ "nfsprog\0"
1117 /* 16 */ "nfsvers\0"
1118 /* 17 */ "vers\0"
1119 /* 18 */ "proto\0"
1120 /* 19 */ "namlen\0"
1121 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001122
1123 *opteq++ = '\0';
1124 idx = index_in_strings(options, opt);
1125 switch (idx) {
1126 case 12: // "mounthost"
1127 mounthost = xstrndup(opteq,
1128 strcspn(opteq, " \t\n\r,"));
1129 continue;
1130 case 18: // "proto"
1131 if (!strncmp(opteq, "tcp", 3))
1132 tcp = 1;
1133 else if (!strncmp(opteq, "udp", 3))
1134 tcp = 0;
1135 else
1136 bb_error_msg("warning: unrecognized proto= option");
1137 continue;
1138 case 20: // "addr" - ignore
1139 continue;
1140 }
1141
1142 val = xatoi_u(opteq);
1143 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001144 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001145 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001146 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001147 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001148 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001149 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001150 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001151 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001152 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001153 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001154 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001155 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001156 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001157 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001158 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001159 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001160 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001161 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001162 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001163 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001164 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001165 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001166 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001167 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001168 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001169 data.acregmin = val;
1170 data.acregmax = val;
1171 data.acdirmin = val;
1172 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001173 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001174 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001175 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001176 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001177 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001178 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001179 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001180 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001181 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001182 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001183 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001184 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001185 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001186 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001187 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001188 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001189 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001190 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001191 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001192 case 16: // "nfsvers"
1193 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001194 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001195 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001196 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001197 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001198 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001199 //else
1200 // bb_error_msg("warning: option namlen is not supported\n");
1201 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001202 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001203 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1204 goto fail;
1205 }
1206 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001207 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001208 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001209 "bg\0"
1210 "fg\0"
1211 "soft\0"
1212 "hard\0"
1213 "intr\0"
1214 "posix\0"
1215 "cto\0"
1216 "ac\0"
1217 "tcp\0"
1218 "udp\0"
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001219 "lock\0"
1220 "rdirplus\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001221 int val = 1;
1222 if (!strncmp(opt, "no", 2)) {
1223 val = 0;
1224 opt += 2;
1225 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001226 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001227 case 0: // "bg"
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 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001233#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001234 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001235#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001236 break;
1237 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001238 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001239 break;
1240 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001241 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001242 break;
1243 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001244 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001245 break;
1246 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001247 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001248 break;
1249 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001250 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001251 break;
1252 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001253 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001254 break;
1255 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001256 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001257 break;
1258 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001259 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001260 break;
1261 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001262 if (nfs_mount_version >= 3)
1263 nolock = !val;
1264 else
1265 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001266 break;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001267 case 11: //rdirplus
1268 nordirplus = !val;
1269 break;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001270 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001271 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1272 goto fail;
1273 }
1274 }
1275 }
1276 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1277
1278 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1279 | (intr ? NFS_MOUNT_INTR : 0)
1280 | (posix ? NFS_MOUNT_POSIX : 0)
1281 | (nocto ? NFS_MOUNT_NOCTO : 0)
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001282 | (noac ? NFS_MOUNT_NOAC : 0)
1283 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001284 if (nfs_mount_version >= 2)
1285 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1286 if (nfs_mount_version >= 3)
1287 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1288 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1289 bb_error_msg("NFSv%d not supported", nfsvers);
1290 goto fail;
1291 }
1292 if (nfsvers && !mountvers)
1293 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1294 if (nfsvers && nfsvers < mountvers) {
1295 mountvers = nfsvers;
1296 }
1297
1298 /* Adjust options if none specified */
1299 if (!data.timeo)
1300 data.timeo = tcp ? 70 : 7;
1301
Denis Vlasenko25098f72006-09-14 15:46:33 +00001302 data.version = nfs_mount_version;
1303
1304 if (vfsflags & MS_REMOUNT)
1305 goto do_mount;
1306
1307 /*
1308 * If the previous mount operation on the same host was
1309 * backgrounded, and the "bg" for this mount is also set,
1310 * give up immediately, to avoid the initial timeout.
1311 */
1312 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001313 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001314 if (daemonized <= 0) { /* parent or error */
1315 retval = -daemonized;
1316 goto ret;
1317 }
1318 }
1319
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001320 /* Create mount daemon client */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001321 /* See if the nfs host = mount host. */
1322 if (mounthost) {
1323 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1324 mount_server_addr.sin_family = AF_INET;
1325 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1326 } else {
1327 hp = gethostbyname(mounthost);
1328 if (hp == NULL) {
1329 bb_herror_msg("%s", mounthost);
1330 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001331 }
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001332 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
1333 bb_error_msg("got bad hp->h_length");
1334 hp->h_length = sizeof(struct in_addr);
1335 }
1336 mount_server_addr.sin_family = AF_INET;
1337 memcpy(&mount_server_addr.sin_addr,
1338 hp->h_addr, hp->h_length);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001339 }
1340 }
1341
1342 /*
1343 * The following loop implements the mount retries. When the mount
1344 * times out, and the "bg" option is set, we background ourself
1345 * and continue trying.
1346 *
1347 * The case where the mount point is not present and the "bg"
1348 * option is set, is treated as a timeout. This is done to
1349 * support nested mounts.
1350 *
1351 * The "retry" count specified by the user is the number of
1352 * minutes to retry before giving up.
1353 */
1354 {
1355 struct timeval total_timeout;
1356 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001357 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001358 time_t t;
1359 time_t prevt;
1360 time_t timeout;
1361
1362 retry_timeout.tv_sec = 3;
1363 retry_timeout.tv_usec = 0;
1364 total_timeout.tv_sec = 20;
1365 total_timeout.tv_usec = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001366/* FIXME: use monotonic()? */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001367 timeout = time(NULL) + 60 * retry;
1368 prevt = 0;
1369 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001370 retry:
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001371 /* Be careful not to use too many CPU cycles */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001372 if (t - prevt < 30)
1373 sleep(30);
1374
Denis Vlasenkob9256052007-09-28 10:29:17 +00001375 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001376 mountprog,
1377 mountvers,
1378 proto,
1379 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001380 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001381
1382 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001383 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001384 msock = RPC_ANYSOCK;
1385
Denis Vlasenkob9256052007-09-28 10:29:17 +00001386 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001387 case IPPROTO_UDP:
1388 mclient = clntudp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001389 pm_mnt.pm_prog,
1390 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001391 retry_timeout,
1392 &msock);
1393 if (mclient)
1394 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001395 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001396 msock = RPC_ANYSOCK;
1397 case IPPROTO_TCP:
1398 mclient = clnttcp_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 &msock, 0, 0);
1402 break;
1403 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001404 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001405 }
1406 if (!mclient) {
1407 if (!daemonized && prevt == 0)
1408 error_msg_rpc(clnt_spcreateerror(" "));
1409 } else {
1410 enum clnt_stat clnt_stat;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001411
1412 /* Try to mount hostname:pathname */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001413 mclient->cl_auth = authunix_create_default();
1414
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001415 /* Make pointers in xdr_mountres3 NULL so
Denis Vlasenko25098f72006-09-14 15:46:33 +00001416 * that xdr_array allocates memory for us
1417 */
1418 memset(&status, 0, sizeof(status));
1419
Denis Vlasenkob9256052007-09-28 10:29:17 +00001420 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001421 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1422 (xdrproc_t) xdr_dirpath,
1423 (caddr_t) &pathname,
1424 (xdrproc_t) xdr_mountres3,
1425 (caddr_t) &status,
1426 total_timeout);
1427 else
1428 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1429 (xdrproc_t) xdr_dirpath,
1430 (caddr_t) &pathname,
1431 (xdrproc_t) xdr_fhstatus,
1432 (caddr_t) &status,
1433 total_timeout);
1434
1435 if (clnt_stat == RPC_SUCCESS)
1436 goto prepare_kernel_data; /* we're done */
1437 if (errno != ECONNREFUSED) {
1438 error_msg_rpc(clnt_sperror(mclient, " "));
1439 goto fail; /* don't retry */
1440 }
1441 /* Connection refused */
1442 if (!daemonized && prevt == 0) /* print just once */
1443 error_msg_rpc(clnt_sperror(mclient, " "));
1444 auth_destroy(mclient->cl_auth);
1445 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001446 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001447 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001448 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001449 }
1450
1451 /* Timeout. We are going to retry... maybe */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001452 if (!bg)
1453 goto fail;
1454 if (!daemonized) {
1455 daemonized = daemonize();
1456 if (daemonized <= 0) { /* parent or error */
1457 retval = -daemonized;
1458 goto ret;
1459 }
1460 }
1461 prevt = t;
1462 t = time(NULL);
1463 if (t >= timeout)
1464 /* TODO error message */
1465 goto fail;
1466
1467 goto retry;
1468 }
1469
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001470 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001471
1472 if (nfsvers == 2) {
1473 if (status.nfsv2.fhs_status != 0) {
1474 bb_error_msg("%s:%s failed, reason given by server: %s",
1475 hostname, pathname,
1476 nfs_strerror(status.nfsv2.fhs_status));
1477 goto fail;
1478 }
1479 memcpy(data.root.data,
1480 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1481 NFS_FHSIZE);
1482 data.root.size = NFS_FHSIZE;
1483 memcpy(data.old_root.data,
1484 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1485 NFS_FHSIZE);
1486 } else {
1487 fhandle3 *my_fhandle;
1488 if (status.nfsv3.fhs_status != 0) {
1489 bb_error_msg("%s:%s failed, reason given by server: %s",
1490 hostname, pathname,
1491 nfs_strerror(status.nfsv3.fhs_status));
1492 goto fail;
1493 }
1494 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1495 memset(data.old_root.data, 0, NFS_FHSIZE);
1496 memset(&data.root, 0, sizeof(data.root));
1497 data.root.size = my_fhandle->fhandle3_len;
1498 memcpy(data.root.data,
1499 (char *) my_fhandle->fhandle3_val,
1500 my_fhandle->fhandle3_len);
1501
1502 data.flags |= NFS_MOUNT_VER3;
1503 }
1504
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001505 /* Create nfs socket for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001506 if (tcp) {
1507 if (nfs_mount_version < 3) {
1508 bb_error_msg("NFS over TCP is not supported");
1509 goto fail;
1510 }
1511 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1512 } else
1513 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1514 if (fsock < 0) {
1515 bb_perror_msg("nfs socket");
1516 goto fail;
1517 }
1518 if (bindresvport(fsock, 0) < 0) {
1519 bb_perror_msg("nfs bindresvport");
1520 goto fail;
1521 }
1522 if (port == 0) {
1523 server_addr.sin_port = PMAPPORT;
1524 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1525 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1526 if (port == 0)
1527 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001528 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001529 server_addr.sin_port = htons(port);
1530
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001531 /* Prepare data structure for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001532 data.fd = fsock;
1533 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1534 strncpy(data.hostname, hostname, sizeof(data.hostname));
1535
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001536 /* Clean up */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001537 auth_destroy(mclient->cl_auth);
1538 clnt_destroy(mclient);
1539 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001540 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001541
1542 if (bg) {
1543 /* We must wait until mount directory is available */
1544 struct stat statbuf;
1545 int delay = 1;
1546 while (stat(mp->mnt_dir, &statbuf) == -1) {
1547 if (!daemonized) {
1548 daemonized = daemonize();
1549 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001550/* FIXME: parent doesn't close fsock - ??! */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001551 retval = -daemonized;
1552 goto ret;
1553 }
1554 }
1555 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1556 delay *= 2;
1557 if (delay > 30)
1558 delay = 30;
1559 }
1560 }
1561
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001562 /* Perform actual mount */
1563 do_mount:
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001564 mp->mnt_type = (char*)"nfs";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001565 retval = mount_it_now(mp, vfsflags, (char*)&data);
1566 goto ret;
1567
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001568 /* Abort */
1569 fail:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001570 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001571 if (mclient) {
1572 auth_destroy(mclient->cl_auth);
1573 clnt_destroy(mclient);
1574 }
1575 close(msock);
1576 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001577 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001578 close(fsock);
1579
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001580 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001581 free(hostname);
1582 free(mounthost);
1583 free(filteropts);
1584 return retval;
1585}
1586
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001587#else // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001588
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001589// Never called. Call should be optimized out.
Denis Vlasenkob4133682008-02-18 13:05:38 +00001590int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001591
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001592#endif // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001593
1594// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1595// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001596// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001597static int singlemount(struct mntent *mp, int ignore_busy)
1598{
Denis Vlasenkob4133682008-02-18 13:05:38 +00001599 int rc = -1;
1600 long vfsflags;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001601 char *loopFile = NULL, *filteropts = NULL;
1602 llist_t *fl = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001603 struct stat st;
1604
1605 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1606
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001607 // Treat fstype "auto" as unspecified
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001608 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1609 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001610
Denis Vlasenko2535f122007-09-15 13:28:30 +00001611 // Might this be a virtual filesystem?
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001612 if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1613 char *args[35];
1614 char *s;
1615 int n;
1616 // fsname: "cmd#arg1#arg2..."
1617 // WARNING: allows execution of arbitrary commands!
1618 // Try "mount 'sh#-c#sh' bogus_dir".
1619 // It is safe ONLY because non-root
1620 // cannot use two-argument mount command
1621 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1622 // "mount: can't find sh#-c#sh in /etc/fstab"
1623 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1624
1625 s = mp->mnt_fsname;
1626 n = 0;
1627 args[n++] = s;
1628 while (*s && n < 35 - 2) {
1629 if (*s++ == '#' && *s != '#') {
1630 s[-1] = '\0';
1631 args[n++] = s;
Denis Vlasenko2535f122007-09-15 13:28:30 +00001632 }
1633 }
Denis Vlasenko2535f122007-09-15 13:28:30 +00001634 args[n++] = mp->mnt_dir;
1635 args[n] = NULL;
1636 rc = wait4pid(xspawn(args));
1637 goto report_error;
1638 }
1639
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001640 // Might this be an CIFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001641 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001642 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1643 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1644 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001645 ) {
Denis Vlasenko5c329932009-04-12 12:16:21 +00001646#if 0 /* reported to break things */
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001647 len_and_sockaddr *lsa;
1648 char *ip, *dotted;
1649 char *s;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001650
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001651 // Replace '/' with '\' and verify that unc points to "//server/share".
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001652 for (s = mp->mnt_fsname; *s; ++s)
1653 if (*s == '/') *s = '\\';
1654
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001655 // Get server IP
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001656 s = strrchr(mp->mnt_fsname, '\\');
Denis Vlasenko5c329932009-04-12 12:16:21 +00001657 if (s <= mp->mnt_fsname+1)
1658 goto report_error;
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001659 *s = '\0';
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001660 lsa = host2sockaddr(mp->mnt_fsname+2, 0);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001661 *s = '\\';
Denis Vlasenko5c329932009-04-12 12:16:21 +00001662 if (!lsa)
1663 goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001664
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001665 // Insert ip=... option into string flags.
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001666 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001667 ip = xasprintf("ip=%s", dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001668 parse_mount_options(ip, &filteropts);
1669
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001670 // Compose new unc '\\server-ip\share'
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001671 // (s => slash after hostname)
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001672 mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
Denis Vlasenko5c329932009-04-12 12:16:21 +00001673#endif
1674 // Lock is required [why?]
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001675 vfsflags |= MS_MANDLOCK;
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001676 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001677 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko5c329932009-04-12 12:16:21 +00001678#if 0
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001679 if (ENABLE_FEATURE_CLEAN_UP) {
1680 free(mp->mnt_fsname);
1681 free(ip);
1682 free(dotted);
1683 free(lsa);
1684 }
Denis Vlasenko5c329932009-04-12 12:16:21 +00001685#endif
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001686 goto report_error;
1687 }
1688
1689 // Might this be an NFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001690 if (ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001691 && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001692 && strchr(mp->mnt_fsname, ':') != NULL
1693 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001694 rc = nfsmount(mp, vfsflags, filteropts);
1695 goto report_error;
1696 }
1697
1698 // Look at the file. (Not found isn't a failure for remount, or for
1699 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001700 // (We use stat, not lstat, in order to allow
1701 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001702 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001703 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1704 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001705 // Do we need to allocate a loopback device for it?
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001706 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1707 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001708 mp->mnt_fsname = NULL; // will receive malloced loop dev name
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001709 if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) {
1710 if (errno == EPERM || errno == EACCES)
1711 bb_error_msg(bb_msg_perm_denied_are_you_root);
1712 else
Denys Vlasenko6331cf02009-11-13 09:08:27 +01001713 bb_perror_msg("can't setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001714 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001715 }
1716
1717 // Autodetect bind mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001718 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1719 vfsflags |= MS_BIND;
1720 }
1721
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001722 // If we know the fstype (or don't need to), jump straight
1723 // to the actual mount.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001724 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1725 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001726 else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001727 // Loop through filesystem types until mount succeeds
1728 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001729
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001730 // Initialize list of block backed filesystems. This has to be
1731 // done here so that during "mount -a", mounts after /proc shows up
1732 // can autodetect.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001733 if (!fslist) {
1734 fslist = get_block_backed_filesystems();
1735 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1736 atexit(delete_block_backed_filesystems);
1737 }
1738
1739 for (fl = fslist; fl; fl = fl->link) {
1740 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001741 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001742 if (!rc)
1743 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001744 }
1745 }
1746
1747 // If mount failed, clean up loop file (if any).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001748 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1749 del_loop(mp->mnt_fsname);
1750 if (ENABLE_FEATURE_CLEAN_UP) {
1751 free(loopFile);
1752 free(mp->mnt_fsname);
1753 }
1754 }
1755
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001756 report_error:
1757 if (ENABLE_FEATURE_CLEAN_UP)
1758 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001759
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001760 if (errno == EBUSY && ignore_busy)
1761 return 0;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001762 if (rc < 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001763 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001764 return rc;
1765}
1766
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001767/* -O support
1768 * Unlike -t, -O should interpret "no" prefix differently:
1769 * -t noa,b,c = -t no(a,b,c) = mount all except fs'es with types a,b, and c
1770 * -O noa,b,c = -O noa,b,c = mount all with without option a,
1771 * or with option b or c.
1772 * But for now we do not support -O a,b,c at all (only -O a).
1773 *
1774 * Another difference from -t support (match_fstype) is that
1775 * we need to examine the _list_ of options in fsopt, not just a string.
1776 */
1777static int match_opt(const char *fs_opt, const char *O_opt)
1778{
1779 int match = 1;
1780 int len;
1781
1782 if (!O_opt)
1783 return match;
1784
1785 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
1786 match--;
1787 O_opt += 2;
1788 }
1789
1790 len = strlen(O_opt);
1791 while (1) {
1792 if (strncmp(fs_opt, O_opt, len) == 0
1793 && (fs_opt[len] == '\0' || fs_opt[len] == ',')
1794 ) {
1795 return match;
1796 }
1797 fs_opt = strchr(fs_opt, ',');
1798 if (!fs_opt)
1799 break;
1800 fs_opt++;
1801 }
1802
1803 return !match;
1804}
1805
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001806// Parse options, if necessary parse fstab/mtab, and call singlemount for
1807// each directory to be mounted.
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001808static const char must_be_root[] ALIGN1 = "you must be root";
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001809
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001810int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001811int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001812{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001813 char *cmdopts = xzalloc(1);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001814 char *fstype = NULL;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001815 char *O_optmatch = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001816 char *storage_path;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001817 llist_t *lst_o = NULL;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001818 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001819 FILE *fstab;
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001820 int i, j, rc = 0;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001821 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001822 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001823 IF_NOT_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001824
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001825 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00001826
Denis Vlasenkof732e962008-02-18 12:07:49 +00001827 // Parse long options, like --bind and --move. Note that -o option
1828 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001829 for (i = j = 1; argv[i]; i++) {
1830 if (argv[i][0] == '-' && argv[i][1] == '-')
1831 append_mount_options(&cmdopts, argv[i] + 2);
1832 else
1833 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001834 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001835 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001836
1837 // Parse remaining options
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001838 // Max 2 params; -o is a list, -v is a counter
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001839 opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001840 opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001841 IF_FEATURE_MOUNT_VERBOSE(, &verbose));
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001842 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00001843 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
1844 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001845 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001846
1847 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001848 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00001849 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001850 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1851
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001852 if (!mountTable)
1853 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001854
Denis Vlasenko2535f122007-09-15 13:28:30 +00001855 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001856 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001857 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001858 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00001859 // util-linux 2.12a happily shows rootfs...
1860 //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001861
1862 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1863 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1864 mtpair->mnt_dir, mtpair->mnt_type,
1865 mtpair->mnt_opts);
1866 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001867 if (ENABLE_FEATURE_CLEAN_UP)
1868 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001869 return EXIT_SUCCESS;
1870 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001871 storage_path = NULL;
1872 } else {
1873 // When we have two arguments, the second is the directory and we can
1874 // skip looking at fstab entirely. We can always abspath() the directory
1875 // argument when we get it.
1876 if (argv[1]) {
1877 if (nonroot)
1878 bb_error_msg_and_die(must_be_root);
1879 mtpair->mnt_fsname = argv[0];
1880 mtpair->mnt_dir = argv[1];
1881 mtpair->mnt_type = fstype;
1882 mtpair->mnt_opts = cmdopts;
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00001883 resolve_mount_spec(&mtpair->mnt_fsname);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001884 rc = singlemount(mtpair, 0);
1885 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00001886 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001887 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001888 }
1889
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001890 // Past this point, we are handling either "mount -a [opts]"
1891 // or "mount [opts] single_param"
1892
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001893 i = parse_mount_options(cmdopts, NULL); // FIXME: should be "long", not "int"
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001894 if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
1895 bb_error_msg_and_die(must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00001896
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001897 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001898 if (ENABLE_FEATURE_MOUNT_FLAGS
1899 && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
1900 ) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001901 // verbose_mount(source, target, type, flags, data)
1902 rc = verbose_mount("", argv[0], "", i, "");
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001903 if (rc)
1904 bb_simple_perror_msg_and_die(argv[0]);
1905 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001906 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001907
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001908 // Open either fstab or mtab
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001909 fstabname = "/etc/fstab";
1910 if (i & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001911 // WARNING. I am not sure this matches util-linux's
1912 // behavior. It's possible util-linux does not
1913 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001914 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001915 }
1916 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001917 if (!fstab)
Denys Vlasenko6331cf02009-11-13 09:08:27 +01001918 bb_perror_msg_and_die("can't read %s", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001919
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001920 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00001921 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001922 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001923 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001924
1925 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00001926 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001927 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001928 GETMNTENT_BUFSIZE/2)
1929 ) { // End of fstab/mtab is reached
1930 mtcur = mtother; // the thing we found last time
1931 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001932 }
1933
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001934 // If we're trying to mount something specific and this isn't it,
1935 // skip it. Note we must match the exact text in fstab (ala
1936 // "proc") or a full path from root
1937 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001938
1939 // Is this what we're looking for?
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001940 if (strcmp(argv[0], mtcur->mnt_fsname) &&
1941 strcmp(storage_path, mtcur->mnt_fsname) &&
1942 strcmp(argv[0], mtcur->mnt_dir) &&
1943 strcmp(storage_path, mtcur->mnt_dir)) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001944
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001945 // Remember this entry. Something later may have
1946 // overmounted it, and we want the _last_ match.
1947 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001948
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001949 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001950 } else {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001951 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001952 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001953 if (nonroot)
1954 bb_error_msg_and_die(must_be_root);
1955
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001956 // Does type match? (NULL matches always)
1957 if (!match_fstype(mtcur, fstype))
1958 continue;
1959
1960 // Skip noauto and swap anyway.
1961 if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
1962 // swap is bogus "fstype", parse_mount_options can't check fstypes
1963 || strcasecmp(mtcur->mnt_type, "swap") == 0
1964 ) {
1965 continue;
1966 }
1967
1968 // Does (at least one) option match?
1969 // (NULL matches always)
1970 if (!match_opt(mtcur->mnt_opts, O_optmatch))
1971 continue;
1972
1973 resolve_mount_spec(&mtcur->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001974
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001975 // NFS mounts want this to be xrealloc-able
1976 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00001977
1978 // Mount this thing
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001979 if (singlemount(mtcur, 1)) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001980 // Count number of failed mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001981 rc++;
1982 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001983 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001984 }
1985 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001986
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001987 // End of fstab/mtab is reached.
1988 // Were we looking for something specific?
1989 if (argv[0]) {
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001990 long l;
1991
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001992 // If we didn't find anything, complain
1993 if (!mtcur->mnt_fsname)
1994 bb_error_msg_and_die("can't find %s in %s",
1995 argv[0], fstabname);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001996
1997 // What happens when we try to "mount swap_partition"?
1998 // (fstab containts "swap_partition swap swap defaults 0 0")
1999 // util-linux-ng 2.13.1 does this:
2000 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2001 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2002 // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory)
2003 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2004 // exit_group(32) = ?
2005#if 0
2006 // In case we want to simply skip swap partitions:
2007 l = parse_mount_options(mtcur->mnt_opts, NULL);
2008 if ((l & MOUNT_SWAP)
2009 // swap is bogus "fstype", parse_mount_options can't check fstypes
2010 || strcasecmp(mtcur->mnt_type, "swap") == 0
2011 ) {
2012 goto ret;
2013 }
2014#endif
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002015 if (nonroot) {
2016 // fstab must have "users" or "user"
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002017 l = parse_mount_options(mtcur->mnt_opts, NULL);
2018 if (!(l & MOUNT_USERS))
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002019 bb_error_msg_and_die(must_be_root);
2020 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002021
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002022 // Mount the last thing we found
2023 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2024 append_mount_options(&(mtcur->mnt_opts), cmdopts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002025 resolve_mount_spec(&mtpair->mnt_fsname);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002026 rc = singlemount(mtcur, 0);
2027 if (ENABLE_FEATURE_CLEAN_UP)
2028 free(mtcur->mnt_opts);
2029 }
2030
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002031 //ret:
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002032 if (ENABLE_FEATURE_CLEAN_UP)
2033 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002034 if (ENABLE_FEATURE_CLEAN_UP) {
2035 free(storage_path);
2036 free(cmdopts);
2037 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002038 return rc;
2039}