blob: 3b77af728ec6b6983ab9d8f9af445693d3f7a93f [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 */
Eric Andersencc8ed391999-10-05 16:24:54 +000011
Rob Landley22d26fc2006-06-15 15:49:36 +000012/* Design notes: There is no spec for mount. Remind me to write one.
Rob Landleydc0955b2006-03-14 18:16:25 +000013
14 mount_main() calls singlemount() which calls mount_it_now().
Eric Andersen9601a1c2006-03-20 18:07:50 +000015
Rob Landleydc0955b2006-03-14 18:16:25 +000016 mount_main() can loop through /etc/fstab for mount -a
17 singlemount() can loop through /etc/filesystems for fstype detection.
18 mount_it_now() does the actual mount.
19*/
20
Eric Andersencc8ed391999-10-05 16:24:54 +000021#include <mntent.h>
Bernhard Reutner-Fischerf4701962008-01-27 12:50:12 +000022#include <syslog.h>
Denis Vlasenkode7684a2008-02-18 21:08:49 +000023#include "libbb.h"
Eric Andersenbd22ed82000-07-08 18:55:24 +000024
Denis Vlasenko6aa76962008-03-18 01:44:52 +000025#if ENABLE_FEATURE_MOUNT_LABEL
Denis Vlasenkode7684a2008-02-18 21:08:49 +000026#include "volume_id.h"
Denis Vlasenko6aa76962008-03-18 01:44:52 +000027#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +000028
29/* Needed for nfs support only */
Denis Vlasenko30a64cd2006-09-15 15:12:00 +000030#include <sys/utsname.h>
31#undef TRUE
32#undef FALSE
33#include <rpc/rpc.h>
34#include <rpc/pmap_prot.h>
35#include <rpc/pmap_clnt.h>
36
Denis Vlasenko2535f122007-09-15 13:28:30 +000037#ifndef MS_SILENT
38#define MS_SILENT (1 << 15)
39#endif
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +000040/* Grab more as needed from util-linux's mount/mount_constants.h */
41#ifndef MS_DIRSYNC
42#define MS_DIRSYNC 128 /* Directory modifications are synchronous */
43#endif
44
Denis Vlasenko30a64cd2006-09-15 15:12:00 +000045
Denis Vlasenko908d6b72006-12-18 23:07:42 +000046#if defined(__dietlibc__)
47/* 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
48 * dietlibc-0.30 does not have implementation of getmntent_r() */
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +000049static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
50 char* buffer ATTRIBUTE_UNUSED, int bufsize ATTRIBUTE_UNUSED)
Denis Vlasenko908d6b72006-12-18 23:07:42 +000051{
Denis Vlasenko908d6b72006-12-18 23:07:42 +000052 struct mntent* ment = getmntent(stream);
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +000053 return memcpy(result, ment, sizeof(*ment));
Denis Vlasenko908d6b72006-12-18 23:07:42 +000054}
55#endif
56
57
Rob Landleydc0955b2006-03-14 18:16:25 +000058// Not real flags, but we want to be able to check for this.
Denis Vlasenko13c5a682006-10-16 22:39:51 +000059enum {
Denis Vlasenkoa4522c52008-03-17 08:46:43 +000060 MOUNT_USERS = (1 << 28) * ENABLE_DESKTOP,
61 MOUNT_NOAUTO = (1 << 29),
62 MOUNT_SWAP = (1 << 30),
Denis Vlasenko13c5a682006-10-16 22:39:51 +000063};
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +000064
65
66#define OPTION_STR "o:t:rwanfvsi"
67enum {
68 OPT_o = (1 << 0),
69 OPT_t = (1 << 1),
70 OPT_r = (1 << 2),
71 OPT_w = (1 << 3),
72 OPT_a = (1 << 4),
73 OPT_n = (1 << 5),
74 OPT_f = (1 << 6),
75 OPT_v = (1 << 7),
76 OPT_s = (1 << 8),
77 OPT_i = (1 << 9),
78};
79
80#if ENABLE_FEATURE_MTAB_SUPPORT
81#define useMtab (!(option_mask32 & OPT_n))
82#else
83#define useMtab 0
84#endif
85
86#if ENABLE_FEATURE_MOUNT_FAKE
87#define fakeIt (option_mask32 & OPT_f)
88#else
89#define fakeIt 0
90#endif
91
92
Denis Vlasenko13c5a682006-10-16 22:39:51 +000093// TODO: more "user" flag compatibility.
94// "user" option (from mount manpage):
95// Only the user that mounted a filesystem can unmount it again.
96// If any user should be able to unmount, then use users instead of user
97// in the fstab line. The owner option is similar to the user option,
98// with the restriction that the user must be the owner of the special file.
99// This may be useful e.g. for /dev/fd if a login script makes
100// the console user owner of this device.
Rob Landley3ba7bd12006-08-09 19:51:13 +0000101
Rob Landleydc0955b2006-03-14 18:16:25 +0000102/* Standard mount options (from -o options or --options), with corresponding
103 * flags */
Eric Andersencc8ed391999-10-05 16:24:54 +0000104
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000105static const int32_t mount_options[] = {
Rob Landleye3781b72006-08-08 01:39:49 +0000106 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
Rob Landleydc0955b2006-03-14 18:16:25 +0000107
Rob Landleye3781b72006-08-08 01:39:49 +0000108 USE_FEATURE_MOUNT_LOOP(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000109 /* "loop" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000110 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000111
Rob Landleye3781b72006-08-08 01:39:49 +0000112 USE_FEATURE_MOUNT_FSTAB(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000113 /* "defaults" */ 0,
114 /* "quiet" 0 - do not filter out, vfat wants to see it */
115 /* "noauto" */ MOUNT_NOAUTO,
116 /* "sw" */ MOUNT_SWAP,
117 /* "swap" */ MOUNT_SWAP,
118 USE_DESKTOP(/* "user" */ MOUNT_USERS,)
119 USE_DESKTOP(/* "users" */ MOUNT_USERS,)
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000120 /* "_netdev" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000121 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000122
Rob Landleye3781b72006-08-08 01:39:49 +0000123 USE_FEATURE_MOUNT_FLAGS(
124 // vfs flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000125 /* "nosuid" */ MS_NOSUID,
126 /* "suid" */ ~MS_NOSUID,
127 /* "dev" */ ~MS_NODEV,
128 /* "nodev" */ MS_NODEV,
129 /* "exec" */ ~MS_NOEXEC,
130 /* "noexec" */ MS_NOEXEC,
131 /* "sync" */ MS_SYNCHRONOUS,
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +0000132 /* "dirsync" */ MS_DIRSYNC,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000133 /* "async" */ ~MS_SYNCHRONOUS,
134 /* "atime" */ ~MS_NOATIME,
135 /* "noatime" */ MS_NOATIME,
136 /* "diratime" */ ~MS_NODIRATIME,
137 /* "nodiratime" */ MS_NODIRATIME,
138 /* "loud" */ ~MS_SILENT,
Eric Andersen9601a1c2006-03-20 18:07:50 +0000139
Rob Landleye3781b72006-08-08 01:39:49 +0000140 // action flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000141 /* "bind" */ MS_BIND,
142 /* "move" */ MS_MOVE,
143 /* "shared" */ MS_SHARED,
144 /* "slave" */ MS_SLAVE,
145 /* "private" */ MS_PRIVATE,
146 /* "unbindable" */ MS_UNBINDABLE,
147 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
148 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
149 /* "rprivate" */ MS_SLAVE|MS_RECURSIVE,
150 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
Rob Landleye3781b72006-08-08 01:39:49 +0000151 )
152
153 // Always understood.
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000154 /* "ro" */ MS_RDONLY, // vfs flag
155 /* "rw" */ ~MS_RDONLY, // vfs flag
156 /* "remount" */ MS_REMOUNT // action flag
Eric Andersencc8ed391999-10-05 16:24:54 +0000157};
158
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000159static const char mount_option_str[] =
160 USE_FEATURE_MOUNT_LOOP(
161 "loop" "\0"
162 )
163 USE_FEATURE_MOUNT_FSTAB(
164 "defaults" "\0"
165 /* "quiet" "\0" - do not filter out, vfat wants to see it */
166 "noauto" "\0"
167 "sw" "\0"
168 "swap" "\0"
169 USE_DESKTOP("user" "\0")
170 USE_DESKTOP("users" "\0")
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000171 "_netdev" "\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000172 )
173 USE_FEATURE_MOUNT_FLAGS(
174 // vfs flags
175 "nosuid" "\0"
176 "suid" "\0"
177 "dev" "\0"
178 "nodev" "\0"
179 "exec" "\0"
180 "noexec" "\0"
181 "sync" "\0"
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +0000182 "dirsync" "\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000183 "async" "\0"
184 "atime" "\0"
185 "noatime" "\0"
186 "diratime" "\0"
187 "nodiratime" "\0"
188 "loud" "\0"
189
190 // action flags
191 "bind" "\0"
192 "move" "\0"
193 "shared" "\0"
194 "slave" "\0"
195 "private" "\0"
196 "unbindable" "\0"
197 "rshared" "\0"
198 "rslave" "\0"
199 "rprivate" "\0"
200 "runbindable" "\0"
201 )
202
203 // Always understood.
204 "ro" "\0" // vfs flag
205 "rw" "\0" // vfs flag
206 "remount" "\0" // action flag
207;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000208
Denis Vlasenkof732e962008-02-18 12:07:49 +0000209
210struct globals {
211#if ENABLE_FEATURE_MOUNT_NFS
212 smalluint nfs_mount_version;
213#endif
214#if ENABLE_FEATURE_MOUNT_VERBOSE
215 unsigned verbose;
216#endif
217 llist_t *fslist;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000218 char getmntent_buf[1];
Denis Vlasenkof732e962008-02-18 12:07:49 +0000219
220};
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000221enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
Denis Vlasenkof732e962008-02-18 12:07:49 +0000222#define G (*(struct globals*)&bb_common_bufsiz1)
223#define nfs_mount_version (G.nfs_mount_version)
Denis Vlasenkob4133682008-02-18 13:05:38 +0000224#if ENABLE_FEATURE_MOUNT_VERBOSE
Denis Vlasenkof732e962008-02-18 12:07:49 +0000225#define verbose (G.verbose )
Denis Vlasenkob4133682008-02-18 13:05:38 +0000226#else
227#define verbose 0
228#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000229#define fslist (G.fslist )
230#define getmntent_buf (G.getmntent_buf )
231
232
233#if ENABLE_FEATURE_MOUNT_VERBOSE
234static int verbose_mount(const char *source, const char *target,
235 const char *filesystemtype,
236 unsigned long mountflags, const void *data)
237{
238 int rc;
239
240 errno = 0;
241 rc = mount(source, target, filesystemtype, mountflags, data);
242 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000243 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000244 source, target, filesystemtype,
245 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000246 return rc;
247}
248#else
249#define verbose_mount(...) mount(__VA_ARGS__)
250#endif
251
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000252static int resolve_mount_spec(char **fsname)
253{
254 char *tmp = NULL;
255
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000256#if ENABLE_FEATURE_MOUNT_LABEL
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000257 if (!strncmp(*fsname, "UUID=", 5))
258 tmp = get_devname_from_uuid(*fsname + 5);
259 else if (!strncmp(*fsname, "LABEL=", 6))
260 tmp = get_devname_from_label(*fsname + 6);
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000261#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000262
263 if (tmp) {
264 *fsname = tmp;
265 return 1;
266 }
267 return 0;
268}
269
Rob Landleydc0955b2006-03-14 18:16:25 +0000270/* Append mount options to string */
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000271static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000272{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000273 if (*oldopts && **oldopts) {
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000274 /* do not insert options which are already there */
275 while (newopts[0]) {
276 char *p;
277 int len = strlen(newopts);
278 p = strchr(newopts, ',');
279 if (p) len = p - newopts;
280 p = *oldopts;
281 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000282 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000283 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000284 goto skip;
285 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000286 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000287 p++;
288 }
289 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
290 free(*oldopts);
291 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000292 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000293 newopts += len;
294 while (newopts[0] == ',') newopts++;
295 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000296 } else {
297 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000298 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000299 }
300}
301
302/* Use the mount_options list to parse options into flags.
Denis Vlasenko00d7d6c2006-09-11 17:42:44 +0000303 * Also return list of unrecognized options if unrecognized!=NULL */
Denis Vlasenkob4133682008-02-18 13:05:38 +0000304static long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000305{
Denis Vlasenkob4133682008-02-18 13:05:38 +0000306 long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000307
Rob Landley6a6798b2005-08-10 20:35:54 +0000308 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000309 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000310 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000311 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000312 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000313
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000314 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000315
Bernhard Reutner-Fischerf9a07842008-05-30 10:44:37 +0000316/* FIXME: use hasmntopt() */
Rob Landley6a6798b2005-08-10 20:35:54 +0000317 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000318 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000319 if (!strcasecmp(option_str, options)) {
320 long fl = mount_options[i];
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000321 if (fl < 0) flags &= fl;
Rob Landleydc0955b2006-03-14 18:16:25 +0000322 else flags |= fl;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000323 break;
324 }
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000325 option_str += strlen(option_str) + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000326 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000327 // If unrecognized not NULL, append unrecognized mount options */
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000328 if (unrecognized && i == ARRAY_SIZE(mount_options)) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000329 // Add it to strflags, to pass on to kernel
Rob Landleydc0955b2006-03-14 18:16:25 +0000330 i = *unrecognized ? strlen(*unrecognized) : 0;
331 *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000332
Rob Landley6a6798b2005-08-10 20:35:54 +0000333 // Comma separated if it's not the first one
Rob Landleydc0955b2006-03-14 18:16:25 +0000334 if (i) (*unrecognized)[i++] = ',';
335 strcpy((*unrecognized)+i, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000336 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000337
Denis Vlasenko2535f122007-09-15 13:28:30 +0000338 if (!comma)
339 break;
340 // Advance to next option
341 *comma = ',';
342 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000343 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000344
Rob Landleydc0955b2006-03-14 18:16:25 +0000345 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000346}
347
Rob Landleydc0955b2006-03-14 18:16:25 +0000348// Return a list of all block device backed filesystems
Matt Kraai12400822001-04-17 04:32:50 +0000349
Rob Landleydc0955b2006-03-14 18:16:25 +0000350static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000351{
Denis Vlasenko87468852007-04-13 23:22:00 +0000352 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000353 "/etc/filesystems",
354 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000355 };
356 char *fs, *buf;
Rob Landleydc0955b2006-03-14 18:16:25 +0000357 llist_t *list = 0;
358 int i;
359 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000360
Denis Vlasenko87468852007-04-13 23:22:00 +0000361 for (i = 0; i < 2; i++) {
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000362 f = fopen(filesystems[i], "r");
363 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000364
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000365 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000366 if (!strncmp(buf, "nodev", 5) && isspace(buf[5]))
367 continue;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000368 fs = skip_whitespace(buf);
Denis Vlasenko372686b2006-10-12 22:42:33 +0000369 if (*fs=='#' || *fs=='*' || !*fs) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000370
Denis Vlasenko372686b2006-10-12 22:42:33 +0000371 llist_add_to_end(&list, xstrdup(fs));
372 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000373 }
374 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
375 }
376
377 return list;
378}
379
Rob Landleydc0955b2006-03-14 18:16:25 +0000380#if ENABLE_FEATURE_CLEAN_UP
381static void delete_block_backed_filesystems(void)
382{
Rob Landleya6b5b602006-05-08 19:03:07 +0000383 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000384}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000385#else
386void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000387#endif
388
Rob Landleydc0955b2006-03-14 18:16:25 +0000389// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000390// NB: mp->xxx fields may be trashed on exit
Denis Vlasenkob4133682008-02-18 13:05:38 +0000391static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000392{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000393 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000394
Denis Vlasenkob4133682008-02-18 13:05:38 +0000395 if (fakeIt) {
396 if (verbose >= 2)
397 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
398 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
399 vfsflags, filteropts);
400 goto mtab;
401 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000402
Rob Landleydc0955b2006-03-14 18:16:25 +0000403 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000404 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000405 errno = 0;
406 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000407 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000408
409 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000410 // helper program mount.<mnt_type>
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000411 if (ENABLE_FEATURE_MOUNT_HELPERS && rc) {
412 char *args[6];
413 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000414 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000415 rc = 1;
416 if (filteropts) {
417 args[rc++] = (char *)"-o";
418 args[rc++] = filteropts;
419 }
420 args[rc++] = mp->mnt_fsname;
421 args[rc++] = mp->mnt_dir;
422 args[rc] = NULL;
423 rc = wait4pid(spawn(args));
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000424 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000425 if (!rc)
426 break;
427 errno = errno_save;
428 }
429
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000430 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000431 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000432 if (!(vfsflags & MS_SILENT))
433 bb_error_msg("%s is write-protected, mounting read-only",
434 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000435 vfsflags |= MS_RDONLY;
436 }
437
Rob Landleydc0955b2006-03-14 18:16:25 +0000438 // Abort entirely if permission denied.
439
440 if (rc && errno == EPERM)
441 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
442
443 /* If the mount was successful, and we're maintaining an old-style
444 * mtab file by hand, add the new entry to it now. */
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000445 mtab:
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000446 if (useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000447 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000448 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000449 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000450 int i;
451
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000452 if (!mountTable) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000453 bb_error_msg("no %s", bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000454 goto ret;
455 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000456
457 // Add vfs string flags
458
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000459 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
460 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
461 append_mount_options(&(mp->mnt_opts), option_str);
462 option_str += strlen(option_str) + 1;
463 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000464
465 // Remove trailing / (if any) from directory we mounted on
466
Denis Vlasenko727ef942006-09-14 13:19:19 +0000467 i = strlen(mp->mnt_dir) - 1;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000468 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000469
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000470 // Convert to canonical pathnames as needed
Denis Vlasenko727ef942006-09-14 13:19:19 +0000471
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000472 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000473 fsname = 0;
Denis Vlasenko727ef942006-09-14 13:19:19 +0000474 if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000475 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000476 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000477 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000478 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000479
480 // Write and close.
481
Denis Vlasenko727ef942006-09-14 13:19:19 +0000482 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000483 endmntent(mountTable);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000484 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000485 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000486 free(fsname);
487 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000488 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000489 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000490 return rc;
491}
492
Denis Vlasenko25098f72006-09-14 15:46:33 +0000493#if ENABLE_FEATURE_MOUNT_NFS
494
495/*
496 * Linux NFS mount
497 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
498 *
499 * Licensed under GPLv2, see file LICENSE in this tarball for details.
500 *
501 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
502 * numbers to be specified on the command line.
503 *
504 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
505 * Omit the call to connect() for Linux version 1.3.11 or later.
506 *
507 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
508 * Implemented the "bg", "fg" and "retry" mount options for NFS.
509 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000510 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000511 * - added Native Language Support
512 *
513 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
514 * plus NFSv3 stuff.
515 */
516
Denis Vlasenko25098f72006-09-14 15:46:33 +0000517/* This is just a warning of a common mistake. Possibly this should be a
518 * uclibc faq entry rather than in busybox... */
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000519#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000520#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
521#endif
522
523#define MOUNTPORT 635
524#define MNTPATHLEN 1024
525#define MNTNAMLEN 255
526#define FHSIZE 32
527#define FHSIZE3 64
528
529typedef char fhandle[FHSIZE];
530
531typedef struct {
532 unsigned int fhandle3_len;
533 char *fhandle3_val;
534} fhandle3;
535
536enum mountstat3 {
537 MNT_OK = 0,
538 MNT3ERR_PERM = 1,
539 MNT3ERR_NOENT = 2,
540 MNT3ERR_IO = 5,
541 MNT3ERR_ACCES = 13,
542 MNT3ERR_NOTDIR = 20,
543 MNT3ERR_INVAL = 22,
544 MNT3ERR_NAMETOOLONG = 63,
545 MNT3ERR_NOTSUPP = 10004,
546 MNT3ERR_SERVERFAULT = 10006,
547};
548typedef enum mountstat3 mountstat3;
549
550struct fhstatus {
551 unsigned int fhs_status;
552 union {
553 fhandle fhs_fhandle;
554 } fhstatus_u;
555};
556typedef struct fhstatus fhstatus;
557
558struct mountres3_ok {
559 fhandle3 fhandle;
560 struct {
561 unsigned int auth_flavours_len;
562 char *auth_flavours_val;
563 } auth_flavours;
564};
565typedef struct mountres3_ok mountres3_ok;
566
567struct mountres3 {
568 mountstat3 fhs_status;
569 union {
570 mountres3_ok mountinfo;
571 } mountres3_u;
572};
573typedef struct mountres3 mountres3;
574
575typedef char *dirpath;
576
577typedef char *name;
578
579typedef struct mountbody *mountlist;
580
581struct mountbody {
582 name ml_hostname;
583 dirpath ml_directory;
584 mountlist ml_next;
585};
586typedef struct mountbody mountbody;
587
588typedef struct groupnode *groups;
589
590struct groupnode {
591 name gr_name;
592 groups gr_next;
593};
594typedef struct groupnode groupnode;
595
596typedef struct exportnode *exports;
597
598struct exportnode {
599 dirpath ex_dir;
600 groups ex_groups;
601 exports ex_next;
602};
603typedef struct exportnode exportnode;
604
605struct ppathcnf {
606 int pc_link_max;
607 short pc_max_canon;
608 short pc_max_input;
609 short pc_name_max;
610 short pc_path_max;
611 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000612 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000613 char pc_xxx;
614 short pc_mask[2];
615};
616typedef struct ppathcnf ppathcnf;
617
618#define MOUNTPROG 100005
619#define MOUNTVERS 1
620
621#define MOUNTPROC_NULL 0
622#define MOUNTPROC_MNT 1
623#define MOUNTPROC_DUMP 2
624#define MOUNTPROC_UMNT 3
625#define MOUNTPROC_UMNTALL 4
626#define MOUNTPROC_EXPORT 5
627#define MOUNTPROC_EXPORTALL 6
628
629#define MOUNTVERS_POSIX 2
630
631#define MOUNTPROC_PATHCONF 7
632
633#define MOUNT_V3 3
634
635#define MOUNTPROC3_NULL 0
636#define MOUNTPROC3_MNT 1
637#define MOUNTPROC3_DUMP 2
638#define MOUNTPROC3_UMNT 3
639#define MOUNTPROC3_UMNTALL 4
640#define MOUNTPROC3_EXPORT 5
641
642enum {
643#ifndef NFS_FHSIZE
644 NFS_FHSIZE = 32,
645#endif
646#ifndef NFS_PORT
647 NFS_PORT = 2049
648#endif
649};
650
Denis Vlasenko25098f72006-09-14 15:46:33 +0000651/*
652 * We want to be able to compile mount on old kernels in such a way
653 * that the binary will work well on more recent kernels.
654 * Thus, if necessary we teach nfsmount.c the structure of new fields
655 * that will come later.
656 *
657 * Moreover, the new kernel includes conflict with glibc includes
658 * so it is easiest to ignore the kernel altogether (at compile time).
659 */
660
661struct nfs2_fh {
662 char data[32];
663};
664struct nfs3_fh {
665 unsigned short size;
666 unsigned char data[64];
667};
668
669struct nfs_mount_data {
670 int version; /* 1 */
671 int fd; /* 1 */
672 struct nfs2_fh old_root; /* 1 */
673 int flags; /* 1 */
674 int rsize; /* 1 */
675 int wsize; /* 1 */
676 int timeo; /* 1 */
677 int retrans; /* 1 */
678 int acregmin; /* 1 */
679 int acregmax; /* 1 */
680 int acdirmin; /* 1 */
681 int acdirmax; /* 1 */
682 struct sockaddr_in addr; /* 1 */
683 char hostname[256]; /* 1 */
684 int namlen; /* 2 */
685 unsigned int bsize; /* 3 */
686 struct nfs3_fh root; /* 4 */
687};
688
689/* bits in the flags field */
690enum {
691 NFS_MOUNT_SOFT = 0x0001, /* 1 */
692 NFS_MOUNT_INTR = 0x0002, /* 1 */
693 NFS_MOUNT_SECURE = 0x0004, /* 1 */
694 NFS_MOUNT_POSIX = 0x0008, /* 1 */
695 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
696 NFS_MOUNT_NOAC = 0x0020, /* 1 */
697 NFS_MOUNT_TCP = 0x0040, /* 2 */
698 NFS_MOUNT_VER3 = 0x0080, /* 3 */
699 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
700 NFS_MOUNT_NONLM = 0x0200 /* 3 */
701};
702
703
704/*
705 * We need to translate between nfs status return values and
706 * the local errno values which may not be the same.
707 *
708 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
709 * "after #include <errno.h> the symbol errno is reserved for any use,
710 * it cannot even be used as a struct tag or field name".
711 */
712
713#ifndef EDQUOT
714#define EDQUOT ENOSPC
715#endif
716
717// Convert each NFSERR_BLAH into EBLAH
718
719static const struct {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000720 short stat;
721 short errnum;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000722} nfs_errtbl[] = {
723 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
724 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
725 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
726 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
727};
728
729static char *nfs_strerror(int status)
730{
731 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000732
733 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
734 if (nfs_errtbl[i].stat == status)
735 return strerror(nfs_errtbl[i].errnum);
736 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000737 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000738}
739
740static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
741{
742 if (!xdr_opaque(xdrs, objp, FHSIZE))
743 return FALSE;
744 return TRUE;
745}
746
747static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
748{
749 if (!xdr_u_int(xdrs, &objp->fhs_status))
750 return FALSE;
751 switch (objp->fhs_status) {
752 case 0:
753 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
754 return FALSE;
755 break;
756 default:
757 break;
758 }
759 return TRUE;
760}
761
762static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
763{
764 if (!xdr_string(xdrs, objp, MNTPATHLEN))
765 return FALSE;
766 return TRUE;
767}
768
769static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
770{
771 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
772 return FALSE;
773 return TRUE;
774}
775
776static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
777{
778 if (!xdr_fhandle3(xdrs, &objp->fhandle))
779 return FALSE;
780 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
781 sizeof (int), (xdrproc_t) xdr_int))
782 return FALSE;
783 return TRUE;
784}
785
786static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
787{
788 if (!xdr_enum(xdrs, (enum_t *) objp))
789 return FALSE;
790 return TRUE;
791}
792
793static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
794{
795 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
796 return FALSE;
797 switch (objp->fhs_status) {
798 case MNT_OK:
799 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
800 return FALSE;
801 break;
802 default:
803 break;
804 }
805 return TRUE;
806}
807
808#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
809
Denis Vlasenko25098f72006-09-14 15:46:33 +0000810/*
811 * Unfortunately, the kernel prints annoying console messages
812 * in case of an unexpected nfs mount version (instead of
813 * just returning some error). Therefore we'll have to try
814 * and figure out what version the kernel expects.
815 *
816 * Variables:
817 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
818 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
819 * nfs_mount_version: version this source and running kernel can handle
820 */
821static void
822find_kernel_nfs_mount_version(void)
823{
Denis Vlasenkob9256052007-09-28 10:29:17 +0000824 int kernel_version;
825
826 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000827 return;
828
829 nfs_mount_version = 4; /* default */
830
831 kernel_version = get_linux_version_code();
832 if (kernel_version) {
833 if (kernel_version < KERNEL_VERSION(2,1,32))
834 nfs_mount_version = 1;
835 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
836 (kernel_version >= KERNEL_VERSION(2,3,0) &&
837 kernel_version < KERNEL_VERSION(2,3,99)))
838 nfs_mount_version = 3;
839 /* else v4 since 2.3.99pre4 */
840 }
841}
842
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +0000843static void
Denis Vlasenkob9256052007-09-28 10:29:17 +0000844get_mountport(struct pmap *pm_mnt,
845 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +0000846 long unsigned prog,
847 long unsigned version,
848 long unsigned proto,
849 long unsigned port)
850{
851 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000852
853 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +0000854/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
855 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000856 pmap = pmap_getmaps(server_addr);
857
858 if (version > MAX_NFSPROT)
859 version = MAX_NFSPROT;
860 if (!prog)
861 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000862 pm_mnt->pm_prog = prog;
863 pm_mnt->pm_vers = version;
864 pm_mnt->pm_prot = proto;
865 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000866
Denis Vlasenko25098f72006-09-14 15:46:33 +0000867 while (pmap) {
868 if (pmap->pml_map.pm_prog != prog)
869 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000870 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000871 goto next;
872 if (version > 2 && pmap->pml_map.pm_vers != version)
873 goto next;
874 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
875 goto next;
876 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
Denis Vlasenkob9256052007-09-28 10:29:17 +0000877 (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto) ||
Denis Vlasenko25098f72006-09-14 15:46:33 +0000878 (port && pmap->pml_map.pm_port != port))
879 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000880 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
881 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +0000882 pmap = pmap->pml_next;
883 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000884 if (!pm_mnt->pm_vers)
885 pm_mnt->pm_vers = MOUNTVERS;
886 if (!pm_mnt->pm_port)
887 pm_mnt->pm_port = MOUNTPORT;
888 if (!pm_mnt->pm_prot)
889 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000890}
891
Denis Vlasenkof0000652007-09-04 18:30:26 +0000892#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +0000893static int daemonize(void)
894{
895 int fd;
896 int pid = fork();
897 if (pid < 0) /* error */
898 return -errno;
899 if (pid > 0) /* parent */
900 return 0;
901 /* child */
902 fd = xopen(bb_dev_null, O_RDWR);
903 dup2(fd, 0);
904 dup2(fd, 1);
905 dup2(fd, 2);
Denis Vlasenko9af7c9d2007-01-19 21:19:35 +0000906 while (fd > 2) close(fd--);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000907 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +0000908 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000909 logmode = LOGMODE_SYSLOG;
910 return 1;
911}
Denis Vlasenkof0000652007-09-04 18:30:26 +0000912#else
913static inline int daemonize(void) { return -ENOSYS; }
914#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000915
916// TODO
Denis Vlasenko68404f12008-03-17 09:00:54 +0000917static inline int we_saw_this_host_before(const char *hostname ATTRIBUTE_UNUSED)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000918{
919 return 0;
920}
921
922/* RPC strerror analogs are terminally idiotic:
923 * *mandatory* prefix and \n at end.
924 * This hopefully helps. Usage:
925 * error_msg_rpc(clnt_*error*(" ")) */
926static void error_msg_rpc(const char *msg)
927{
Denis Vlasenko23514fe2006-09-19 14:07:52 +0000928 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000929 while (msg[0] == ' ' || msg[0] == ':') msg++;
930 len = strlen(msg);
931 while (len && msg[len-1] == '\n') len--;
932 bb_error_msg("%.*s", len, msg);
933}
934
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +0000935// NB: mp->xxx fields may be trashed on exit
Denis Vlasenkob4133682008-02-18 13:05:38 +0000936static int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000937{
938 CLIENT *mclient;
939 char *hostname;
940 char *pathname;
941 char *mounthost;
942 struct nfs_mount_data data;
943 char *opt;
944 struct hostent *hp;
945 struct sockaddr_in server_addr;
946 struct sockaddr_in mount_server_addr;
947 int msock, fsock;
948 union {
949 struct fhstatus nfsv2;
950 struct mountres3 nfsv3;
951 } status;
952 int daemonized;
953 char *s;
954 int port;
955 int mountport;
956 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +0000957#if BB_MMU
958 int bg = 0;
959#else
960 enum { bg = 0 };
961#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000962 int soft;
963 int intr;
964 int posix;
965 int nocto;
966 int noac;
967 int nolock;
968 int retry;
969 int tcp;
970 int mountprog;
971 int mountvers;
972 int nfsprog;
973 int nfsvers;
974 int retval;
975
976 find_kernel_nfs_mount_version();
977
978 daemonized = 0;
979 mounthost = NULL;
980 retval = ETIMEDOUT;
981 msock = fsock = -1;
982 mclient = NULL;
983
984 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
985
986 filteropts = xstrdup(filteropts); /* going to trash it later... */
987
988 hostname = xstrdup(mp->mnt_fsname);
989 /* mount_main() guarantees that ':' is there */
990 s = strchr(hostname, ':');
991 pathname = s + 1;
992 *s = '\0';
993 /* Ignore all but first hostname in replicated mounts
994 until they can be fully supported. (mack@sgi.com) */
995 s = strchr(hostname, ',');
996 if (s) {
997 *s = '\0';
998 bb_error_msg("warning: multiple hostnames not supported");
999 }
1000
1001 server_addr.sin_family = AF_INET;
1002 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1003 hp = gethostbyname(hostname);
1004 if (hp == NULL) {
1005 bb_herror_msg("%s", hostname);
1006 goto fail;
1007 }
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001008 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001009 bb_error_msg("got bad hp->h_length");
1010 hp->h_length = sizeof(struct in_addr);
1011 }
1012 memcpy(&server_addr.sin_addr,
1013 hp->h_addr, hp->h_length);
1014 }
1015
1016 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1017
1018 /* add IP address to mtab options for use when unmounting */
1019
1020 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1021 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1022 } else {
1023 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1024 mp->mnt_opts[0] ? "," : "",
1025 inet_ntoa(server_addr.sin_addr));
1026 free(mp->mnt_opts);
1027 mp->mnt_opts = tmp;
1028 }
1029
1030 /* Set default options.
1031 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1032 * let the kernel decide.
1033 * timeo is filled in after we know whether it'll be TCP or UDP. */
1034 memset(&data, 0, sizeof(data));
1035 data.retrans = 3;
1036 data.acregmin = 3;
1037 data.acregmax = 60;
1038 data.acdirmin = 30;
1039 data.acdirmax = 60;
1040 data.namlen = NAME_MAX;
1041
Denis Vlasenko25098f72006-09-14 15:46:33 +00001042 soft = 0;
1043 intr = 0;
1044 posix = 0;
1045 nocto = 0;
1046 nolock = 0;
1047 noac = 0;
1048 retry = 10000; /* 10000 minutes ~ 1 week */
1049 tcp = 0;
1050
1051 mountprog = MOUNTPROG;
1052 mountvers = 0;
1053 port = 0;
1054 mountport = 0;
1055 nfsprog = 100003;
1056 nfsvers = 0;
1057
1058 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001059 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001060 char *opteq = strchr(opt, '=');
1061 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001062 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001063 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001064 /* 0 */ "rsize\0"
1065 /* 1 */ "wsize\0"
1066 /* 2 */ "timeo\0"
1067 /* 3 */ "retrans\0"
1068 /* 4 */ "acregmin\0"
1069 /* 5 */ "acregmax\0"
1070 /* 6 */ "acdirmin\0"
1071 /* 7 */ "acdirmax\0"
1072 /* 8 */ "actimeo\0"
1073 /* 9 */ "retry\0"
1074 /* 10 */ "port\0"
1075 /* 11 */ "mountport\0"
1076 /* 12 */ "mounthost\0"
1077 /* 13 */ "mountprog\0"
1078 /* 14 */ "mountvers\0"
1079 /* 15 */ "nfsprog\0"
1080 /* 16 */ "nfsvers\0"
1081 /* 17 */ "vers\0"
1082 /* 18 */ "proto\0"
1083 /* 19 */ "namlen\0"
1084 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001085
1086 *opteq++ = '\0';
1087 idx = index_in_strings(options, opt);
1088 switch (idx) {
1089 case 12: // "mounthost"
1090 mounthost = xstrndup(opteq,
1091 strcspn(opteq, " \t\n\r,"));
1092 continue;
1093 case 18: // "proto"
1094 if (!strncmp(opteq, "tcp", 3))
1095 tcp = 1;
1096 else if (!strncmp(opteq, "udp", 3))
1097 tcp = 0;
1098 else
1099 bb_error_msg("warning: unrecognized proto= option");
1100 continue;
1101 case 20: // "addr" - ignore
1102 continue;
1103 }
1104
1105 val = xatoi_u(opteq);
1106 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001107 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001108 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001109 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001110 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001111 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001112 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001113 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001114 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001115 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001116 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001117 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001118 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001119 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001120 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001121 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001122 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001123 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001124 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001125 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001126 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001127 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001128 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001129 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001130 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001131 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001132 data.acregmin = val;
1133 data.acregmax = val;
1134 data.acdirmin = val;
1135 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001136 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001137 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001138 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001139 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001140 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001141 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001142 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001143 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001144 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001145 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001146 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001147 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001148 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001149 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001150 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001151 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001152 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001153 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001154 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001155 case 16: // "nfsvers"
1156 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001157 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001158 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001159 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001160 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001161 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001162 //else
1163 // bb_error_msg("warning: option namlen is not supported\n");
1164 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001165 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001166 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1167 goto fail;
1168 }
1169 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001170 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001171 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001172 "bg\0"
1173 "fg\0"
1174 "soft\0"
1175 "hard\0"
1176 "intr\0"
1177 "posix\0"
1178 "cto\0"
1179 "ac\0"
1180 "tcp\0"
1181 "udp\0"
1182 "lock\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001183 int val = 1;
1184 if (!strncmp(opt, "no", 2)) {
1185 val = 0;
1186 opt += 2;
1187 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001188 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001189 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001190#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001191 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001192#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001193 break;
1194 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001195#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001196 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001197#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001198 break;
1199 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001200 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001201 break;
1202 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001203 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001204 break;
1205 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001206 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001207 break;
1208 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001209 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001210 break;
1211 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001212 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001213 break;
1214 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001215 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001216 break;
1217 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001218 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001219 break;
1220 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001221 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001222 break;
1223 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001224 if (nfs_mount_version >= 3)
1225 nolock = !val;
1226 else
1227 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001228 break;
1229 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001230 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1231 goto fail;
1232 }
1233 }
1234 }
1235 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1236
1237 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1238 | (intr ? NFS_MOUNT_INTR : 0)
1239 | (posix ? NFS_MOUNT_POSIX : 0)
1240 | (nocto ? NFS_MOUNT_NOCTO : 0)
1241 | (noac ? NFS_MOUNT_NOAC : 0);
1242 if (nfs_mount_version >= 2)
1243 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1244 if (nfs_mount_version >= 3)
1245 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1246 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1247 bb_error_msg("NFSv%d not supported", nfsvers);
1248 goto fail;
1249 }
1250 if (nfsvers && !mountvers)
1251 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1252 if (nfsvers && nfsvers < mountvers) {
1253 mountvers = nfsvers;
1254 }
1255
1256 /* Adjust options if none specified */
1257 if (!data.timeo)
1258 data.timeo = tcp ? 70 : 7;
1259
Denis Vlasenko25098f72006-09-14 15:46:33 +00001260 data.version = nfs_mount_version;
1261
1262 if (vfsflags & MS_REMOUNT)
1263 goto do_mount;
1264
1265 /*
1266 * If the previous mount operation on the same host was
1267 * backgrounded, and the "bg" for this mount is also set,
1268 * give up immediately, to avoid the initial timeout.
1269 */
1270 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001271 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001272 if (daemonized <= 0) { /* parent or error */
1273 retval = -daemonized;
1274 goto ret;
1275 }
1276 }
1277
1278 /* create mount daemon client */
1279 /* See if the nfs host = mount host. */
1280 if (mounthost) {
1281 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1282 mount_server_addr.sin_family = AF_INET;
1283 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1284 } else {
1285 hp = gethostbyname(mounthost);
1286 if (hp == NULL) {
1287 bb_herror_msg("%s", mounthost);
1288 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001289 }
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001290 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
1291 bb_error_msg("got bad hp->h_length");
1292 hp->h_length = sizeof(struct in_addr);
1293 }
1294 mount_server_addr.sin_family = AF_INET;
1295 memcpy(&mount_server_addr.sin_addr,
1296 hp->h_addr, hp->h_length);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001297 }
1298 }
1299
1300 /*
1301 * The following loop implements the mount retries. When the mount
1302 * times out, and the "bg" option is set, we background ourself
1303 * and continue trying.
1304 *
1305 * The case where the mount point is not present and the "bg"
1306 * option is set, is treated as a timeout. This is done to
1307 * support nested mounts.
1308 *
1309 * The "retry" count specified by the user is the number of
1310 * minutes to retry before giving up.
1311 */
1312 {
1313 struct timeval total_timeout;
1314 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001315 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001316 time_t t;
1317 time_t prevt;
1318 time_t timeout;
1319
1320 retry_timeout.tv_sec = 3;
1321 retry_timeout.tv_usec = 0;
1322 total_timeout.tv_sec = 20;
1323 total_timeout.tv_usec = 0;
1324 timeout = time(NULL) + 60 * retry;
1325 prevt = 0;
1326 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001327 retry:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001328 /* be careful not to use too many CPU cycles */
1329 if (t - prevt < 30)
1330 sleep(30);
1331
Denis Vlasenkob9256052007-09-28 10:29:17 +00001332 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001333 mountprog,
1334 mountvers,
1335 proto,
1336 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001337 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001338
1339 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001340 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001341 msock = RPC_ANYSOCK;
1342
Denis Vlasenkob9256052007-09-28 10:29:17 +00001343 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001344 case IPPROTO_UDP:
1345 mclient = clntudp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001346 pm_mnt.pm_prog,
1347 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001348 retry_timeout,
1349 &msock);
1350 if (mclient)
1351 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001352 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001353 msock = RPC_ANYSOCK;
1354 case IPPROTO_TCP:
1355 mclient = clnttcp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001356 pm_mnt.pm_prog,
1357 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001358 &msock, 0, 0);
1359 break;
1360 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001361 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001362 }
1363 if (!mclient) {
1364 if (!daemonized && prevt == 0)
1365 error_msg_rpc(clnt_spcreateerror(" "));
1366 } else {
1367 enum clnt_stat clnt_stat;
1368 /* try to mount hostname:pathname */
1369 mclient->cl_auth = authunix_create_default();
1370
1371 /* make pointers in xdr_mountres3 NULL so
1372 * that xdr_array allocates memory for us
1373 */
1374 memset(&status, 0, sizeof(status));
1375
Denis Vlasenkob9256052007-09-28 10:29:17 +00001376 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001377 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1378 (xdrproc_t) xdr_dirpath,
1379 (caddr_t) &pathname,
1380 (xdrproc_t) xdr_mountres3,
1381 (caddr_t) &status,
1382 total_timeout);
1383 else
1384 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1385 (xdrproc_t) xdr_dirpath,
1386 (caddr_t) &pathname,
1387 (xdrproc_t) xdr_fhstatus,
1388 (caddr_t) &status,
1389 total_timeout);
1390
1391 if (clnt_stat == RPC_SUCCESS)
1392 goto prepare_kernel_data; /* we're done */
1393 if (errno != ECONNREFUSED) {
1394 error_msg_rpc(clnt_sperror(mclient, " "));
1395 goto fail; /* don't retry */
1396 }
1397 /* Connection refused */
1398 if (!daemonized && prevt == 0) /* print just once */
1399 error_msg_rpc(clnt_sperror(mclient, " "));
1400 auth_destroy(mclient->cl_auth);
1401 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001402 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001403 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001404 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001405 }
1406
1407 /* Timeout. We are going to retry... maybe */
1408
1409 if (!bg)
1410 goto fail;
1411 if (!daemonized) {
1412 daemonized = daemonize();
1413 if (daemonized <= 0) { /* parent or error */
1414 retval = -daemonized;
1415 goto ret;
1416 }
1417 }
1418 prevt = t;
1419 t = time(NULL);
1420 if (t >= timeout)
1421 /* TODO error message */
1422 goto fail;
1423
1424 goto retry;
1425 }
1426
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001427 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001428
1429 if (nfsvers == 2) {
1430 if (status.nfsv2.fhs_status != 0) {
1431 bb_error_msg("%s:%s failed, reason given by server: %s",
1432 hostname, pathname,
1433 nfs_strerror(status.nfsv2.fhs_status));
1434 goto fail;
1435 }
1436 memcpy(data.root.data,
1437 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1438 NFS_FHSIZE);
1439 data.root.size = NFS_FHSIZE;
1440 memcpy(data.old_root.data,
1441 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1442 NFS_FHSIZE);
1443 } else {
1444 fhandle3 *my_fhandle;
1445 if (status.nfsv3.fhs_status != 0) {
1446 bb_error_msg("%s:%s failed, reason given by server: %s",
1447 hostname, pathname,
1448 nfs_strerror(status.nfsv3.fhs_status));
1449 goto fail;
1450 }
1451 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1452 memset(data.old_root.data, 0, NFS_FHSIZE);
1453 memset(&data.root, 0, sizeof(data.root));
1454 data.root.size = my_fhandle->fhandle3_len;
1455 memcpy(data.root.data,
1456 (char *) my_fhandle->fhandle3_val,
1457 my_fhandle->fhandle3_len);
1458
1459 data.flags |= NFS_MOUNT_VER3;
1460 }
1461
1462 /* create nfs socket for kernel */
1463
1464 if (tcp) {
1465 if (nfs_mount_version < 3) {
1466 bb_error_msg("NFS over TCP is not supported");
1467 goto fail;
1468 }
1469 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1470 } else
1471 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1472 if (fsock < 0) {
1473 bb_perror_msg("nfs socket");
1474 goto fail;
1475 }
1476 if (bindresvport(fsock, 0) < 0) {
1477 bb_perror_msg("nfs bindresvport");
1478 goto fail;
1479 }
1480 if (port == 0) {
1481 server_addr.sin_port = PMAPPORT;
1482 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1483 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1484 if (port == 0)
1485 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001486 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001487 server_addr.sin_port = htons(port);
1488
1489 /* prepare data structure for kernel */
1490
1491 data.fd = fsock;
1492 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1493 strncpy(data.hostname, hostname, sizeof(data.hostname));
1494
1495 /* clean up */
1496
1497 auth_destroy(mclient->cl_auth);
1498 clnt_destroy(mclient);
1499 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001500 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001501
1502 if (bg) {
1503 /* We must wait until mount directory is available */
1504 struct stat statbuf;
1505 int delay = 1;
1506 while (stat(mp->mnt_dir, &statbuf) == -1) {
1507 if (!daemonized) {
1508 daemonized = daemonize();
1509 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001510 // FIXME: parent doesn't close fsock - ??!
Denis Vlasenko25098f72006-09-14 15:46:33 +00001511 retval = -daemonized;
1512 goto ret;
1513 }
1514 }
1515 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1516 delay *= 2;
1517 if (delay > 30)
1518 delay = 30;
1519 }
1520 }
1521
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001522 do_mount: /* perform actual mount */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001523
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001524 mp->mnt_type = (char*)"nfs";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001525 retval = mount_it_now(mp, vfsflags, (char*)&data);
1526 goto ret;
1527
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001528 fail: /* abort */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001529
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001530 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001531 if (mclient) {
1532 auth_destroy(mclient->cl_auth);
1533 clnt_destroy(mclient);
1534 }
1535 close(msock);
1536 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001537 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001538 close(fsock);
1539
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001540 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001541 free(hostname);
1542 free(mounthost);
1543 free(filteropts);
1544 return retval;
1545}
1546
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001547#else /* !ENABLE_FEATURE_MOUNT_NFS */
1548
1549/* Never called. Call should be optimized out. */
Denis Vlasenkob4133682008-02-18 13:05:38 +00001550int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001551
1552#endif /* !ENABLE_FEATURE_MOUNT_NFS */
1553
1554// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1555// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001556// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001557static int singlemount(struct mntent *mp, int ignore_busy)
1558{
Denis Vlasenkob4133682008-02-18 13:05:38 +00001559 int rc = -1;
1560 long vfsflags;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001561 char *loopFile = 0, *filteropts = 0;
1562 llist_t *fl = 0;
1563 struct stat st;
1564
1565 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1566
1567 // Treat fstype "auto" as unspecified.
1568
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001569 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1570 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001571
Denis Vlasenko2535f122007-09-15 13:28:30 +00001572 // Might this be a virtual filesystem?
1573
1574 if (ENABLE_FEATURE_MOUNT_HELPERS
Denis Vlasenkoc03e8722007-12-26 20:56:55 +00001575 && (strchr(mp->mnt_fsname, '#'))
Denis Vlasenko2535f122007-09-15 13:28:30 +00001576 ) {
1577 char *s, *p, *args[35];
1578 int n = 0;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001579// FIXME: does it allow execution of arbitrary commands?!
1580// What args[0] can end up with?
Denis Vlasenko2535f122007-09-15 13:28:30 +00001581 for (s = p = mp->mnt_fsname; *s && n < 35-3; ++s) {
1582 if (s[0] == '#' && s[1] != '#') {
1583 *s = '\0';
1584 args[n++] = p;
1585 p = s + 1;
1586 }
1587 }
1588 args[n++] = p;
1589 args[n++] = mp->mnt_dir;
1590 args[n] = NULL;
1591 rc = wait4pid(xspawn(args));
1592 goto report_error;
1593 }
1594
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001595 // Might this be an CIFS filesystem?
1596
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001597 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001598 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1599 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1600 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001601 ) {
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001602 len_and_sockaddr *lsa;
1603 char *ip, *dotted;
1604 char *s;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001605
1606 rc = 1;
1607 // Replace '/' with '\' and verify that unc points to "//server/share".
1608
1609 for (s = mp->mnt_fsname; *s; ++s)
1610 if (*s == '/') *s = '\\';
1611
1612 // get server IP
1613
1614 s = strrchr(mp->mnt_fsname, '\\');
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001615 if (s <= mp->mnt_fsname+1) goto report_error;
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001616 *s = '\0';
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001617 lsa = host2sockaddr(mp->mnt_fsname+2, 0);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001618 *s = '\\';
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001619 if (!lsa) goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001620
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001621 // insert ip=... option into string flags.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001622
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001623 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001624 ip = xasprintf("ip=%s", dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001625 parse_mount_options(ip, &filteropts);
1626
1627 // compose new unc '\\server-ip\share'
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001628 // (s => slash after hostname)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001629
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001630 mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001631
1632 // lock is required
1633 vfsflags |= MS_MANDLOCK;
1634
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001635 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001636 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001637 if (ENABLE_FEATURE_CLEAN_UP) {
1638 free(mp->mnt_fsname);
1639 free(ip);
1640 free(dotted);
1641 free(lsa);
1642 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001643 goto report_error;
1644 }
1645
1646 // Might this be an NFS filesystem?
1647
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001648 if (ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001649 && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001650 && strchr(mp->mnt_fsname, ':') != NULL
1651 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001652 rc = nfsmount(mp, vfsflags, filteropts);
1653 goto report_error;
1654 }
1655
1656 // Look at the file. (Not found isn't a failure for remount, or for
1657 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001658 // (We use stat, not lstat, in order to allow
1659 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001660
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001661 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001662 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1663 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001664 // Do we need to allocate a loopback device for it?
1665
1666 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1667 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001668 mp->mnt_fsname = NULL; /* will receive malloced loop dev name */
1669 if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) {
1670 if (errno == EPERM || errno == EACCES)
1671 bb_error_msg(bb_msg_perm_denied_are_you_root);
1672 else
1673 bb_perror_msg("cannot setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001674 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001675 }
1676
1677 // Autodetect bind mounts
1678
1679 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1680 vfsflags |= MS_BIND;
1681 }
1682
1683 /* If we know the fstype (or don't need to), jump straight
1684 * to the actual mount. */
1685
1686 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1687 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001688 else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001689 // Loop through filesystem types until mount succeeds
1690 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001691
1692 /* Initialize list of block backed filesystems. This has to be
1693 * done here so that during "mount -a", mounts after /proc shows up
1694 * can autodetect. */
1695
1696 if (!fslist) {
1697 fslist = get_block_backed_filesystems();
1698 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1699 atexit(delete_block_backed_filesystems);
1700 }
1701
1702 for (fl = fslist; fl; fl = fl->link) {
1703 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001704 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001705 if (!rc) break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001706 }
1707 }
1708
1709 // If mount failed, clean up loop file (if any).
1710
1711 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1712 del_loop(mp->mnt_fsname);
1713 if (ENABLE_FEATURE_CLEAN_UP) {
1714 free(loopFile);
1715 free(mp->mnt_fsname);
1716 }
1717 }
1718
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001719 report_error:
1720 if (ENABLE_FEATURE_CLEAN_UP)
1721 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001722
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001723 if (errno == EBUSY && ignore_busy)
1724 return 0;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001725 if (rc < 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001726 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001727 return rc;
1728}
1729
1730// Parse options, if necessary parse fstab/mtab, and call singlemount for
1731// each directory to be mounted.
1732
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001733static const char must_be_root[] ALIGN1 = "you must be root";
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001734
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001735int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001736int mount_main(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001737{
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001738 char *cmdopts = xstrdup("");
1739 char *fstype = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001740 char *storage_path;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001741 char *opt_o;
1742 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001743 FILE *fstab;
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001744 int i, j, rc = 0;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001745 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001746 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001747 SKIP_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001748
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001749 USE_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00001750
Denis Vlasenkof732e962008-02-18 12:07:49 +00001751 // Parse long options, like --bind and --move. Note that -o option
1752 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001753 for (i = j = 1; argv[i]; i++) {
1754 if (argv[i][0] == '-' && argv[i][1] == '-')
1755 append_mount_options(&cmdopts, argv[i] + 2);
1756 else
1757 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001758 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001759 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001760
1761 // Parse remaining options
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001762 // Max 2 params; -v is a counter
1763 opt_complementary = "?2" USE_FEATURE_MOUNT_VERBOSE(":vv");
Denis Vlasenkof732e962008-02-18 12:07:49 +00001764 opt = getopt32(argv, OPTION_STR, &opt_o, &fstype
1765 USE_FEATURE_MOUNT_VERBOSE(, &verbose));
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00001766 if (opt & OPT_o) append_mount_options(&cmdopts, opt_o); // -o
1767 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
1768 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001769 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001770
1771 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001772 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00001773 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001774 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1775
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001776 if (!mountTable)
1777 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001778
Denis Vlasenko2535f122007-09-15 13:28:30 +00001779 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001780 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001781 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001782 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00001783 // util-linux 2.12a happily shows rootfs...
1784 //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001785
1786 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1787 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1788 mtpair->mnt_dir, mtpair->mnt_type,
1789 mtpair->mnt_opts);
1790 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001791 if (ENABLE_FEATURE_CLEAN_UP)
1792 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001793 return EXIT_SUCCESS;
1794 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001795 storage_path = NULL;
1796 } else {
1797 // When we have two arguments, the second is the directory and we can
1798 // skip looking at fstab entirely. We can always abspath() the directory
1799 // argument when we get it.
1800 if (argv[1]) {
1801 if (nonroot)
1802 bb_error_msg_and_die(must_be_root);
1803 mtpair->mnt_fsname = argv[0];
1804 mtpair->mnt_dir = argv[1];
1805 mtpair->mnt_type = fstype;
1806 mtpair->mnt_opts = cmdopts;
1807 if (ENABLE_FEATURE_MOUNT_LABEL) {
1808 resolve_mount_spec(&mtpair->mnt_fsname);
1809 }
1810 rc = singlemount(mtpair, 0);
1811 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00001812 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001813 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001814 }
1815
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001816 // Past this point, we are handling either "mount -a [opts]"
1817 // or "mount [opts] single_param"
1818
Denis Vlasenkob4133682008-02-18 13:05:38 +00001819 i = parse_mount_options(cmdopts, 0); // FIXME: should be "long", not "int"
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001820 if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
1821 bb_error_msg_and_die(must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00001822
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001823 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001824 if (ENABLE_FEATURE_MOUNT_FLAGS
1825 && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
1826 ) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001827 rc = verbose_mount(/*source:*/ "", /*target:*/ argv[0],
1828 /*type:*/ "", /*flags:*/ i, /*data:*/ "");
1829 if (rc)
1830 bb_simple_perror_msg_and_die(argv[0]);
1831 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001832 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001833
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001834 // Open either fstab or mtab
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001835 fstabname = "/etc/fstab";
1836 if (i & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001837 // WARNING. I am not sure this matches util-linux's
1838 // behavior. It's possible util-linux does not
1839 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001840 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001841 }
1842 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001843 if (!fstab)
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001844 bb_perror_msg_and_die("cannot read %s", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001845
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001846 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00001847 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001848 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001849 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001850
1851 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00001852 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001853 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001854 GETMNTENT_BUFSIZE/2)
1855 ) { // End of fstab/mtab is reached
1856 mtcur = mtother; // the thing we found last time
1857 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001858 }
1859
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001860 // If we're trying to mount something specific and this isn't it,
1861 // skip it. Note we must match the exact text in fstab (ala
1862 // "proc") or a full path from root
1863 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001864
1865 // Is this what we're looking for?
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001866 if (strcmp(argv[0], mtcur->mnt_fsname) &&
1867 strcmp(storage_path, mtcur->mnt_fsname) &&
1868 strcmp(argv[0], mtcur->mnt_dir) &&
1869 strcmp(storage_path, mtcur->mnt_dir)) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001870
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001871 // Remember this entry. Something later may have
1872 // overmounted it, and we want the _last_ match.
1873 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001874
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001875 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001876 } else {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001877 // Do we need to match a filesystem type?
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001878 if (fstype && match_fstype(mtcur, fstype))
1879 continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001880
1881 // Skip noauto and swap anyway.
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001882 if (parse_mount_options(mtcur->mnt_opts, 0) & (MOUNT_NOAUTO | MOUNT_SWAP))
1883 continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001884
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001885 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001886 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001887 if (nonroot)
1888 bb_error_msg_and_die(must_be_root);
1889
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001890 // Mount this thing
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001891 if (ENABLE_FEATURE_MOUNT_LABEL)
Denis Vlasenkode7684a2008-02-18 21:08:49 +00001892 resolve_mount_spec(&mtpair->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001893
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001894 // NFS mounts want this to be xrealloc-able
1895 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001896 if (singlemount(mtcur, 1)) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001897 // Count number of failed mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001898 rc++;
1899 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001900 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001901 }
1902 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001903
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001904 // End of fstab/mtab is reached.
1905 // Were we looking for something specific?
1906 if (argv[0]) {
1907 // If we didn't find anything, complain
1908 if (!mtcur->mnt_fsname)
1909 bb_error_msg_and_die("can't find %s in %s",
1910 argv[0], fstabname);
1911 if (nonroot) {
1912 // fstab must have "users" or "user"
1913 if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS))
1914 bb_error_msg_and_die(must_be_root);
1915 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001916
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001917 // Mount the last thing we found
1918 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1919 append_mount_options(&(mtcur->mnt_opts), cmdopts);
1920 if (ENABLE_FEATURE_MOUNT_LABEL) {
1921 resolve_mount_spec(&mtpair->mnt_fsname);
1922 }
1923 rc = singlemount(mtcur, 0);
1924 if (ENABLE_FEATURE_CLEAN_UP)
1925 free(mtcur->mnt_opts);
1926 }
1927
1928 if (ENABLE_FEATURE_CLEAN_UP)
1929 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001930 if (ENABLE_FEATURE_CLEAN_UP) {
1931 free(storage_path);
1932 free(cmdopts);
1933 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001934 return rc;
1935}