blob: 4ac52cd02f2fca3e3f656d4700be1b532723e370 [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>
Denis Vlasenko2535f122007-09-15 13:28:30 +000022#include "libbb.h"
Bernhard Reutner-Fischerf4701962008-01-27 12:50:12 +000023#include <syslog.h>
Eric Andersenbd22ed82000-07-08 18:55:24 +000024
Denis Vlasenko30a64cd2006-09-15 15:12:00 +000025/* Needed for nfs support only... */
Denis Vlasenko30a64cd2006-09-15 15:12:00 +000026#include <sys/utsname.h>
27#undef TRUE
28#undef FALSE
29#include <rpc/rpc.h>
30#include <rpc/pmap_prot.h>
31#include <rpc/pmap_clnt.h>
32
Denis Vlasenko2535f122007-09-15 13:28:30 +000033#ifndef MS_SILENT
34#define MS_SILENT (1 << 15)
35#endif
Denis Vlasenko30a64cd2006-09-15 15:12:00 +000036
Denis Vlasenko908d6b72006-12-18 23:07:42 +000037#if defined(__dietlibc__)
38/* 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
39 * dietlibc-0.30 does not have implementation of getmntent_r() */
Denis Vlasenko63430ae2007-10-29 19:18:39 +000040static struct mntent *getmntent_r(FILE* stream, struct mntent* result, char* buffer, int bufsize)
Denis Vlasenko908d6b72006-12-18 23:07:42 +000041{
Denis Vlasenko908d6b72006-12-18 23:07:42 +000042 struct mntent* ment = getmntent(stream);
43 memcpy(result, ment, sizeof(struct mntent));
44 return result;
45}
46#endif
47
Denis Vlasenko2535f122007-09-15 13:28:30 +000048#define getmntent_buf bb_common_bufsiz1
49
Denis Vlasenko908d6b72006-12-18 23:07:42 +000050
Rob Landleydc0955b2006-03-14 18:16:25 +000051// Not real flags, but we want to be able to check for this.
Denis Vlasenko13c5a682006-10-16 22:39:51 +000052enum {
53 MOUNT_USERS = (1<<28)*ENABLE_DESKTOP,
54 MOUNT_NOAUTO = (1<<29),
55 MOUNT_SWAP = (1<<30),
56};
57// TODO: more "user" flag compatibility.
58// "user" option (from mount manpage):
59// Only the user that mounted a filesystem can unmount it again.
60// If any user should be able to unmount, then use users instead of user
61// in the fstab line. The owner option is similar to the user option,
62// with the restriction that the user must be the owner of the special file.
63// This may be useful e.g. for /dev/fd if a login script makes
64// the console user owner of this device.
Rob Landley3ba7bd12006-08-09 19:51:13 +000065
Rob Landleydc0955b2006-03-14 18:16:25 +000066/* Standard mount options (from -o options or --options), with corresponding
67 * flags */
Eric Andersencc8ed391999-10-05 16:24:54 +000068
Denis Vlasenko63430ae2007-10-29 19:18:39 +000069static const int32_t mount_options[] = {
Rob Landleye3781b72006-08-08 01:39:49 +000070 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
Rob Landleydc0955b2006-03-14 18:16:25 +000071
Rob Landleye3781b72006-08-08 01:39:49 +000072 USE_FEATURE_MOUNT_LOOP(
Denis Vlasenko63430ae2007-10-29 19:18:39 +000073 /* "loop" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +000074 )
Rob Landleydc0955b2006-03-14 18:16:25 +000075
Rob Landleye3781b72006-08-08 01:39:49 +000076 USE_FEATURE_MOUNT_FSTAB(
Denis Vlasenko63430ae2007-10-29 19:18:39 +000077 /* "defaults" */ 0,
78 /* "quiet" 0 - do not filter out, vfat wants to see it */
79 /* "noauto" */ MOUNT_NOAUTO,
80 /* "sw" */ MOUNT_SWAP,
81 /* "swap" */ MOUNT_SWAP,
82 USE_DESKTOP(/* "user" */ MOUNT_USERS,)
83 USE_DESKTOP(/* "users" */ MOUNT_USERS,)
Denis Vlasenko8c638cb2008-01-29 09:31:09 +000084 /* "_netdev" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +000085 )
Rob Landleydc0955b2006-03-14 18:16:25 +000086
Rob Landleye3781b72006-08-08 01:39:49 +000087 USE_FEATURE_MOUNT_FLAGS(
88 // vfs flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +000089 /* "nosuid" */ MS_NOSUID,
90 /* "suid" */ ~MS_NOSUID,
91 /* "dev" */ ~MS_NODEV,
92 /* "nodev" */ MS_NODEV,
93 /* "exec" */ ~MS_NOEXEC,
94 /* "noexec" */ MS_NOEXEC,
95 /* "sync" */ MS_SYNCHRONOUS,
96 /* "async" */ ~MS_SYNCHRONOUS,
97 /* "atime" */ ~MS_NOATIME,
98 /* "noatime" */ MS_NOATIME,
99 /* "diratime" */ ~MS_NODIRATIME,
100 /* "nodiratime" */ MS_NODIRATIME,
101 /* "loud" */ ~MS_SILENT,
Eric Andersen9601a1c2006-03-20 18:07:50 +0000102
Rob Landleye3781b72006-08-08 01:39:49 +0000103 // action flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000104 /* "bind" */ MS_BIND,
105 /* "move" */ MS_MOVE,
106 /* "shared" */ MS_SHARED,
107 /* "slave" */ MS_SLAVE,
108 /* "private" */ MS_PRIVATE,
109 /* "unbindable" */ MS_UNBINDABLE,
110 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
111 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
112 /* "rprivate" */ MS_SLAVE|MS_RECURSIVE,
113 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
Rob Landleye3781b72006-08-08 01:39:49 +0000114 )
115
116 // Always understood.
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000117 /* "ro" */ MS_RDONLY, // vfs flag
118 /* "rw" */ ~MS_RDONLY, // vfs flag
119 /* "remount" */ MS_REMOUNT // action flag
Eric Andersencc8ed391999-10-05 16:24:54 +0000120};
121
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000122static const char mount_option_str[] =
123 USE_FEATURE_MOUNT_LOOP(
124 "loop" "\0"
125 )
126 USE_FEATURE_MOUNT_FSTAB(
127 "defaults" "\0"
128 /* "quiet" "\0" - do not filter out, vfat wants to see it */
129 "noauto" "\0"
130 "sw" "\0"
131 "swap" "\0"
132 USE_DESKTOP("user" "\0")
133 USE_DESKTOP("users" "\0")
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000134 "_netdev" "\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000135 )
136 USE_FEATURE_MOUNT_FLAGS(
137 // vfs flags
138 "nosuid" "\0"
139 "suid" "\0"
140 "dev" "\0"
141 "nodev" "\0"
142 "exec" "\0"
143 "noexec" "\0"
144 "sync" "\0"
145 "async" "\0"
146 "atime" "\0"
147 "noatime" "\0"
148 "diratime" "\0"
149 "nodiratime" "\0"
150 "loud" "\0"
151
152 // action flags
153 "bind" "\0"
154 "move" "\0"
155 "shared" "\0"
156 "slave" "\0"
157 "private" "\0"
158 "unbindable" "\0"
159 "rshared" "\0"
160 "rslave" "\0"
161 "rprivate" "\0"
162 "runbindable" "\0"
163 )
164
165 // Always understood.
166 "ro" "\0" // vfs flag
167 "rw" "\0" // vfs flag
168 "remount" "\0" // action flag
169;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000170
Rob Landleydc0955b2006-03-14 18:16:25 +0000171/* Append mount options to string */
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000172static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000173{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000174 if (*oldopts && **oldopts) {
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000175 /* do not insert options which are already there */
176 while (newopts[0]) {
177 char *p;
178 int len = strlen(newopts);
179 p = strchr(newopts, ',');
180 if (p) len = p - newopts;
181 p = *oldopts;
182 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000183 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000184 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000185 goto skip;
186 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000187 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000188 p++;
189 }
190 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
191 free(*oldopts);
192 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000193 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000194 newopts += len;
195 while (newopts[0] == ',') newopts++;
196 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000197 } else {
198 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000199 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000200 }
201}
202
203/* Use the mount_options list to parse options into flags.
Denis Vlasenko00d7d6c2006-09-11 17:42:44 +0000204 * Also return list of unrecognized options if unrecognized!=NULL */
Rob Landleydc0955b2006-03-14 18:16:25 +0000205static int parse_mount_options(char *options, char **unrecognized)
206{
207 int flags = MS_SILENT;
208
Rob Landley6a6798b2005-08-10 20:35:54 +0000209 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000210 for (;;) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000211 int i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000212 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000213 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000214
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000215 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000216
Rob Landley6a6798b2005-08-10 20:35:54 +0000217 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000218 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000219 if (!strcasecmp(option_str, options)) {
220 long fl = mount_options[i];
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000221 if (fl < 0) flags &= fl;
Rob Landleydc0955b2006-03-14 18:16:25 +0000222 else flags |= fl;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000223 break;
224 }
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000225 option_str += strlen(option_str) + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000226 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000227 // If unrecognized not NULL, append unrecognized mount options */
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000228 if (unrecognized && i == ARRAY_SIZE(mount_options)) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000229 // Add it to strflags, to pass on to kernel
Rob Landleydc0955b2006-03-14 18:16:25 +0000230 i = *unrecognized ? strlen(*unrecognized) : 0;
231 *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000232
Rob Landley6a6798b2005-08-10 20:35:54 +0000233 // Comma separated if it's not the first one
Rob Landleydc0955b2006-03-14 18:16:25 +0000234 if (i) (*unrecognized)[i++] = ',';
235 strcpy((*unrecognized)+i, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000236 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000237
Denis Vlasenko2535f122007-09-15 13:28:30 +0000238 if (!comma)
239 break;
240 // Advance to next option
241 *comma = ',';
242 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000243 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000244
Rob Landleydc0955b2006-03-14 18:16:25 +0000245 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000246}
247
Rob Landleydc0955b2006-03-14 18:16:25 +0000248// Return a list of all block device backed filesystems
Matt Kraai12400822001-04-17 04:32:50 +0000249
Rob Landleydc0955b2006-03-14 18:16:25 +0000250static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000251{
Denis Vlasenko87468852007-04-13 23:22:00 +0000252 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000253 "/etc/filesystems",
254 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000255 };
256 char *fs, *buf;
Rob Landleydc0955b2006-03-14 18:16:25 +0000257 llist_t *list = 0;
258 int i;
259 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000260
Denis Vlasenko87468852007-04-13 23:22:00 +0000261 for (i = 0; i < 2; i++) {
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000262 f = fopen(filesystems[i], "r");
263 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000264
Denis Vlasenko2d5ca602006-10-12 22:43:20 +0000265 while ((buf = xmalloc_getline(f)) != 0) {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000266 if (!strncmp(buf, "nodev", 5) && isspace(buf[5]))
267 continue;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000268 fs = skip_whitespace(buf);
Denis Vlasenko372686b2006-10-12 22:42:33 +0000269 if (*fs=='#' || *fs=='*' || !*fs) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000270
Denis Vlasenko372686b2006-10-12 22:42:33 +0000271 llist_add_to_end(&list, xstrdup(fs));
272 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000273 }
274 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
275 }
276
277 return list;
278}
279
Denis Vlasenkob9256052007-09-28 10:29:17 +0000280static llist_t *fslist;
Rob Landleydc0955b2006-03-14 18:16:25 +0000281
Rob Landleydc0955b2006-03-14 18:16:25 +0000282#if ENABLE_FEATURE_CLEAN_UP
283static void delete_block_backed_filesystems(void)
284{
Rob Landleya6b5b602006-05-08 19:03:07 +0000285 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000286}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000287#else
288void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000289#endif
290
291#if ENABLE_FEATURE_MTAB_SUPPORT
Denis Vlasenko00d7d6c2006-09-11 17:42:44 +0000292static int useMtab = 1;
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000293static int fakeIt;
Rob Landleydc0955b2006-03-14 18:16:25 +0000294#else
Denis Vlasenko116080a2006-09-21 11:13:08 +0000295#define useMtab 0
296#define fakeIt 0
Rob Landleydc0955b2006-03-14 18:16:25 +0000297#endif
298
299// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000300// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko25098f72006-09-14 15:46:33 +0000301static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000302{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000303 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000304
Denis Vlasenkob1726782006-09-29 14:43:20 +0000305 if (fakeIt) goto mtab;
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000306
Rob Landleydc0955b2006-03-14 18:16:25 +0000307 // Mount, with fallback to read-only if necessary.
308
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000309 for (;;) {
Rob Landleydc0955b2006-03-14 18:16:25 +0000310 rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
311 vfsflags, filteropts);
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000312 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000313 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000314 if (!(vfsflags & MS_SILENT))
315 bb_error_msg("%s is write-protected, mounting read-only",
316 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000317 vfsflags |= MS_RDONLY;
318 }
319
Rob Landleydc0955b2006-03-14 18:16:25 +0000320 // Abort entirely if permission denied.
321
322 if (rc && errno == EPERM)
323 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
324
325 /* If the mount was successful, and we're maintaining an old-style
326 * mtab file by hand, add the new entry to it now. */
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000327 mtab:
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000328 if (ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000329 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000330 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000331 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000332 int i;
333
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000334 if (!mountTable) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000335 bb_error_msg("no %s", bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000336 goto ret;
337 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000338
339 // Add vfs string flags
340
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000341 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
342 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
343 append_mount_options(&(mp->mnt_opts), option_str);
344 option_str += strlen(option_str) + 1;
345 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000346
347 // Remove trailing / (if any) from directory we mounted on
348
Denis Vlasenko727ef942006-09-14 13:19:19 +0000349 i = strlen(mp->mnt_dir) - 1;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000350 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000351
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000352 // Convert to canonical pathnames as needed
Denis Vlasenko727ef942006-09-14 13:19:19 +0000353
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000354 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000355 fsname = 0;
Denis Vlasenko727ef942006-09-14 13:19:19 +0000356 if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000357 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000358 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000359 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000360 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000361
362 // Write and close.
363
Denis Vlasenko727ef942006-09-14 13:19:19 +0000364 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000365 endmntent(mountTable);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000366 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000367 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000368 free(fsname);
369 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000370 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000371 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000372 return rc;
373}
374
Denis Vlasenko25098f72006-09-14 15:46:33 +0000375#if ENABLE_FEATURE_MOUNT_NFS
376
377/*
378 * Linux NFS mount
379 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
380 *
381 * Licensed under GPLv2, see file LICENSE in this tarball for details.
382 *
383 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
384 * numbers to be specified on the command line.
385 *
386 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
387 * Omit the call to connect() for Linux version 1.3.11 or later.
388 *
389 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
390 * Implemented the "bg", "fg" and "retry" mount options for NFS.
391 *
392 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
393 * - added Native Language Support
394 *
395 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
396 * plus NFSv3 stuff.
397 */
398
Denis Vlasenko25098f72006-09-14 15:46:33 +0000399/* This is just a warning of a common mistake. Possibly this should be a
400 * uclibc faq entry rather than in busybox... */
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000401#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000402#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
403#endif
404
405#define MOUNTPORT 635
406#define MNTPATHLEN 1024
407#define MNTNAMLEN 255
408#define FHSIZE 32
409#define FHSIZE3 64
410
411typedef char fhandle[FHSIZE];
412
413typedef struct {
414 unsigned int fhandle3_len;
415 char *fhandle3_val;
416} fhandle3;
417
418enum mountstat3 {
419 MNT_OK = 0,
420 MNT3ERR_PERM = 1,
421 MNT3ERR_NOENT = 2,
422 MNT3ERR_IO = 5,
423 MNT3ERR_ACCES = 13,
424 MNT3ERR_NOTDIR = 20,
425 MNT3ERR_INVAL = 22,
426 MNT3ERR_NAMETOOLONG = 63,
427 MNT3ERR_NOTSUPP = 10004,
428 MNT3ERR_SERVERFAULT = 10006,
429};
430typedef enum mountstat3 mountstat3;
431
432struct fhstatus {
433 unsigned int fhs_status;
434 union {
435 fhandle fhs_fhandle;
436 } fhstatus_u;
437};
438typedef struct fhstatus fhstatus;
439
440struct mountres3_ok {
441 fhandle3 fhandle;
442 struct {
443 unsigned int auth_flavours_len;
444 char *auth_flavours_val;
445 } auth_flavours;
446};
447typedef struct mountres3_ok mountres3_ok;
448
449struct mountres3 {
450 mountstat3 fhs_status;
451 union {
452 mountres3_ok mountinfo;
453 } mountres3_u;
454};
455typedef struct mountres3 mountres3;
456
457typedef char *dirpath;
458
459typedef char *name;
460
461typedef struct mountbody *mountlist;
462
463struct mountbody {
464 name ml_hostname;
465 dirpath ml_directory;
466 mountlist ml_next;
467};
468typedef struct mountbody mountbody;
469
470typedef struct groupnode *groups;
471
472struct groupnode {
473 name gr_name;
474 groups gr_next;
475};
476typedef struct groupnode groupnode;
477
478typedef struct exportnode *exports;
479
480struct exportnode {
481 dirpath ex_dir;
482 groups ex_groups;
483 exports ex_next;
484};
485typedef struct exportnode exportnode;
486
487struct ppathcnf {
488 int pc_link_max;
489 short pc_max_canon;
490 short pc_max_input;
491 short pc_name_max;
492 short pc_path_max;
493 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000494 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000495 char pc_xxx;
496 short pc_mask[2];
497};
498typedef struct ppathcnf ppathcnf;
499
500#define MOUNTPROG 100005
501#define MOUNTVERS 1
502
503#define MOUNTPROC_NULL 0
504#define MOUNTPROC_MNT 1
505#define MOUNTPROC_DUMP 2
506#define MOUNTPROC_UMNT 3
507#define MOUNTPROC_UMNTALL 4
508#define MOUNTPROC_EXPORT 5
509#define MOUNTPROC_EXPORTALL 6
510
511#define MOUNTVERS_POSIX 2
512
513#define MOUNTPROC_PATHCONF 7
514
515#define MOUNT_V3 3
516
517#define MOUNTPROC3_NULL 0
518#define MOUNTPROC3_MNT 1
519#define MOUNTPROC3_DUMP 2
520#define MOUNTPROC3_UMNT 3
521#define MOUNTPROC3_UMNTALL 4
522#define MOUNTPROC3_EXPORT 5
523
524enum {
525#ifndef NFS_FHSIZE
526 NFS_FHSIZE = 32,
527#endif
528#ifndef NFS_PORT
529 NFS_PORT = 2049
530#endif
531};
532
Denis Vlasenko25098f72006-09-14 15:46:33 +0000533/*
534 * We want to be able to compile mount on old kernels in such a way
535 * that the binary will work well on more recent kernels.
536 * Thus, if necessary we teach nfsmount.c the structure of new fields
537 * that will come later.
538 *
539 * Moreover, the new kernel includes conflict with glibc includes
540 * so it is easiest to ignore the kernel altogether (at compile time).
541 */
542
543struct nfs2_fh {
544 char data[32];
545};
546struct nfs3_fh {
547 unsigned short size;
548 unsigned char data[64];
549};
550
551struct nfs_mount_data {
552 int version; /* 1 */
553 int fd; /* 1 */
554 struct nfs2_fh old_root; /* 1 */
555 int flags; /* 1 */
556 int rsize; /* 1 */
557 int wsize; /* 1 */
558 int timeo; /* 1 */
559 int retrans; /* 1 */
560 int acregmin; /* 1 */
561 int acregmax; /* 1 */
562 int acdirmin; /* 1 */
563 int acdirmax; /* 1 */
564 struct sockaddr_in addr; /* 1 */
565 char hostname[256]; /* 1 */
566 int namlen; /* 2 */
567 unsigned int bsize; /* 3 */
568 struct nfs3_fh root; /* 4 */
569};
570
571/* bits in the flags field */
572enum {
573 NFS_MOUNT_SOFT = 0x0001, /* 1 */
574 NFS_MOUNT_INTR = 0x0002, /* 1 */
575 NFS_MOUNT_SECURE = 0x0004, /* 1 */
576 NFS_MOUNT_POSIX = 0x0008, /* 1 */
577 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
578 NFS_MOUNT_NOAC = 0x0020, /* 1 */
579 NFS_MOUNT_TCP = 0x0040, /* 2 */
580 NFS_MOUNT_VER3 = 0x0080, /* 3 */
581 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
582 NFS_MOUNT_NONLM = 0x0200 /* 3 */
583};
584
585
586/*
587 * We need to translate between nfs status return values and
588 * the local errno values which may not be the same.
589 *
590 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
591 * "after #include <errno.h> the symbol errno is reserved for any use,
592 * it cannot even be used as a struct tag or field name".
593 */
594
595#ifndef EDQUOT
596#define EDQUOT ENOSPC
597#endif
598
599// Convert each NFSERR_BLAH into EBLAH
600
601static const struct {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000602 short stat;
603 short errnum;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000604} nfs_errtbl[] = {
605 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
606 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
607 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
608 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
609};
610
611static char *nfs_strerror(int status)
612{
613 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000614
615 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
616 if (nfs_errtbl[i].stat == status)
617 return strerror(nfs_errtbl[i].errnum);
618 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000619 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000620}
621
622static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
623{
624 if (!xdr_opaque(xdrs, objp, FHSIZE))
625 return FALSE;
626 return TRUE;
627}
628
629static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
630{
631 if (!xdr_u_int(xdrs, &objp->fhs_status))
632 return FALSE;
633 switch (objp->fhs_status) {
634 case 0:
635 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
636 return FALSE;
637 break;
638 default:
639 break;
640 }
641 return TRUE;
642}
643
644static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
645{
646 if (!xdr_string(xdrs, objp, MNTPATHLEN))
647 return FALSE;
648 return TRUE;
649}
650
651static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
652{
653 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
654 return FALSE;
655 return TRUE;
656}
657
658static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
659{
660 if (!xdr_fhandle3(xdrs, &objp->fhandle))
661 return FALSE;
662 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
663 sizeof (int), (xdrproc_t) xdr_int))
664 return FALSE;
665 return TRUE;
666}
667
668static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
669{
670 if (!xdr_enum(xdrs, (enum_t *) objp))
671 return FALSE;
672 return TRUE;
673}
674
675static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
676{
677 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
678 return FALSE;
679 switch (objp->fhs_status) {
680 case MNT_OK:
681 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
682 return FALSE;
683 break;
684 default:
685 break;
686 }
687 return TRUE;
688}
689
690#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
691
Denis Vlasenkob9256052007-09-28 10:29:17 +0000692static smalluint nfs_mount_version;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000693
694/*
695 * Unfortunately, the kernel prints annoying console messages
696 * in case of an unexpected nfs mount version (instead of
697 * just returning some error). Therefore we'll have to try
698 * and figure out what version the kernel expects.
699 *
700 * Variables:
701 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
702 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
703 * nfs_mount_version: version this source and running kernel can handle
704 */
705static void
706find_kernel_nfs_mount_version(void)
707{
Denis Vlasenkob9256052007-09-28 10:29:17 +0000708 int kernel_version;
709
710 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000711 return;
712
713 nfs_mount_version = 4; /* default */
714
715 kernel_version = get_linux_version_code();
716 if (kernel_version) {
717 if (kernel_version < KERNEL_VERSION(2,1,32))
718 nfs_mount_version = 1;
719 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
720 (kernel_version >= KERNEL_VERSION(2,3,0) &&
721 kernel_version < KERNEL_VERSION(2,3,99)))
722 nfs_mount_version = 3;
723 /* else v4 since 2.3.99pre4 */
724 }
725}
726
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +0000727static void
Denis Vlasenkob9256052007-09-28 10:29:17 +0000728get_mountport(struct pmap *pm_mnt,
729 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +0000730 long unsigned prog,
731 long unsigned version,
732 long unsigned proto,
733 long unsigned port)
734{
735 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000736
737 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +0000738/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
739 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000740 pmap = pmap_getmaps(server_addr);
741
742 if (version > MAX_NFSPROT)
743 version = MAX_NFSPROT;
744 if (!prog)
745 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000746 pm_mnt->pm_prog = prog;
747 pm_mnt->pm_vers = version;
748 pm_mnt->pm_prot = proto;
749 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000750
Denis Vlasenko25098f72006-09-14 15:46:33 +0000751 while (pmap) {
752 if (pmap->pml_map.pm_prog != prog)
753 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000754 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000755 goto next;
756 if (version > 2 && pmap->pml_map.pm_vers != version)
757 goto next;
758 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
759 goto next;
760 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
Denis Vlasenkob9256052007-09-28 10:29:17 +0000761 (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto) ||
Denis Vlasenko25098f72006-09-14 15:46:33 +0000762 (port && pmap->pml_map.pm_port != port))
763 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000764 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
765 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +0000766 pmap = pmap->pml_next;
767 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000768 if (!pm_mnt->pm_vers)
769 pm_mnt->pm_vers = MOUNTVERS;
770 if (!pm_mnt->pm_port)
771 pm_mnt->pm_port = MOUNTPORT;
772 if (!pm_mnt->pm_prot)
773 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000774}
775
Denis Vlasenkof0000652007-09-04 18:30:26 +0000776#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +0000777static int daemonize(void)
778{
779 int fd;
780 int pid = fork();
781 if (pid < 0) /* error */
782 return -errno;
783 if (pid > 0) /* parent */
784 return 0;
785 /* child */
786 fd = xopen(bb_dev_null, O_RDWR);
787 dup2(fd, 0);
788 dup2(fd, 1);
789 dup2(fd, 2);
Denis Vlasenko9af7c9d2007-01-19 21:19:35 +0000790 while (fd > 2) close(fd--);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000791 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +0000792 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000793 logmode = LOGMODE_SYSLOG;
794 return 1;
795}
Denis Vlasenkof0000652007-09-04 18:30:26 +0000796#else
797static inline int daemonize(void) { return -ENOSYS; }
798#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000799
800// TODO
801static inline int we_saw_this_host_before(const char *hostname)
802{
803 return 0;
804}
805
806/* RPC strerror analogs are terminally idiotic:
807 * *mandatory* prefix and \n at end.
808 * This hopefully helps. Usage:
809 * error_msg_rpc(clnt_*error*(" ")) */
810static void error_msg_rpc(const char *msg)
811{
Denis Vlasenko23514fe2006-09-19 14:07:52 +0000812 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000813 while (msg[0] == ' ' || msg[0] == ':') msg++;
814 len = strlen(msg);
815 while (len && msg[len-1] == '\n') len--;
816 bb_error_msg("%.*s", len, msg);
817}
818
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +0000819// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko25098f72006-09-14 15:46:33 +0000820static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
821{
822 CLIENT *mclient;
823 char *hostname;
824 char *pathname;
825 char *mounthost;
826 struct nfs_mount_data data;
827 char *opt;
828 struct hostent *hp;
829 struct sockaddr_in server_addr;
830 struct sockaddr_in mount_server_addr;
831 int msock, fsock;
832 union {
833 struct fhstatus nfsv2;
834 struct mountres3 nfsv3;
835 } status;
836 int daemonized;
837 char *s;
838 int port;
839 int mountport;
840 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +0000841#if BB_MMU
842 int bg = 0;
843#else
844 enum { bg = 0 };
845#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000846 int soft;
847 int intr;
848 int posix;
849 int nocto;
850 int noac;
851 int nolock;
852 int retry;
853 int tcp;
854 int mountprog;
855 int mountvers;
856 int nfsprog;
857 int nfsvers;
858 int retval;
859
860 find_kernel_nfs_mount_version();
861
862 daemonized = 0;
863 mounthost = NULL;
864 retval = ETIMEDOUT;
865 msock = fsock = -1;
866 mclient = NULL;
867
868 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
869
870 filteropts = xstrdup(filteropts); /* going to trash it later... */
871
872 hostname = xstrdup(mp->mnt_fsname);
873 /* mount_main() guarantees that ':' is there */
874 s = strchr(hostname, ':');
875 pathname = s + 1;
876 *s = '\0';
877 /* Ignore all but first hostname in replicated mounts
878 until they can be fully supported. (mack@sgi.com) */
879 s = strchr(hostname, ',');
880 if (s) {
881 *s = '\0';
882 bb_error_msg("warning: multiple hostnames not supported");
883 }
884
885 server_addr.sin_family = AF_INET;
886 if (!inet_aton(hostname, &server_addr.sin_addr)) {
887 hp = gethostbyname(hostname);
888 if (hp == NULL) {
889 bb_herror_msg("%s", hostname);
890 goto fail;
891 }
892 if (hp->h_length > sizeof(struct in_addr)) {
893 bb_error_msg("got bad hp->h_length");
894 hp->h_length = sizeof(struct in_addr);
895 }
896 memcpy(&server_addr.sin_addr,
897 hp->h_addr, hp->h_length);
898 }
899
900 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
901
902 /* add IP address to mtab options for use when unmounting */
903
904 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
905 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
906 } else {
907 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
908 mp->mnt_opts[0] ? "," : "",
909 inet_ntoa(server_addr.sin_addr));
910 free(mp->mnt_opts);
911 mp->mnt_opts = tmp;
912 }
913
914 /* Set default options.
915 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
916 * let the kernel decide.
917 * timeo is filled in after we know whether it'll be TCP or UDP. */
918 memset(&data, 0, sizeof(data));
919 data.retrans = 3;
920 data.acregmin = 3;
921 data.acregmax = 60;
922 data.acdirmin = 30;
923 data.acdirmax = 60;
924 data.namlen = NAME_MAX;
925
Denis Vlasenko25098f72006-09-14 15:46:33 +0000926 soft = 0;
927 intr = 0;
928 posix = 0;
929 nocto = 0;
930 nolock = 0;
931 noac = 0;
932 retry = 10000; /* 10000 minutes ~ 1 week */
933 tcp = 0;
934
935 mountprog = MOUNTPROG;
936 mountvers = 0;
937 port = 0;
938 mountport = 0;
939 nfsprog = 100003;
940 nfsvers = 0;
941
942 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +0000943 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +0000944 char *opteq = strchr(opt, '=');
945 if (opteq) {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000946 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000947 /* 0 */ "rsize\0"
948 /* 1 */ "wsize\0"
949 /* 2 */ "timeo\0"
950 /* 3 */ "retrans\0"
951 /* 4 */ "acregmin\0"
952 /* 5 */ "acregmax\0"
953 /* 6 */ "acdirmin\0"
954 /* 7 */ "acdirmax\0"
955 /* 8 */ "actimeo\0"
956 /* 9 */ "retry\0"
957 /* 10 */ "port\0"
958 /* 11 */ "mountport\0"
959 /* 12 */ "mounthost\0"
960 /* 13 */ "mountprog\0"
961 /* 14 */ "mountvers\0"
962 /* 15 */ "nfsprog\0"
963 /* 16 */ "nfsvers\0"
964 /* 17 */ "vers\0"
965 /* 18 */ "proto\0"
966 /* 19 */ "namlen\0"
967 /* 20 */ "addr\0";
Denis Vlasenko13858992006-10-08 12:49:22 +0000968 int val = xatoi_u(opteq + 1);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000969 *opteq = '\0';
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000970 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +0000971 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +0000972 data.rsize = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +0000973 break;
974 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +0000975 data.wsize = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +0000976 break;
977 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +0000978 data.timeo = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +0000979 break;
980 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +0000981 data.retrans = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +0000982 break;
983 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +0000984 data.acregmin = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +0000985 break;
986 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +0000987 data.acregmax = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +0000988 break;
989 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +0000990 data.acdirmin = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +0000991 break;
992 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +0000993 data.acdirmax = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +0000994 break;
995 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +0000996 data.acregmin = val;
997 data.acregmax = val;
998 data.acdirmin = val;
999 data.acdirmax = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001000 break;
1001 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001002 retry = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001003 break;
1004 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001005 port = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001006 break;
1007 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001008 mountport = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001009 break;
1010 case 12: // "mounthost"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001011 mounthost = xstrndup(opteq+1,
Denis Vlasenko5af906e2006-11-05 18:05:09 +00001012 strcspn(opteq+1," \t\n\r,"));
Denis Vlasenko68f21872006-10-26 01:47:34 +00001013 break;
1014 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001015 mountprog = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001016 break;
1017 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001018 mountvers = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001019 break;
1020 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001021 nfsprog = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001022 break;
1023 case 16: // "nfsvers"
1024 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001025 nfsvers = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001026 break;
1027 case 18: // "proto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001028 if (!strncmp(opteq+1, "tcp", 3))
1029 tcp = 1;
1030 else if (!strncmp(opteq+1, "udp", 3))
1031 tcp = 0;
1032 else
1033 bb_error_msg("warning: unrecognized proto= option");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001034 break;
1035 case 19: // "namlen"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001036 if (nfs_mount_version >= 2)
1037 data.namlen = val;
1038 else
1039 bb_error_msg("warning: option namlen is not supported\n");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001040 break;
1041 case 20: // "addr" - ignore
1042 break;
1043 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001044 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1045 goto fail;
1046 }
1047 }
1048 else {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001049 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001050 "bg\0"
1051 "fg\0"
1052 "soft\0"
1053 "hard\0"
1054 "intr\0"
1055 "posix\0"
1056 "cto\0"
1057 "ac\0"
1058 "tcp\0"
1059 "udp\0"
1060 "lock\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001061 int val = 1;
1062 if (!strncmp(opt, "no", 2)) {
1063 val = 0;
1064 opt += 2;
1065 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001066 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001067 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001068#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001069 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001070#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001071 break;
1072 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001073#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001074 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001075#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001076 break;
1077 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001078 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001079 break;
1080 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001081 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001082 break;
1083 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001084 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001085 break;
1086 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001087 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001088 break;
1089 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001090 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001091 break;
1092 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001093 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001094 break;
1095 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001096 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001097 break;
1098 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001099 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001100 break;
1101 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001102 if (nfs_mount_version >= 3)
1103 nolock = !val;
1104 else
1105 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001106 break;
1107 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001108 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1109 goto fail;
1110 }
1111 }
1112 }
1113 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1114
1115 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1116 | (intr ? NFS_MOUNT_INTR : 0)
1117 | (posix ? NFS_MOUNT_POSIX : 0)
1118 | (nocto ? NFS_MOUNT_NOCTO : 0)
1119 | (noac ? NFS_MOUNT_NOAC : 0);
1120 if (nfs_mount_version >= 2)
1121 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1122 if (nfs_mount_version >= 3)
1123 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1124 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1125 bb_error_msg("NFSv%d not supported", nfsvers);
1126 goto fail;
1127 }
1128 if (nfsvers && !mountvers)
1129 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1130 if (nfsvers && nfsvers < mountvers) {
1131 mountvers = nfsvers;
1132 }
1133
1134 /* Adjust options if none specified */
1135 if (!data.timeo)
1136 data.timeo = tcp ? 70 : 7;
1137
Denis Vlasenko25098f72006-09-14 15:46:33 +00001138 data.version = nfs_mount_version;
1139
1140 if (vfsflags & MS_REMOUNT)
1141 goto do_mount;
1142
1143 /*
1144 * If the previous mount operation on the same host was
1145 * backgrounded, and the "bg" for this mount is also set,
1146 * give up immediately, to avoid the initial timeout.
1147 */
1148 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001149 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001150 if (daemonized <= 0) { /* parent or error */
1151 retval = -daemonized;
1152 goto ret;
1153 }
1154 }
1155
1156 /* create mount daemon client */
1157 /* See if the nfs host = mount host. */
1158 if (mounthost) {
1159 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1160 mount_server_addr.sin_family = AF_INET;
1161 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1162 } else {
1163 hp = gethostbyname(mounthost);
1164 if (hp == NULL) {
1165 bb_herror_msg("%s", mounthost);
1166 goto fail;
1167 } else {
1168 if (hp->h_length > sizeof(struct in_addr)) {
1169 bb_error_msg("got bad hp->h_length?");
1170 hp->h_length = sizeof(struct in_addr);
1171 }
1172 mount_server_addr.sin_family = AF_INET;
1173 memcpy(&mount_server_addr.sin_addr,
1174 hp->h_addr, hp->h_length);
1175 }
1176 }
1177 }
1178
1179 /*
1180 * The following loop implements the mount retries. When the mount
1181 * times out, and the "bg" option is set, we background ourself
1182 * and continue trying.
1183 *
1184 * The case where the mount point is not present and the "bg"
1185 * option is set, is treated as a timeout. This is done to
1186 * support nested mounts.
1187 *
1188 * The "retry" count specified by the user is the number of
1189 * minutes to retry before giving up.
1190 */
1191 {
1192 struct timeval total_timeout;
1193 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001194 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001195 time_t t;
1196 time_t prevt;
1197 time_t timeout;
1198
1199 retry_timeout.tv_sec = 3;
1200 retry_timeout.tv_usec = 0;
1201 total_timeout.tv_sec = 20;
1202 total_timeout.tv_usec = 0;
1203 timeout = time(NULL) + 60 * retry;
1204 prevt = 0;
1205 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001206 retry:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001207 /* be careful not to use too many CPU cycles */
1208 if (t - prevt < 30)
1209 sleep(30);
1210
Denis Vlasenkob9256052007-09-28 10:29:17 +00001211 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001212 mountprog,
1213 mountvers,
1214 proto,
1215 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001216 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001217
1218 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001219 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001220 msock = RPC_ANYSOCK;
1221
Denis Vlasenkob9256052007-09-28 10:29:17 +00001222 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001223 case IPPROTO_UDP:
1224 mclient = clntudp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001225 pm_mnt.pm_prog,
1226 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001227 retry_timeout,
1228 &msock);
1229 if (mclient)
1230 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001231 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001232 msock = RPC_ANYSOCK;
1233 case IPPROTO_TCP:
1234 mclient = clnttcp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001235 pm_mnt.pm_prog,
1236 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001237 &msock, 0, 0);
1238 break;
1239 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001240 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001241 }
1242 if (!mclient) {
1243 if (!daemonized && prevt == 0)
1244 error_msg_rpc(clnt_spcreateerror(" "));
1245 } else {
1246 enum clnt_stat clnt_stat;
1247 /* try to mount hostname:pathname */
1248 mclient->cl_auth = authunix_create_default();
1249
1250 /* make pointers in xdr_mountres3 NULL so
1251 * that xdr_array allocates memory for us
1252 */
1253 memset(&status, 0, sizeof(status));
1254
Denis Vlasenkob9256052007-09-28 10:29:17 +00001255 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001256 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1257 (xdrproc_t) xdr_dirpath,
1258 (caddr_t) &pathname,
1259 (xdrproc_t) xdr_mountres3,
1260 (caddr_t) &status,
1261 total_timeout);
1262 else
1263 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1264 (xdrproc_t) xdr_dirpath,
1265 (caddr_t) &pathname,
1266 (xdrproc_t) xdr_fhstatus,
1267 (caddr_t) &status,
1268 total_timeout);
1269
1270 if (clnt_stat == RPC_SUCCESS)
1271 goto prepare_kernel_data; /* we're done */
1272 if (errno != ECONNREFUSED) {
1273 error_msg_rpc(clnt_sperror(mclient, " "));
1274 goto fail; /* don't retry */
1275 }
1276 /* Connection refused */
1277 if (!daemonized && prevt == 0) /* print just once */
1278 error_msg_rpc(clnt_sperror(mclient, " "));
1279 auth_destroy(mclient->cl_auth);
1280 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001281 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001282 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001283 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001284 }
1285
1286 /* Timeout. We are going to retry... maybe */
1287
1288 if (!bg)
1289 goto fail;
1290 if (!daemonized) {
1291 daemonized = daemonize();
1292 if (daemonized <= 0) { /* parent or error */
1293 retval = -daemonized;
1294 goto ret;
1295 }
1296 }
1297 prevt = t;
1298 t = time(NULL);
1299 if (t >= timeout)
1300 /* TODO error message */
1301 goto fail;
1302
1303 goto retry;
1304 }
1305
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001306 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001307
1308 if (nfsvers == 2) {
1309 if (status.nfsv2.fhs_status != 0) {
1310 bb_error_msg("%s:%s failed, reason given by server: %s",
1311 hostname, pathname,
1312 nfs_strerror(status.nfsv2.fhs_status));
1313 goto fail;
1314 }
1315 memcpy(data.root.data,
1316 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1317 NFS_FHSIZE);
1318 data.root.size = NFS_FHSIZE;
1319 memcpy(data.old_root.data,
1320 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1321 NFS_FHSIZE);
1322 } else {
1323 fhandle3 *my_fhandle;
1324 if (status.nfsv3.fhs_status != 0) {
1325 bb_error_msg("%s:%s failed, reason given by server: %s",
1326 hostname, pathname,
1327 nfs_strerror(status.nfsv3.fhs_status));
1328 goto fail;
1329 }
1330 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1331 memset(data.old_root.data, 0, NFS_FHSIZE);
1332 memset(&data.root, 0, sizeof(data.root));
1333 data.root.size = my_fhandle->fhandle3_len;
1334 memcpy(data.root.data,
1335 (char *) my_fhandle->fhandle3_val,
1336 my_fhandle->fhandle3_len);
1337
1338 data.flags |= NFS_MOUNT_VER3;
1339 }
1340
1341 /* create nfs socket for kernel */
1342
1343 if (tcp) {
1344 if (nfs_mount_version < 3) {
1345 bb_error_msg("NFS over TCP is not supported");
1346 goto fail;
1347 }
1348 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1349 } else
1350 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1351 if (fsock < 0) {
1352 bb_perror_msg("nfs socket");
1353 goto fail;
1354 }
1355 if (bindresvport(fsock, 0) < 0) {
1356 bb_perror_msg("nfs bindresvport");
1357 goto fail;
1358 }
1359 if (port == 0) {
1360 server_addr.sin_port = PMAPPORT;
1361 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1362 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1363 if (port == 0)
1364 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001365 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001366 server_addr.sin_port = htons(port);
1367
1368 /* prepare data structure for kernel */
1369
1370 data.fd = fsock;
1371 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1372 strncpy(data.hostname, hostname, sizeof(data.hostname));
1373
1374 /* clean up */
1375
1376 auth_destroy(mclient->cl_auth);
1377 clnt_destroy(mclient);
1378 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001379 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001380
1381 if (bg) {
1382 /* We must wait until mount directory is available */
1383 struct stat statbuf;
1384 int delay = 1;
1385 while (stat(mp->mnt_dir, &statbuf) == -1) {
1386 if (!daemonized) {
1387 daemonized = daemonize();
1388 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001389 // FIXME: parent doesn't close fsock - ??!
Denis Vlasenko25098f72006-09-14 15:46:33 +00001390 retval = -daemonized;
1391 goto ret;
1392 }
1393 }
1394 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1395 delay *= 2;
1396 if (delay > 30)
1397 delay = 30;
1398 }
1399 }
1400
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001401 do_mount: /* perform actual mount */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001402
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001403 mp->mnt_type = (char*)"nfs";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001404 retval = mount_it_now(mp, vfsflags, (char*)&data);
1405 goto ret;
1406
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001407 fail: /* abort */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001408
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001409 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001410 if (mclient) {
1411 auth_destroy(mclient->cl_auth);
1412 clnt_destroy(mclient);
1413 }
1414 close(msock);
1415 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001416 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001417 close(fsock);
1418
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001419 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001420 free(hostname);
1421 free(mounthost);
1422 free(filteropts);
1423 return retval;
1424}
1425
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001426#else /* !ENABLE_FEATURE_MOUNT_NFS */
1427
1428/* Never called. Call should be optimized out. */
1429int nfsmount(struct mntent *mp, int vfsflags, char *filteropts);
1430
1431#endif /* !ENABLE_FEATURE_MOUNT_NFS */
1432
1433// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1434// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001435// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001436static int singlemount(struct mntent *mp, int ignore_busy)
1437{
1438 int rc = -1, vfsflags;
1439 char *loopFile = 0, *filteropts = 0;
1440 llist_t *fl = 0;
1441 struct stat st;
1442
1443 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1444
1445 // Treat fstype "auto" as unspecified.
1446
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001447 if (mp->mnt_type && strcmp(mp->mnt_type,"auto") == 0)
1448 mp->mnt_type = 0;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001449
Denis Vlasenko2535f122007-09-15 13:28:30 +00001450 // Might this be a virtual filesystem?
1451
1452 if (ENABLE_FEATURE_MOUNT_HELPERS
Denis Vlasenkoc03e8722007-12-26 20:56:55 +00001453 && (strchr(mp->mnt_fsname, '#'))
Denis Vlasenko2535f122007-09-15 13:28:30 +00001454 ) {
1455 char *s, *p, *args[35];
1456 int n = 0;
1457 for (s = p = mp->mnt_fsname; *s && n < 35-3; ++s) {
1458 if (s[0] == '#' && s[1] != '#') {
1459 *s = '\0';
1460 args[n++] = p;
1461 p = s + 1;
1462 }
1463 }
1464 args[n++] = p;
1465 args[n++] = mp->mnt_dir;
1466 args[n] = NULL;
1467 rc = wait4pid(xspawn(args));
1468 goto report_error;
1469 }
1470
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001471 // Might this be an CIFS filesystem?
1472
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001473 if (ENABLE_FEATURE_MOUNT_CIFS
1474 && (!mp->mnt_type || strcmp(mp->mnt_type,"cifs") == 0)
1475 && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\')
1476 && mp->mnt_fsname[0]==mp->mnt_fsname[1]
1477 ) {
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001478 len_and_sockaddr *lsa;
1479 char *ip, *dotted;
1480 char *s;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001481
1482 rc = 1;
1483 // Replace '/' with '\' and verify that unc points to "//server/share".
1484
1485 for (s = mp->mnt_fsname; *s; ++s)
1486 if (*s == '/') *s = '\\';
1487
1488 // get server IP
1489
1490 s = strrchr(mp->mnt_fsname, '\\');
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001491 if (s <= mp->mnt_fsname+1) goto report_error;
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001492 *s = '\0';
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001493 lsa = host2sockaddr(mp->mnt_fsname+2, 0);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001494 *s = '\\';
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001495 if (!lsa) goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001496
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001497 // insert ip=... option into string flags.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001498
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001499 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001500 ip = xasprintf("ip=%s", dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001501 parse_mount_options(ip, &filteropts);
1502
1503 // compose new unc '\\server-ip\share'
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001504 // (s => slash after hostname)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001505
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001506 mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001507
1508 // lock is required
1509 vfsflags |= MS_MANDLOCK;
1510
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001511 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001512 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001513 if (ENABLE_FEATURE_CLEAN_UP) {
1514 free(mp->mnt_fsname);
1515 free(ip);
1516 free(dotted);
1517 free(lsa);
1518 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001519 goto report_error;
1520 }
1521
1522 // Might this be an NFS filesystem?
1523
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001524 if (ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001525 && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001526 && strchr(mp->mnt_fsname, ':') != NULL
1527 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001528 rc = nfsmount(mp, vfsflags, filteropts);
1529 goto report_error;
1530 }
1531
1532 // Look at the file. (Not found isn't a failure for remount, or for
1533 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001534 // (We use stat, not lstat, in order to allow
1535 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001536
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001537 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001538 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1539 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001540 // Do we need to allocate a loopback device for it?
1541
1542 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1543 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001544 mp->mnt_fsname = NULL; /* will receive malloced loop dev name */
1545 if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) {
1546 if (errno == EPERM || errno == EACCES)
1547 bb_error_msg(bb_msg_perm_denied_are_you_root);
1548 else
1549 bb_perror_msg("cannot setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001550 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001551 }
1552
1553 // Autodetect bind mounts
1554
1555 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1556 vfsflags |= MS_BIND;
1557 }
1558
1559 /* If we know the fstype (or don't need to), jump straight
1560 * to the actual mount. */
1561
1562 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1563 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001564 else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001565 // Loop through filesystem types until mount succeeds
1566 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001567
1568 /* Initialize list of block backed filesystems. This has to be
1569 * done here so that during "mount -a", mounts after /proc shows up
1570 * can autodetect. */
1571
1572 if (!fslist) {
1573 fslist = get_block_backed_filesystems();
1574 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1575 atexit(delete_block_backed_filesystems);
1576 }
1577
1578 for (fl = fslist; fl; fl = fl->link) {
1579 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001580 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001581 if (!rc) break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001582 }
1583 }
1584
1585 // If mount failed, clean up loop file (if any).
1586
1587 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1588 del_loop(mp->mnt_fsname);
1589 if (ENABLE_FEATURE_CLEAN_UP) {
1590 free(loopFile);
1591 free(mp->mnt_fsname);
1592 }
1593 }
1594
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001595 report_error:
1596 if (ENABLE_FEATURE_CLEAN_UP)
1597 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001598
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001599 if (errno == EBUSY && ignore_busy)
1600 return 0;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001601 if (rc < 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001602 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001603 return rc;
1604}
1605
1606// Parse options, if necessary parse fstab/mtab, and call singlemount for
1607// each directory to be mounted.
1608
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001609static const char must_be_root[] ALIGN1 = "you must be root";
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001610
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001611int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001612int mount_main(int argc, char **argv)
1613{
Denis Vlasenkoda3cec92006-09-24 01:01:01 +00001614 enum { OPT_ALL = 0x10 };
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001615
1616 char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001617 char *opt_o;
1618 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001619 FILE *fstab;
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001620 int i, j, rc = 0;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001621 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001622 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001623 SKIP_DESKTOP(const int nonroot = 0;)
1624 USE_DESKTOP( int nonroot = (getuid() != 0);)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001625
1626 /* parse long options, like --bind and --move. Note that -o option
1627 * and --option are synonymous. Yes, this means --remount,rw works. */
1628
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001629 for (i = j = 0; i < argc; i++) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001630 if (argv[i][0] == '-' && argv[i][1] == '-') {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001631 append_mount_options(&cmdopts, argv[i]+2);
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001632 } else argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001633 }
Denis Vlasenkoda3cec92006-09-24 01:01:01 +00001634 argv[j] = 0;
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001635 argc = j;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001636
1637 // Parse remaining options
1638
Denis Vlasenkofb48f6c2007-08-29 11:49:41 +00001639 opt = getopt32(argv, "o:t:rwanfvsi", &opt_o, &fstype);
Denis Vlasenkoda3cec92006-09-24 01:01:01 +00001640 if (opt & 0x1) append_mount_options(&cmdopts, opt_o); // -o
1641 //if (opt & 0x2) // -t
1642 if (opt & 0x4) append_mount_options(&cmdopts, "ro"); // -r
1643 if (opt & 0x8) append_mount_options(&cmdopts, "rw"); // -w
1644 //if (opt & 0x10) // -a
Denis Vlasenkob1726782006-09-29 14:43:20 +00001645 if (opt & 0x20) USE_FEATURE_MTAB_SUPPORT(useMtab = 0); // -n
1646 if (opt & 0x40) USE_FEATURE_MTAB_SUPPORT(fakeIt = 1); // -f
Denis Vlasenko546cd182006-10-02 18:52:49 +00001647 //if (opt & 0x80) // -v: verbose (ignore)
1648 //if (opt & 0x100) // -s: sloppy (ignore)
Denis Vlasenkofb48f6c2007-08-29 11:49:41 +00001649 //if (opt & 0x200) // -i: don't call mount.<fstype> (ignore)
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001650 argv += optind;
1651 argc -= optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001652
1653 // Three or more non-option arguments? Die with a usage message.
1654
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001655 if (argc > 2) bb_show_usage();
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001656
1657 // If we have no arguments, show currently mounted filesystems
1658
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001659 if (!argc) {
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001660 if (!(opt & OPT_ALL)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001661 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1662
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001663 if (!mountTable) bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001664
Denis Vlasenko2535f122007-09-15 13:28:30 +00001665 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
1666 sizeof(getmntent_buf)))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001667 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001668 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00001669 // util-linux 2.12a happily shows rootfs...
1670 //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001671
1672 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1673 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1674 mtpair->mnt_dir, mtpair->mnt_type,
1675 mtpair->mnt_opts);
1676 }
1677 if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
1678 return EXIT_SUCCESS;
1679 }
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001680 } else storage_path = bb_simplify_path(argv[0]);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001681
1682 // When we have two arguments, the second is the directory and we can
1683 // skip looking at fstab entirely. We can always abspath() the directory
1684 // argument when we get it.
1685
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001686 if (argc == 2) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001687 if (nonroot)
1688 bb_error_msg_and_die(must_be_root);
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001689 mtpair->mnt_fsname = argv[0];
1690 mtpair->mnt_dir = argv[1];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001691 mtpair->mnt_type = fstype;
1692 mtpair->mnt_opts = cmdopts;
1693 rc = singlemount(mtpair, 0);
1694 goto clean_up;
1695 }
1696
Denis Vlasenko546cd182006-10-02 18:52:49 +00001697 i = parse_mount_options(cmdopts, 0);
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001698 if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
1699 bb_error_msg_and_die(must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00001700
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001701 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko546cd182006-10-02 18:52:49 +00001702
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001703 if (ENABLE_FEATURE_MOUNT_FLAGS
1704 && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
1705 ) {
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001706 rc = mount("", argv[0], "", i, "");
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +00001707 if (rc) bb_simple_perror_msg_and_die(argv[0]);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001708 goto clean_up;
1709 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001710
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001711 // Open either fstab or mtab
1712
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001713 fstabname = "/etc/fstab";
1714 if (i & MS_REMOUNT) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001715 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001716 }
1717 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001718 if (!fstab)
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001719 bb_perror_msg_and_die("cannot read %s", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001720
1721 // Loop through entries until we find what we're looking for.
1722
Denis Vlasenko546cd182006-10-02 18:52:49 +00001723 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001724 for (;;) {
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001725 struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001726
1727 // Get next fstab entry
1728
Denis Vlasenko2535f122007-09-15 13:28:30 +00001729 if (!getmntent_r(fstab, mtcur, getmntent_buf
1730 + (mtcur==mtpair ? sizeof(getmntent_buf)/2 : 0),
1731 sizeof(getmntent_buf)/2))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001732 {
1733 // Were we looking for something specific?
1734
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001735 if (argc) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001736
1737 // If we didn't find anything, complain.
1738
1739 if (!mtnext->mnt_fsname)
1740 bb_error_msg_and_die("can't find %s in %s",
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001741 argv[0], fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001742
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001743 mtcur = mtnext;
1744 if (nonroot) {
1745 // fstab must have "users" or "user"
1746 if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS))
1747 bb_error_msg_and_die(must_be_root);
1748 }
1749
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001750 // Mount the last thing we found.
1751
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001752 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001753 append_mount_options(&(mtcur->mnt_opts), cmdopts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001754 rc = singlemount(mtcur, 0);
1755 free(mtcur->mnt_opts);
1756 }
1757 goto clean_up;
1758 }
1759
1760 /* If we're trying to mount something specific and this isn't it,
1761 * skip it. Note we must match both the exact text in fstab (ala
1762 * "proc") or a full path from root */
1763
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001764 if (argc) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001765
1766 // Is this what we're looking for?
1767
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001768 if (strcmp(argv[0], mtcur->mnt_fsname) &&
1769 strcmp(storage_path, mtcur->mnt_fsname) &&
1770 strcmp(argv[0], mtcur->mnt_dir) &&
1771 strcmp(storage_path, mtcur->mnt_dir)) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001772
1773 // Remember this entry. Something later may have overmounted
1774 // it, and we want the _last_ match.
1775
1776 mtcur = mtnext;
1777
1778 // If we're mounting all.
1779
1780 } else {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001781 // Do we need to match a filesystem type?
Denis Vlasenkobf295dd2007-04-05 21:57:47 +00001782 if (fstype && match_fstype(mtcur, fstype)) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001783
1784 // Skip noauto and swap anyway.
1785
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001786 if (parse_mount_options(mtcur->mnt_opts, 0)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001787 & (MOUNT_NOAUTO | MOUNT_SWAP)) continue;
1788
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001789 // No, mount -a won't mount anything,
1790 // even user mounts, for mere humans.
1791
1792 if (nonroot)
1793 bb_error_msg_and_die(must_be_root);
1794
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001795 // Mount this thing.
1796
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001797 // NFS mounts want this to be xrealloc-able
1798 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001799 if (singlemount(mtcur, 1)) {
1800 /* Count number of failed mounts */
1801 rc++;
1802 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001803 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001804 }
1805 }
1806 if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab);
1807
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001808 clean_up:
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001809
1810 if (ENABLE_FEATURE_CLEAN_UP) {
1811 free(storage_path);
1812 free(cmdopts);
1813 }
1814
1815 return rc;
1816}