blob: 3cfbc5600fa70395b5f830f38734f0d31b379122 [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 Vlasenko63430ae2007-10-29 19:18:39 +000049static struct mntent *getmntent_r(FILE* stream, struct mntent* result, char* buffer, int bufsize)
Denis Vlasenko908d6b72006-12-18 23:07:42 +000050{
Denis Vlasenko908d6b72006-12-18 23:07:42 +000051 struct mntent* ment = getmntent(stream);
52 memcpy(result, ment, sizeof(struct mntent));
53 return result;
54}
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
Rob Landley6a6798b2005-08-10 20:35:54 +0000316 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000317 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000318 if (!strcasecmp(option_str, options)) {
319 long fl = mount_options[i];
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000320 if (fl < 0) flags &= fl;
Rob Landleydc0955b2006-03-14 18:16:25 +0000321 else flags |= fl;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000322 break;
323 }
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000324 option_str += strlen(option_str) + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000325 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000326 // If unrecognized not NULL, append unrecognized mount options */
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000327 if (unrecognized && i == ARRAY_SIZE(mount_options)) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000328 // Add it to strflags, to pass on to kernel
Rob Landleydc0955b2006-03-14 18:16:25 +0000329 i = *unrecognized ? strlen(*unrecognized) : 0;
330 *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000331
Rob Landley6a6798b2005-08-10 20:35:54 +0000332 // Comma separated if it's not the first one
Rob Landleydc0955b2006-03-14 18:16:25 +0000333 if (i) (*unrecognized)[i++] = ',';
334 strcpy((*unrecognized)+i, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000335 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000336
Denis Vlasenko2535f122007-09-15 13:28:30 +0000337 if (!comma)
338 break;
339 // Advance to next option
340 *comma = ',';
341 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000342 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000343
Rob Landleydc0955b2006-03-14 18:16:25 +0000344 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000345}
346
Rob Landleydc0955b2006-03-14 18:16:25 +0000347// Return a list of all block device backed filesystems
Matt Kraai12400822001-04-17 04:32:50 +0000348
Rob Landleydc0955b2006-03-14 18:16:25 +0000349static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000350{
Denis Vlasenko87468852007-04-13 23:22:00 +0000351 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000352 "/etc/filesystems",
353 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000354 };
355 char *fs, *buf;
Rob Landleydc0955b2006-03-14 18:16:25 +0000356 llist_t *list = 0;
357 int i;
358 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000359
Denis Vlasenko87468852007-04-13 23:22:00 +0000360 for (i = 0; i < 2; i++) {
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000361 f = fopen(filesystems[i], "r");
362 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000363
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000364 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000365 if (!strncmp(buf, "nodev", 5) && isspace(buf[5]))
366 continue;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000367 fs = skip_whitespace(buf);
Denis Vlasenko372686b2006-10-12 22:42:33 +0000368 if (*fs=='#' || *fs=='*' || !*fs) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000369
Denis Vlasenko372686b2006-10-12 22:42:33 +0000370 llist_add_to_end(&list, xstrdup(fs));
371 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000372 }
373 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
374 }
375
376 return list;
377}
378
Rob Landleydc0955b2006-03-14 18:16:25 +0000379#if ENABLE_FEATURE_CLEAN_UP
380static void delete_block_backed_filesystems(void)
381{
Rob Landleya6b5b602006-05-08 19:03:07 +0000382 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000383}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000384#else
385void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000386#endif
387
Rob Landleydc0955b2006-03-14 18:16:25 +0000388// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000389// NB: mp->xxx fields may be trashed on exit
Denis Vlasenkob4133682008-02-18 13:05:38 +0000390static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000391{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000392 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000393
Denis Vlasenkob4133682008-02-18 13:05:38 +0000394 if (fakeIt) {
395 if (verbose >= 2)
396 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
397 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
398 vfsflags, filteropts);
399 goto mtab;
400 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000401
Rob Landleydc0955b2006-03-14 18:16:25 +0000402 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000403 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000404 errno = 0;
405 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000406 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000407
408 // If mount failed, try
409 // helper program <mnt_type>
410 if (ENABLE_FEATURE_MOUNT_HELPERS && rc) {
411 char *args[6];
412 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000413 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000414 rc = 1;
415 if (filteropts) {
416 args[rc++] = (char *)"-o";
417 args[rc++] = filteropts;
418 }
419 args[rc++] = mp->mnt_fsname;
420 args[rc++] = mp->mnt_dir;
421 args[rc] = NULL;
422 rc = wait4pid(spawn(args));
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000423 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000424 if (!rc)
425 break;
426 errno = errno_save;
427 }
428
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000429 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000430 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000431 if (!(vfsflags & MS_SILENT))
432 bb_error_msg("%s is write-protected, mounting read-only",
433 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000434 vfsflags |= MS_RDONLY;
435 }
436
Rob Landleydc0955b2006-03-14 18:16:25 +0000437 // Abort entirely if permission denied.
438
439 if (rc && errno == EPERM)
440 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
441
442 /* If the mount was successful, and we're maintaining an old-style
443 * mtab file by hand, add the new entry to it now. */
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000444 mtab:
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000445 if (useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000446 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000447 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000448 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000449 int i;
450
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000451 if (!mountTable) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000452 bb_error_msg("no %s", bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000453 goto ret;
454 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000455
456 // Add vfs string flags
457
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000458 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
459 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
460 append_mount_options(&(mp->mnt_opts), option_str);
461 option_str += strlen(option_str) + 1;
462 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000463
464 // Remove trailing / (if any) from directory we mounted on
465
Denis Vlasenko727ef942006-09-14 13:19:19 +0000466 i = strlen(mp->mnt_dir) - 1;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000467 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000468
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000469 // Convert to canonical pathnames as needed
Denis Vlasenko727ef942006-09-14 13:19:19 +0000470
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000471 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000472 fsname = 0;
Denis Vlasenko727ef942006-09-14 13:19:19 +0000473 if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000474 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000475 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000476 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000477 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000478
479 // Write and close.
480
Denis Vlasenko727ef942006-09-14 13:19:19 +0000481 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000482 endmntent(mountTable);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000483 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000484 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000485 free(fsname);
486 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000487 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000488 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000489 return rc;
490}
491
Denis Vlasenko25098f72006-09-14 15:46:33 +0000492#if ENABLE_FEATURE_MOUNT_NFS
493
494/*
495 * Linux NFS mount
496 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
497 *
498 * Licensed under GPLv2, see file LICENSE in this tarball for details.
499 *
500 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
501 * numbers to be specified on the command line.
502 *
503 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
504 * Omit the call to connect() for Linux version 1.3.11 or later.
505 *
506 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
507 * Implemented the "bg", "fg" and "retry" mount options for NFS.
508 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000509 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000510 * - added Native Language Support
511 *
512 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
513 * plus NFSv3 stuff.
514 */
515
Denis Vlasenko25098f72006-09-14 15:46:33 +0000516/* This is just a warning of a common mistake. Possibly this should be a
517 * uclibc faq entry rather than in busybox... */
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000518#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000519#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
520#endif
521
522#define MOUNTPORT 635
523#define MNTPATHLEN 1024
524#define MNTNAMLEN 255
525#define FHSIZE 32
526#define FHSIZE3 64
527
528typedef char fhandle[FHSIZE];
529
530typedef struct {
531 unsigned int fhandle3_len;
532 char *fhandle3_val;
533} fhandle3;
534
535enum mountstat3 {
536 MNT_OK = 0,
537 MNT3ERR_PERM = 1,
538 MNT3ERR_NOENT = 2,
539 MNT3ERR_IO = 5,
540 MNT3ERR_ACCES = 13,
541 MNT3ERR_NOTDIR = 20,
542 MNT3ERR_INVAL = 22,
543 MNT3ERR_NAMETOOLONG = 63,
544 MNT3ERR_NOTSUPP = 10004,
545 MNT3ERR_SERVERFAULT = 10006,
546};
547typedef enum mountstat3 mountstat3;
548
549struct fhstatus {
550 unsigned int fhs_status;
551 union {
552 fhandle fhs_fhandle;
553 } fhstatus_u;
554};
555typedef struct fhstatus fhstatus;
556
557struct mountres3_ok {
558 fhandle3 fhandle;
559 struct {
560 unsigned int auth_flavours_len;
561 char *auth_flavours_val;
562 } auth_flavours;
563};
564typedef struct mountres3_ok mountres3_ok;
565
566struct mountres3 {
567 mountstat3 fhs_status;
568 union {
569 mountres3_ok mountinfo;
570 } mountres3_u;
571};
572typedef struct mountres3 mountres3;
573
574typedef char *dirpath;
575
576typedef char *name;
577
578typedef struct mountbody *mountlist;
579
580struct mountbody {
581 name ml_hostname;
582 dirpath ml_directory;
583 mountlist ml_next;
584};
585typedef struct mountbody mountbody;
586
587typedef struct groupnode *groups;
588
589struct groupnode {
590 name gr_name;
591 groups gr_next;
592};
593typedef struct groupnode groupnode;
594
595typedef struct exportnode *exports;
596
597struct exportnode {
598 dirpath ex_dir;
599 groups ex_groups;
600 exports ex_next;
601};
602typedef struct exportnode exportnode;
603
604struct ppathcnf {
605 int pc_link_max;
606 short pc_max_canon;
607 short pc_max_input;
608 short pc_name_max;
609 short pc_path_max;
610 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000611 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000612 char pc_xxx;
613 short pc_mask[2];
614};
615typedef struct ppathcnf ppathcnf;
616
617#define MOUNTPROG 100005
618#define MOUNTVERS 1
619
620#define MOUNTPROC_NULL 0
621#define MOUNTPROC_MNT 1
622#define MOUNTPROC_DUMP 2
623#define MOUNTPROC_UMNT 3
624#define MOUNTPROC_UMNTALL 4
625#define MOUNTPROC_EXPORT 5
626#define MOUNTPROC_EXPORTALL 6
627
628#define MOUNTVERS_POSIX 2
629
630#define MOUNTPROC_PATHCONF 7
631
632#define MOUNT_V3 3
633
634#define MOUNTPROC3_NULL 0
635#define MOUNTPROC3_MNT 1
636#define MOUNTPROC3_DUMP 2
637#define MOUNTPROC3_UMNT 3
638#define MOUNTPROC3_UMNTALL 4
639#define MOUNTPROC3_EXPORT 5
640
641enum {
642#ifndef NFS_FHSIZE
643 NFS_FHSIZE = 32,
644#endif
645#ifndef NFS_PORT
646 NFS_PORT = 2049
647#endif
648};
649
Denis Vlasenko25098f72006-09-14 15:46:33 +0000650/*
651 * We want to be able to compile mount on old kernels in such a way
652 * that the binary will work well on more recent kernels.
653 * Thus, if necessary we teach nfsmount.c the structure of new fields
654 * that will come later.
655 *
656 * Moreover, the new kernel includes conflict with glibc includes
657 * so it is easiest to ignore the kernel altogether (at compile time).
658 */
659
660struct nfs2_fh {
661 char data[32];
662};
663struct nfs3_fh {
664 unsigned short size;
665 unsigned char data[64];
666};
667
668struct nfs_mount_data {
669 int version; /* 1 */
670 int fd; /* 1 */
671 struct nfs2_fh old_root; /* 1 */
672 int flags; /* 1 */
673 int rsize; /* 1 */
674 int wsize; /* 1 */
675 int timeo; /* 1 */
676 int retrans; /* 1 */
677 int acregmin; /* 1 */
678 int acregmax; /* 1 */
679 int acdirmin; /* 1 */
680 int acdirmax; /* 1 */
681 struct sockaddr_in addr; /* 1 */
682 char hostname[256]; /* 1 */
683 int namlen; /* 2 */
684 unsigned int bsize; /* 3 */
685 struct nfs3_fh root; /* 4 */
686};
687
688/* bits in the flags field */
689enum {
690 NFS_MOUNT_SOFT = 0x0001, /* 1 */
691 NFS_MOUNT_INTR = 0x0002, /* 1 */
692 NFS_MOUNT_SECURE = 0x0004, /* 1 */
693 NFS_MOUNT_POSIX = 0x0008, /* 1 */
694 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
695 NFS_MOUNT_NOAC = 0x0020, /* 1 */
696 NFS_MOUNT_TCP = 0x0040, /* 2 */
697 NFS_MOUNT_VER3 = 0x0080, /* 3 */
698 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
699 NFS_MOUNT_NONLM = 0x0200 /* 3 */
700};
701
702
703/*
704 * We need to translate between nfs status return values and
705 * the local errno values which may not be the same.
706 *
707 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
708 * "after #include <errno.h> the symbol errno is reserved for any use,
709 * it cannot even be used as a struct tag or field name".
710 */
711
712#ifndef EDQUOT
713#define EDQUOT ENOSPC
714#endif
715
716// Convert each NFSERR_BLAH into EBLAH
717
718static const struct {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000719 short stat;
720 short errnum;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000721} nfs_errtbl[] = {
722 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
723 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
724 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
725 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
726};
727
728static char *nfs_strerror(int status)
729{
730 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000731
732 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
733 if (nfs_errtbl[i].stat == status)
734 return strerror(nfs_errtbl[i].errnum);
735 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000736 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000737}
738
739static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
740{
741 if (!xdr_opaque(xdrs, objp, FHSIZE))
742 return FALSE;
743 return TRUE;
744}
745
746static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
747{
748 if (!xdr_u_int(xdrs, &objp->fhs_status))
749 return FALSE;
750 switch (objp->fhs_status) {
751 case 0:
752 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
753 return FALSE;
754 break;
755 default:
756 break;
757 }
758 return TRUE;
759}
760
761static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
762{
763 if (!xdr_string(xdrs, objp, MNTPATHLEN))
764 return FALSE;
765 return TRUE;
766}
767
768static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
769{
770 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
771 return FALSE;
772 return TRUE;
773}
774
775static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
776{
777 if (!xdr_fhandle3(xdrs, &objp->fhandle))
778 return FALSE;
779 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
780 sizeof (int), (xdrproc_t) xdr_int))
781 return FALSE;
782 return TRUE;
783}
784
785static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
786{
787 if (!xdr_enum(xdrs, (enum_t *) objp))
788 return FALSE;
789 return TRUE;
790}
791
792static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
793{
794 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
795 return FALSE;
796 switch (objp->fhs_status) {
797 case MNT_OK:
798 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
799 return FALSE;
800 break;
801 default:
802 break;
803 }
804 return TRUE;
805}
806
807#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
808
Denis Vlasenko25098f72006-09-14 15:46:33 +0000809/*
810 * Unfortunately, the kernel prints annoying console messages
811 * in case of an unexpected nfs mount version (instead of
812 * just returning some error). Therefore we'll have to try
813 * and figure out what version the kernel expects.
814 *
815 * Variables:
816 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
817 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
818 * nfs_mount_version: version this source and running kernel can handle
819 */
820static void
821find_kernel_nfs_mount_version(void)
822{
Denis Vlasenkob9256052007-09-28 10:29:17 +0000823 int kernel_version;
824
825 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000826 return;
827
828 nfs_mount_version = 4; /* default */
829
830 kernel_version = get_linux_version_code();
831 if (kernel_version) {
832 if (kernel_version < KERNEL_VERSION(2,1,32))
833 nfs_mount_version = 1;
834 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
835 (kernel_version >= KERNEL_VERSION(2,3,0) &&
836 kernel_version < KERNEL_VERSION(2,3,99)))
837 nfs_mount_version = 3;
838 /* else v4 since 2.3.99pre4 */
839 }
840}
841
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +0000842static void
Denis Vlasenkob9256052007-09-28 10:29:17 +0000843get_mountport(struct pmap *pm_mnt,
844 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +0000845 long unsigned prog,
846 long unsigned version,
847 long unsigned proto,
848 long unsigned port)
849{
850 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000851
852 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +0000853/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
854 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000855 pmap = pmap_getmaps(server_addr);
856
857 if (version > MAX_NFSPROT)
858 version = MAX_NFSPROT;
859 if (!prog)
860 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000861 pm_mnt->pm_prog = prog;
862 pm_mnt->pm_vers = version;
863 pm_mnt->pm_prot = proto;
864 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000865
Denis Vlasenko25098f72006-09-14 15:46:33 +0000866 while (pmap) {
867 if (pmap->pml_map.pm_prog != prog)
868 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000869 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000870 goto next;
871 if (version > 2 && pmap->pml_map.pm_vers != version)
872 goto next;
873 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
874 goto next;
875 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
Denis Vlasenkob9256052007-09-28 10:29:17 +0000876 (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto) ||
Denis Vlasenko25098f72006-09-14 15:46:33 +0000877 (port && pmap->pml_map.pm_port != port))
878 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000879 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
880 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +0000881 pmap = pmap->pml_next;
882 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000883 if (!pm_mnt->pm_vers)
884 pm_mnt->pm_vers = MOUNTVERS;
885 if (!pm_mnt->pm_port)
886 pm_mnt->pm_port = MOUNTPORT;
887 if (!pm_mnt->pm_prot)
888 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000889}
890
Denis Vlasenkof0000652007-09-04 18:30:26 +0000891#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +0000892static int daemonize(void)
893{
894 int fd;
895 int pid = fork();
896 if (pid < 0) /* error */
897 return -errno;
898 if (pid > 0) /* parent */
899 return 0;
900 /* child */
901 fd = xopen(bb_dev_null, O_RDWR);
902 dup2(fd, 0);
903 dup2(fd, 1);
904 dup2(fd, 2);
Denis Vlasenko9af7c9d2007-01-19 21:19:35 +0000905 while (fd > 2) close(fd--);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000906 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +0000907 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000908 logmode = LOGMODE_SYSLOG;
909 return 1;
910}
Denis Vlasenkof0000652007-09-04 18:30:26 +0000911#else
912static inline int daemonize(void) { return -ENOSYS; }
913#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000914
915// TODO
Denis Vlasenko68404f12008-03-17 09:00:54 +0000916static inline int we_saw_this_host_before(const char *hostname ATTRIBUTE_UNUSED)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000917{
918 return 0;
919}
920
921/* RPC strerror analogs are terminally idiotic:
922 * *mandatory* prefix and \n at end.
923 * This hopefully helps. Usage:
924 * error_msg_rpc(clnt_*error*(" ")) */
925static void error_msg_rpc(const char *msg)
926{
Denis Vlasenko23514fe2006-09-19 14:07:52 +0000927 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000928 while (msg[0] == ' ' || msg[0] == ':') msg++;
929 len = strlen(msg);
930 while (len && msg[len-1] == '\n') len--;
931 bb_error_msg("%.*s", len, msg);
932}
933
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +0000934// NB: mp->xxx fields may be trashed on exit
Denis Vlasenkob4133682008-02-18 13:05:38 +0000935static int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000936{
937 CLIENT *mclient;
938 char *hostname;
939 char *pathname;
940 char *mounthost;
941 struct nfs_mount_data data;
942 char *opt;
943 struct hostent *hp;
944 struct sockaddr_in server_addr;
945 struct sockaddr_in mount_server_addr;
946 int msock, fsock;
947 union {
948 struct fhstatus nfsv2;
949 struct mountres3 nfsv3;
950 } status;
951 int daemonized;
952 char *s;
953 int port;
954 int mountport;
955 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +0000956#if BB_MMU
957 int bg = 0;
958#else
959 enum { bg = 0 };
960#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000961 int soft;
962 int intr;
963 int posix;
964 int nocto;
965 int noac;
966 int nolock;
967 int retry;
968 int tcp;
969 int mountprog;
970 int mountvers;
971 int nfsprog;
972 int nfsvers;
973 int retval;
974
975 find_kernel_nfs_mount_version();
976
977 daemonized = 0;
978 mounthost = NULL;
979 retval = ETIMEDOUT;
980 msock = fsock = -1;
981 mclient = NULL;
982
983 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
984
985 filteropts = xstrdup(filteropts); /* going to trash it later... */
986
987 hostname = xstrdup(mp->mnt_fsname);
988 /* mount_main() guarantees that ':' is there */
989 s = strchr(hostname, ':');
990 pathname = s + 1;
991 *s = '\0';
992 /* Ignore all but first hostname in replicated mounts
993 until they can be fully supported. (mack@sgi.com) */
994 s = strchr(hostname, ',');
995 if (s) {
996 *s = '\0';
997 bb_error_msg("warning: multiple hostnames not supported");
998 }
999
1000 server_addr.sin_family = AF_INET;
1001 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1002 hp = gethostbyname(hostname);
1003 if (hp == NULL) {
1004 bb_herror_msg("%s", hostname);
1005 goto fail;
1006 }
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001007 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001008 bb_error_msg("got bad hp->h_length");
1009 hp->h_length = sizeof(struct in_addr);
1010 }
1011 memcpy(&server_addr.sin_addr,
1012 hp->h_addr, hp->h_length);
1013 }
1014
1015 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1016
1017 /* add IP address to mtab options for use when unmounting */
1018
1019 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1020 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1021 } else {
1022 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1023 mp->mnt_opts[0] ? "," : "",
1024 inet_ntoa(server_addr.sin_addr));
1025 free(mp->mnt_opts);
1026 mp->mnt_opts = tmp;
1027 }
1028
1029 /* Set default options.
1030 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1031 * let the kernel decide.
1032 * timeo is filled in after we know whether it'll be TCP or UDP. */
1033 memset(&data, 0, sizeof(data));
1034 data.retrans = 3;
1035 data.acregmin = 3;
1036 data.acregmax = 60;
1037 data.acdirmin = 30;
1038 data.acdirmax = 60;
1039 data.namlen = NAME_MAX;
1040
Denis Vlasenko25098f72006-09-14 15:46:33 +00001041 soft = 0;
1042 intr = 0;
1043 posix = 0;
1044 nocto = 0;
1045 nolock = 0;
1046 noac = 0;
1047 retry = 10000; /* 10000 minutes ~ 1 week */
1048 tcp = 0;
1049
1050 mountprog = MOUNTPROG;
1051 mountvers = 0;
1052 port = 0;
1053 mountport = 0;
1054 nfsprog = 100003;
1055 nfsvers = 0;
1056
1057 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001058 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001059 char *opteq = strchr(opt, '=');
1060 if (opteq) {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001061 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001062 /* 0 */ "rsize\0"
1063 /* 1 */ "wsize\0"
1064 /* 2 */ "timeo\0"
1065 /* 3 */ "retrans\0"
1066 /* 4 */ "acregmin\0"
1067 /* 5 */ "acregmax\0"
1068 /* 6 */ "acdirmin\0"
1069 /* 7 */ "acdirmax\0"
1070 /* 8 */ "actimeo\0"
1071 /* 9 */ "retry\0"
1072 /* 10 */ "port\0"
1073 /* 11 */ "mountport\0"
1074 /* 12 */ "mounthost\0"
1075 /* 13 */ "mountprog\0"
1076 /* 14 */ "mountvers\0"
1077 /* 15 */ "nfsprog\0"
1078 /* 16 */ "nfsvers\0"
1079 /* 17 */ "vers\0"
1080 /* 18 */ "proto\0"
1081 /* 19 */ "namlen\0"
1082 /* 20 */ "addr\0";
Denis Vlasenko13858992006-10-08 12:49:22 +00001083 int val = xatoi_u(opteq + 1);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001084 *opteq = '\0';
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001085 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001086 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001087 data.rsize = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001088 break;
1089 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001090 data.wsize = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001091 break;
1092 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001093 data.timeo = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001094 break;
1095 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001096 data.retrans = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001097 break;
1098 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001099 data.acregmin = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001100 break;
1101 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001102 data.acregmax = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001103 break;
1104 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001105 data.acdirmin = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001106 break;
1107 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001108 data.acdirmax = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001109 break;
1110 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001111 data.acregmin = val;
1112 data.acregmax = val;
1113 data.acdirmin = val;
1114 data.acdirmax = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001115 break;
1116 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001117 retry = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001118 break;
1119 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001120 port = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001121 break;
1122 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001123 mountport = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001124 break;
1125 case 12: // "mounthost"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001126 mounthost = xstrndup(opteq+1,
Denis Vlasenko5af906e2006-11-05 18:05:09 +00001127 strcspn(opteq+1," \t\n\r,"));
Denis Vlasenko68f21872006-10-26 01:47:34 +00001128 break;
1129 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001130 mountprog = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001131 break;
1132 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001133 mountvers = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001134 break;
1135 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001136 nfsprog = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001137 break;
1138 case 16: // "nfsvers"
1139 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001140 nfsvers = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001141 break;
1142 case 18: // "proto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001143 if (!strncmp(opteq+1, "tcp", 3))
1144 tcp = 1;
1145 else if (!strncmp(opteq+1, "udp", 3))
1146 tcp = 0;
1147 else
1148 bb_error_msg("warning: unrecognized proto= option");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001149 break;
1150 case 19: // "namlen"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001151 if (nfs_mount_version >= 2)
1152 data.namlen = val;
1153 else
1154 bb_error_msg("warning: option namlen is not supported\n");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001155 break;
1156 case 20: // "addr" - ignore
1157 break;
1158 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001159 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1160 goto fail;
1161 }
1162 }
1163 else {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001164 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001165 "bg\0"
1166 "fg\0"
1167 "soft\0"
1168 "hard\0"
1169 "intr\0"
1170 "posix\0"
1171 "cto\0"
1172 "ac\0"
1173 "tcp\0"
1174 "udp\0"
1175 "lock\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001176 int val = 1;
1177 if (!strncmp(opt, "no", 2)) {
1178 val = 0;
1179 opt += 2;
1180 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001181 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001182 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001183#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001184 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001185#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001186 break;
1187 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001188#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001189 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001190#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001191 break;
1192 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001193 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001194 break;
1195 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001196 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001197 break;
1198 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001199 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001200 break;
1201 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001202 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001203 break;
1204 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001205 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001206 break;
1207 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001208 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001209 break;
1210 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001211 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001212 break;
1213 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001214 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001215 break;
1216 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001217 if (nfs_mount_version >= 3)
1218 nolock = !val;
1219 else
1220 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001221 break;
1222 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001223 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1224 goto fail;
1225 }
1226 }
1227 }
1228 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1229
1230 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1231 | (intr ? NFS_MOUNT_INTR : 0)
1232 | (posix ? NFS_MOUNT_POSIX : 0)
1233 | (nocto ? NFS_MOUNT_NOCTO : 0)
1234 | (noac ? NFS_MOUNT_NOAC : 0);
1235 if (nfs_mount_version >= 2)
1236 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1237 if (nfs_mount_version >= 3)
1238 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1239 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1240 bb_error_msg("NFSv%d not supported", nfsvers);
1241 goto fail;
1242 }
1243 if (nfsvers && !mountvers)
1244 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1245 if (nfsvers && nfsvers < mountvers) {
1246 mountvers = nfsvers;
1247 }
1248
1249 /* Adjust options if none specified */
1250 if (!data.timeo)
1251 data.timeo = tcp ? 70 : 7;
1252
Denis Vlasenko25098f72006-09-14 15:46:33 +00001253 data.version = nfs_mount_version;
1254
1255 if (vfsflags & MS_REMOUNT)
1256 goto do_mount;
1257
1258 /*
1259 * If the previous mount operation on the same host was
1260 * backgrounded, and the "bg" for this mount is also set,
1261 * give up immediately, to avoid the initial timeout.
1262 */
1263 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001264 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001265 if (daemonized <= 0) { /* parent or error */
1266 retval = -daemonized;
1267 goto ret;
1268 }
1269 }
1270
1271 /* create mount daemon client */
1272 /* See if the nfs host = mount host. */
1273 if (mounthost) {
1274 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1275 mount_server_addr.sin_family = AF_INET;
1276 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1277 } else {
1278 hp = gethostbyname(mounthost);
1279 if (hp == NULL) {
1280 bb_herror_msg("%s", mounthost);
1281 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001282 }
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001283 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
1284 bb_error_msg("got bad hp->h_length");
1285 hp->h_length = sizeof(struct in_addr);
1286 }
1287 mount_server_addr.sin_family = AF_INET;
1288 memcpy(&mount_server_addr.sin_addr,
1289 hp->h_addr, hp->h_length);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001290 }
1291 }
1292
1293 /*
1294 * The following loop implements the mount retries. When the mount
1295 * times out, and the "bg" option is set, we background ourself
1296 * and continue trying.
1297 *
1298 * The case where the mount point is not present and the "bg"
1299 * option is set, is treated as a timeout. This is done to
1300 * support nested mounts.
1301 *
1302 * The "retry" count specified by the user is the number of
1303 * minutes to retry before giving up.
1304 */
1305 {
1306 struct timeval total_timeout;
1307 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001308 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001309 time_t t;
1310 time_t prevt;
1311 time_t timeout;
1312
1313 retry_timeout.tv_sec = 3;
1314 retry_timeout.tv_usec = 0;
1315 total_timeout.tv_sec = 20;
1316 total_timeout.tv_usec = 0;
1317 timeout = time(NULL) + 60 * retry;
1318 prevt = 0;
1319 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001320 retry:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001321 /* be careful not to use too many CPU cycles */
1322 if (t - prevt < 30)
1323 sleep(30);
1324
Denis Vlasenkob9256052007-09-28 10:29:17 +00001325 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001326 mountprog,
1327 mountvers,
1328 proto,
1329 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001330 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001331
1332 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001333 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001334 msock = RPC_ANYSOCK;
1335
Denis Vlasenkob9256052007-09-28 10:29:17 +00001336 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001337 case IPPROTO_UDP:
1338 mclient = clntudp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001339 pm_mnt.pm_prog,
1340 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001341 retry_timeout,
1342 &msock);
1343 if (mclient)
1344 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001345 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001346 msock = RPC_ANYSOCK;
1347 case IPPROTO_TCP:
1348 mclient = clnttcp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001349 pm_mnt.pm_prog,
1350 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001351 &msock, 0, 0);
1352 break;
1353 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001354 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001355 }
1356 if (!mclient) {
1357 if (!daemonized && prevt == 0)
1358 error_msg_rpc(clnt_spcreateerror(" "));
1359 } else {
1360 enum clnt_stat clnt_stat;
1361 /* try to mount hostname:pathname */
1362 mclient->cl_auth = authunix_create_default();
1363
1364 /* make pointers in xdr_mountres3 NULL so
1365 * that xdr_array allocates memory for us
1366 */
1367 memset(&status, 0, sizeof(status));
1368
Denis Vlasenkob9256052007-09-28 10:29:17 +00001369 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001370 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1371 (xdrproc_t) xdr_dirpath,
1372 (caddr_t) &pathname,
1373 (xdrproc_t) xdr_mountres3,
1374 (caddr_t) &status,
1375 total_timeout);
1376 else
1377 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1378 (xdrproc_t) xdr_dirpath,
1379 (caddr_t) &pathname,
1380 (xdrproc_t) xdr_fhstatus,
1381 (caddr_t) &status,
1382 total_timeout);
1383
1384 if (clnt_stat == RPC_SUCCESS)
1385 goto prepare_kernel_data; /* we're done */
1386 if (errno != ECONNREFUSED) {
1387 error_msg_rpc(clnt_sperror(mclient, " "));
1388 goto fail; /* don't retry */
1389 }
1390 /* Connection refused */
1391 if (!daemonized && prevt == 0) /* print just once */
1392 error_msg_rpc(clnt_sperror(mclient, " "));
1393 auth_destroy(mclient->cl_auth);
1394 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001395 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001396 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001397 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001398 }
1399
1400 /* Timeout. We are going to retry... maybe */
1401
1402 if (!bg)
1403 goto fail;
1404 if (!daemonized) {
1405 daemonized = daemonize();
1406 if (daemonized <= 0) { /* parent or error */
1407 retval = -daemonized;
1408 goto ret;
1409 }
1410 }
1411 prevt = t;
1412 t = time(NULL);
1413 if (t >= timeout)
1414 /* TODO error message */
1415 goto fail;
1416
1417 goto retry;
1418 }
1419
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001420 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001421
1422 if (nfsvers == 2) {
1423 if (status.nfsv2.fhs_status != 0) {
1424 bb_error_msg("%s:%s failed, reason given by server: %s",
1425 hostname, pathname,
1426 nfs_strerror(status.nfsv2.fhs_status));
1427 goto fail;
1428 }
1429 memcpy(data.root.data,
1430 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1431 NFS_FHSIZE);
1432 data.root.size = NFS_FHSIZE;
1433 memcpy(data.old_root.data,
1434 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1435 NFS_FHSIZE);
1436 } else {
1437 fhandle3 *my_fhandle;
1438 if (status.nfsv3.fhs_status != 0) {
1439 bb_error_msg("%s:%s failed, reason given by server: %s",
1440 hostname, pathname,
1441 nfs_strerror(status.nfsv3.fhs_status));
1442 goto fail;
1443 }
1444 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1445 memset(data.old_root.data, 0, NFS_FHSIZE);
1446 memset(&data.root, 0, sizeof(data.root));
1447 data.root.size = my_fhandle->fhandle3_len;
1448 memcpy(data.root.data,
1449 (char *) my_fhandle->fhandle3_val,
1450 my_fhandle->fhandle3_len);
1451
1452 data.flags |= NFS_MOUNT_VER3;
1453 }
1454
1455 /* create nfs socket for kernel */
1456
1457 if (tcp) {
1458 if (nfs_mount_version < 3) {
1459 bb_error_msg("NFS over TCP is not supported");
1460 goto fail;
1461 }
1462 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1463 } else
1464 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1465 if (fsock < 0) {
1466 bb_perror_msg("nfs socket");
1467 goto fail;
1468 }
1469 if (bindresvport(fsock, 0) < 0) {
1470 bb_perror_msg("nfs bindresvport");
1471 goto fail;
1472 }
1473 if (port == 0) {
1474 server_addr.sin_port = PMAPPORT;
1475 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1476 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1477 if (port == 0)
1478 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001479 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001480 server_addr.sin_port = htons(port);
1481
1482 /* prepare data structure for kernel */
1483
1484 data.fd = fsock;
1485 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1486 strncpy(data.hostname, hostname, sizeof(data.hostname));
1487
1488 /* clean up */
1489
1490 auth_destroy(mclient->cl_auth);
1491 clnt_destroy(mclient);
1492 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001493 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001494
1495 if (bg) {
1496 /* We must wait until mount directory is available */
1497 struct stat statbuf;
1498 int delay = 1;
1499 while (stat(mp->mnt_dir, &statbuf) == -1) {
1500 if (!daemonized) {
1501 daemonized = daemonize();
1502 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001503 // FIXME: parent doesn't close fsock - ??!
Denis Vlasenko25098f72006-09-14 15:46:33 +00001504 retval = -daemonized;
1505 goto ret;
1506 }
1507 }
1508 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1509 delay *= 2;
1510 if (delay > 30)
1511 delay = 30;
1512 }
1513 }
1514
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001515 do_mount: /* perform actual mount */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001516
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001517 mp->mnt_type = (char*)"nfs";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001518 retval = mount_it_now(mp, vfsflags, (char*)&data);
1519 goto ret;
1520
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001521 fail: /* abort */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001522
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001523 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001524 if (mclient) {
1525 auth_destroy(mclient->cl_auth);
1526 clnt_destroy(mclient);
1527 }
1528 close(msock);
1529 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001530 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001531 close(fsock);
1532
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001533 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001534 free(hostname);
1535 free(mounthost);
1536 free(filteropts);
1537 return retval;
1538}
1539
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001540#else /* !ENABLE_FEATURE_MOUNT_NFS */
1541
1542/* Never called. Call should be optimized out. */
Denis Vlasenkob4133682008-02-18 13:05:38 +00001543int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001544
1545#endif /* !ENABLE_FEATURE_MOUNT_NFS */
1546
1547// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1548// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001549// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001550static int singlemount(struct mntent *mp, int ignore_busy)
1551{
Denis Vlasenkob4133682008-02-18 13:05:38 +00001552 int rc = -1;
1553 long vfsflags;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001554 char *loopFile = 0, *filteropts = 0;
1555 llist_t *fl = 0;
1556 struct stat st;
1557
1558 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1559
1560 // Treat fstype "auto" as unspecified.
1561
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001562 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1563 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001564
Denis Vlasenko2535f122007-09-15 13:28:30 +00001565 // Might this be a virtual filesystem?
1566
1567 if (ENABLE_FEATURE_MOUNT_HELPERS
Denis Vlasenkoc03e8722007-12-26 20:56:55 +00001568 && (strchr(mp->mnt_fsname, '#'))
Denis Vlasenko2535f122007-09-15 13:28:30 +00001569 ) {
1570 char *s, *p, *args[35];
1571 int n = 0;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001572// FIXME: does it allow execution of arbitrary commands?!
1573// What args[0] can end up with?
Denis Vlasenko2535f122007-09-15 13:28:30 +00001574 for (s = p = mp->mnt_fsname; *s && n < 35-3; ++s) {
1575 if (s[0] == '#' && s[1] != '#') {
1576 *s = '\0';
1577 args[n++] = p;
1578 p = s + 1;
1579 }
1580 }
1581 args[n++] = p;
1582 args[n++] = mp->mnt_dir;
1583 args[n] = NULL;
1584 rc = wait4pid(xspawn(args));
1585 goto report_error;
1586 }
1587
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001588 // Might this be an CIFS filesystem?
1589
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001590 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001591 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1592 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1593 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001594 ) {
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001595 len_and_sockaddr *lsa;
1596 char *ip, *dotted;
1597 char *s;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001598
1599 rc = 1;
1600 // Replace '/' with '\' and verify that unc points to "//server/share".
1601
1602 for (s = mp->mnt_fsname; *s; ++s)
1603 if (*s == '/') *s = '\\';
1604
1605 // get server IP
1606
1607 s = strrchr(mp->mnt_fsname, '\\');
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001608 if (s <= mp->mnt_fsname+1) goto report_error;
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001609 *s = '\0';
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001610 lsa = host2sockaddr(mp->mnt_fsname+2, 0);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001611 *s = '\\';
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001612 if (!lsa) goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001613
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001614 // insert ip=... option into string flags.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001615
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001616 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001617 ip = xasprintf("ip=%s", dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001618 parse_mount_options(ip, &filteropts);
1619
1620 // compose new unc '\\server-ip\share'
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001621 // (s => slash after hostname)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001622
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001623 mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001624
1625 // lock is required
1626 vfsflags |= MS_MANDLOCK;
1627
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001628 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001629 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001630 if (ENABLE_FEATURE_CLEAN_UP) {
1631 free(mp->mnt_fsname);
1632 free(ip);
1633 free(dotted);
1634 free(lsa);
1635 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001636 goto report_error;
1637 }
1638
1639 // Might this be an NFS filesystem?
1640
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001641 if (ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001642 && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001643 && strchr(mp->mnt_fsname, ':') != NULL
1644 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001645 rc = nfsmount(mp, vfsflags, filteropts);
1646 goto report_error;
1647 }
1648
1649 // Look at the file. (Not found isn't a failure for remount, or for
1650 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001651 // (We use stat, not lstat, in order to allow
1652 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001653
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001654 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001655 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1656 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001657 // Do we need to allocate a loopback device for it?
1658
1659 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1660 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001661 mp->mnt_fsname = NULL; /* will receive malloced loop dev name */
1662 if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) {
1663 if (errno == EPERM || errno == EACCES)
1664 bb_error_msg(bb_msg_perm_denied_are_you_root);
1665 else
1666 bb_perror_msg("cannot setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001667 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001668 }
1669
1670 // Autodetect bind mounts
1671
1672 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1673 vfsflags |= MS_BIND;
1674 }
1675
1676 /* If we know the fstype (or don't need to), jump straight
1677 * to the actual mount. */
1678
1679 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1680 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001681 else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001682 // Loop through filesystem types until mount succeeds
1683 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001684
1685 /* Initialize list of block backed filesystems. This has to be
1686 * done here so that during "mount -a", mounts after /proc shows up
1687 * can autodetect. */
1688
1689 if (!fslist) {
1690 fslist = get_block_backed_filesystems();
1691 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1692 atexit(delete_block_backed_filesystems);
1693 }
1694
1695 for (fl = fslist; fl; fl = fl->link) {
1696 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001697 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001698 if (!rc) break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001699 }
1700 }
1701
1702 // If mount failed, clean up loop file (if any).
1703
1704 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1705 del_loop(mp->mnt_fsname);
1706 if (ENABLE_FEATURE_CLEAN_UP) {
1707 free(loopFile);
1708 free(mp->mnt_fsname);
1709 }
1710 }
1711
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001712 report_error:
1713 if (ENABLE_FEATURE_CLEAN_UP)
1714 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001715
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001716 if (errno == EBUSY && ignore_busy)
1717 return 0;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001718 if (rc < 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001719 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001720 return rc;
1721}
1722
1723// Parse options, if necessary parse fstab/mtab, and call singlemount for
1724// each directory to be mounted.
1725
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001726static const char must_be_root[] ALIGN1 = "you must be root";
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001727
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001728int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001729int mount_main(int argc, char **argv)
1730{
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001731 char *cmdopts = xstrdup("");
1732 char *fstype = NULL;
1733 char *storage_path = NULL;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001734 char *opt_o;
1735 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001736 FILE *fstab;
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001737 int i, j, rc = 0;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001738 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001739 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001740 SKIP_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001741
Denis Vlasenkob4133682008-02-18 13:05:38 +00001742 USE_DESKTOP( int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00001743
Denis Vlasenkof732e962008-02-18 12:07:49 +00001744 // Parse long options, like --bind and --move. Note that -o option
1745 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001746
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001747 for (i = j = 0; i < argc; i++) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001748 if (argv[i][0] == '-' && argv[i][1] == '-') {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001749 append_mount_options(&cmdopts, argv[i]+2);
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001750 } else argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001751 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001752 argv[j] = NULL;
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001753 argc = j;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001754
1755 // Parse remaining options
1756
Denis Vlasenkof732e962008-02-18 12:07:49 +00001757#if ENABLE_FEATURE_MOUNT_VERBOSE
1758 opt_complementary = "vv"; // -v is a counter
1759#endif
1760 opt = getopt32(argv, OPTION_STR, &opt_o, &fstype
1761 USE_FEATURE_MOUNT_VERBOSE(, &verbose));
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00001762 if (opt & OPT_o) append_mount_options(&cmdopts, opt_o); // -o
1763 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
1764 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001765 argv += optind;
1766 argc -= optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001767
1768 // Three or more non-option arguments? Die with a usage message.
1769
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001770 if (argc > 2) bb_show_usage();
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001771
1772 // If we have no arguments, show currently mounted filesystems
1773
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001774 if (!argc) {
Denis Vlasenko397de612008-03-17 08:55:44 +00001775 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001776 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1777
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001778 if (!mountTable) bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001779
Denis Vlasenko2535f122007-09-15 13:28:30 +00001780 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001781 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001782 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001783 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00001784 // util-linux 2.12a happily shows rootfs...
1785 //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001786
1787 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1788 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1789 mtpair->mnt_dir, mtpair->mnt_type,
1790 mtpair->mnt_opts);
1791 }
1792 if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
1793 return EXIT_SUCCESS;
1794 }
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001795 } else storage_path = bb_simplify_path(argv[0]);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001796
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
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001801 if (argc == 2) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001802 if (nonroot)
1803 bb_error_msg_and_die(must_be_root);
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001804 mtpair->mnt_fsname = argv[0];
1805 mtpair->mnt_dir = argv[1];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001806 mtpair->mnt_type = fstype;
1807 mtpair->mnt_opts = cmdopts;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00001808 if (ENABLE_FEATURE_MOUNT_LABEL) {
1809 resolve_mount_spec(&mtpair->mnt_fsname);
1810 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001811 rc = singlemount(mtpair, 0);
1812 goto clean_up;
1813 }
1814
Denis Vlasenkob4133682008-02-18 13:05:38 +00001815 i = parse_mount_options(cmdopts, 0); // FIXME: should be "long", not "int"
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001816 if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
1817 bb_error_msg_and_die(must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00001818
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001819 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko546cd182006-10-02 18:52:49 +00001820
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001821 if (ENABLE_FEATURE_MOUNT_FLAGS
1822 && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
1823 ) {
Denis Vlasenkof732e962008-02-18 12:07:49 +00001824 rc = verbose_mount("", argv[0], "", i, "");
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +00001825 if (rc) bb_simple_perror_msg_and_die(argv[0]);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001826 goto clean_up;
1827 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001828
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001829 // Open either fstab or mtab
1830
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001831 fstabname = "/etc/fstab";
1832 if (i & MS_REMOUNT) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001833 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001834 }
1835 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001836 if (!fstab)
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001837 bb_perror_msg_and_die("cannot read %s", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001838
1839 // Loop through entries until we find what we're looking for.
1840
Denis Vlasenko546cd182006-10-02 18:52:49 +00001841 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001842 for (;;) {
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001843 struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001844
1845 // Get next fstab entry
1846
Denis Vlasenko2535f122007-09-15 13:28:30 +00001847 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001848 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
1849 GETMNTENT_BUFSIZE/2))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001850 {
1851 // Were we looking for something specific?
1852
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001853 if (argc) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001854
1855 // If we didn't find anything, complain.
1856
1857 if (!mtnext->mnt_fsname)
1858 bb_error_msg_and_die("can't find %s in %s",
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001859 argv[0], fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001860
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001861 mtcur = mtnext;
1862 if (nonroot) {
1863 // fstab must have "users" or "user"
1864 if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS))
1865 bb_error_msg_and_die(must_be_root);
1866 }
1867
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001868 // Mount the last thing we found.
1869
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001870 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001871 append_mount_options(&(mtcur->mnt_opts), cmdopts);
Denis Vlasenkode7684a2008-02-18 21:08:49 +00001872 if (ENABLE_FEATURE_MOUNT_LABEL) {
1873 resolve_mount_spec(&mtpair->mnt_fsname);
1874 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001875 rc = singlemount(mtcur, 0);
1876 free(mtcur->mnt_opts);
1877 }
1878 goto clean_up;
1879 }
1880
1881 /* If we're trying to mount something specific and this isn't it,
1882 * skip it. Note we must match both the exact text in fstab (ala
1883 * "proc") or a full path from root */
1884
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001885 if (argc) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001886
1887 // Is this what we're looking for?
1888
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001889 if (strcmp(argv[0], mtcur->mnt_fsname) &&
1890 strcmp(storage_path, mtcur->mnt_fsname) &&
1891 strcmp(argv[0], mtcur->mnt_dir) &&
1892 strcmp(storage_path, mtcur->mnt_dir)) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001893
1894 // Remember this entry. Something later may have overmounted
1895 // it, and we want the _last_ match.
1896
1897 mtcur = mtnext;
1898
1899 // If we're mounting all.
1900
1901 } else {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001902 // Do we need to match a filesystem type?
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001903 if (fstype && match_fstype(mtcur, fstype))
1904 continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001905
1906 // Skip noauto and swap anyway.
1907
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001908 if (parse_mount_options(mtcur->mnt_opts, 0) & (MOUNT_NOAUTO | MOUNT_SWAP))
1909 continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001910
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001911 // No, mount -a won't mount anything,
1912 // even user mounts, for mere humans.
1913
1914 if (nonroot)
1915 bb_error_msg_and_die(must_be_root);
1916
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001917 // Mount this thing.
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001918 if (ENABLE_FEATURE_MOUNT_LABEL)
Denis Vlasenkode7684a2008-02-18 21:08:49 +00001919 resolve_mount_spec(&mtpair->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001920
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001921 // NFS mounts want this to be xrealloc-able
1922 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001923 if (singlemount(mtcur, 1)) {
1924 /* Count number of failed mounts */
1925 rc++;
1926 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001927 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001928 }
1929 }
1930 if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab);
1931
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001932 clean_up:
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001933
1934 if (ENABLE_FEATURE_CLEAN_UP) {
1935 free(storage_path);
1936 free(cmdopts);
1937 }
1938
1939 return rc;
1940}