blob: 5e6c3ba72886d4eb18e8171fcf07b0ef30344ea4 [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 Vlasenko32d49bc2008-02-03 23:52:41 +0000312
313 // If mount failed, try
314 // helper program <mnt_type>
315 if (ENABLE_FEATURE_MOUNT_HELPERS && rc) {
316 char *args[6];
317 int errno_save = errno;
318 args[0] = mp->mnt_type;
319 rc = 1;
320 if (filteropts) {
321 args[rc++] = (char *)"-o";
322 args[rc++] = filteropts;
323 }
324 args[rc++] = mp->mnt_fsname;
325 args[rc++] = mp->mnt_dir;
326 args[rc] = NULL;
327 rc = wait4pid(spawn(args));
328 if (!rc)
329 break;
330 errno = errno_save;
331 }
332
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000333 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000334 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000335 if (!(vfsflags & MS_SILENT))
336 bb_error_msg("%s is write-protected, mounting read-only",
337 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000338 vfsflags |= MS_RDONLY;
339 }
340
Rob Landleydc0955b2006-03-14 18:16:25 +0000341 // Abort entirely if permission denied.
342
343 if (rc && errno == EPERM)
344 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
345
346 /* If the mount was successful, and we're maintaining an old-style
347 * mtab file by hand, add the new entry to it now. */
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000348 mtab:
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000349 if (ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000350 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000351 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000352 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000353 int i;
354
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000355 if (!mountTable) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000356 bb_error_msg("no %s", bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000357 goto ret;
358 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000359
360 // Add vfs string flags
361
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000362 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
363 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
364 append_mount_options(&(mp->mnt_opts), option_str);
365 option_str += strlen(option_str) + 1;
366 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000367
368 // Remove trailing / (if any) from directory we mounted on
369
Denis Vlasenko727ef942006-09-14 13:19:19 +0000370 i = strlen(mp->mnt_dir) - 1;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000371 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000372
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000373 // Convert to canonical pathnames as needed
Denis Vlasenko727ef942006-09-14 13:19:19 +0000374
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000375 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000376 fsname = 0;
Denis Vlasenko727ef942006-09-14 13:19:19 +0000377 if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000378 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000379 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000380 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000381 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000382
383 // Write and close.
384
Denis Vlasenko727ef942006-09-14 13:19:19 +0000385 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000386 endmntent(mountTable);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000387 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000388 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000389 free(fsname);
390 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000391 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000392 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000393 return rc;
394}
395
Denis Vlasenko25098f72006-09-14 15:46:33 +0000396#if ENABLE_FEATURE_MOUNT_NFS
397
398/*
399 * Linux NFS mount
400 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
401 *
402 * Licensed under GPLv2, see file LICENSE in this tarball for details.
403 *
404 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
405 * numbers to be specified on the command line.
406 *
407 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
408 * Omit the call to connect() for Linux version 1.3.11 or later.
409 *
410 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
411 * Implemented the "bg", "fg" and "retry" mount options for NFS.
412 *
413 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
414 * - added Native Language Support
415 *
416 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
417 * plus NFSv3 stuff.
418 */
419
Denis Vlasenko25098f72006-09-14 15:46:33 +0000420/* This is just a warning of a common mistake. Possibly this should be a
421 * uclibc faq entry rather than in busybox... */
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000422#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000423#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
424#endif
425
426#define MOUNTPORT 635
427#define MNTPATHLEN 1024
428#define MNTNAMLEN 255
429#define FHSIZE 32
430#define FHSIZE3 64
431
432typedef char fhandle[FHSIZE];
433
434typedef struct {
435 unsigned int fhandle3_len;
436 char *fhandle3_val;
437} fhandle3;
438
439enum mountstat3 {
440 MNT_OK = 0,
441 MNT3ERR_PERM = 1,
442 MNT3ERR_NOENT = 2,
443 MNT3ERR_IO = 5,
444 MNT3ERR_ACCES = 13,
445 MNT3ERR_NOTDIR = 20,
446 MNT3ERR_INVAL = 22,
447 MNT3ERR_NAMETOOLONG = 63,
448 MNT3ERR_NOTSUPP = 10004,
449 MNT3ERR_SERVERFAULT = 10006,
450};
451typedef enum mountstat3 mountstat3;
452
453struct fhstatus {
454 unsigned int fhs_status;
455 union {
456 fhandle fhs_fhandle;
457 } fhstatus_u;
458};
459typedef struct fhstatus fhstatus;
460
461struct mountres3_ok {
462 fhandle3 fhandle;
463 struct {
464 unsigned int auth_flavours_len;
465 char *auth_flavours_val;
466 } auth_flavours;
467};
468typedef struct mountres3_ok mountres3_ok;
469
470struct mountres3 {
471 mountstat3 fhs_status;
472 union {
473 mountres3_ok mountinfo;
474 } mountres3_u;
475};
476typedef struct mountres3 mountres3;
477
478typedef char *dirpath;
479
480typedef char *name;
481
482typedef struct mountbody *mountlist;
483
484struct mountbody {
485 name ml_hostname;
486 dirpath ml_directory;
487 mountlist ml_next;
488};
489typedef struct mountbody mountbody;
490
491typedef struct groupnode *groups;
492
493struct groupnode {
494 name gr_name;
495 groups gr_next;
496};
497typedef struct groupnode groupnode;
498
499typedef struct exportnode *exports;
500
501struct exportnode {
502 dirpath ex_dir;
503 groups ex_groups;
504 exports ex_next;
505};
506typedef struct exportnode exportnode;
507
508struct ppathcnf {
509 int pc_link_max;
510 short pc_max_canon;
511 short pc_max_input;
512 short pc_name_max;
513 short pc_path_max;
514 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000515 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000516 char pc_xxx;
517 short pc_mask[2];
518};
519typedef struct ppathcnf ppathcnf;
520
521#define MOUNTPROG 100005
522#define MOUNTVERS 1
523
524#define MOUNTPROC_NULL 0
525#define MOUNTPROC_MNT 1
526#define MOUNTPROC_DUMP 2
527#define MOUNTPROC_UMNT 3
528#define MOUNTPROC_UMNTALL 4
529#define MOUNTPROC_EXPORT 5
530#define MOUNTPROC_EXPORTALL 6
531
532#define MOUNTVERS_POSIX 2
533
534#define MOUNTPROC_PATHCONF 7
535
536#define MOUNT_V3 3
537
538#define MOUNTPROC3_NULL 0
539#define MOUNTPROC3_MNT 1
540#define MOUNTPROC3_DUMP 2
541#define MOUNTPROC3_UMNT 3
542#define MOUNTPROC3_UMNTALL 4
543#define MOUNTPROC3_EXPORT 5
544
545enum {
546#ifndef NFS_FHSIZE
547 NFS_FHSIZE = 32,
548#endif
549#ifndef NFS_PORT
550 NFS_PORT = 2049
551#endif
552};
553
Denis Vlasenko25098f72006-09-14 15:46:33 +0000554/*
555 * We want to be able to compile mount on old kernels in such a way
556 * that the binary will work well on more recent kernels.
557 * Thus, if necessary we teach nfsmount.c the structure of new fields
558 * that will come later.
559 *
560 * Moreover, the new kernel includes conflict with glibc includes
561 * so it is easiest to ignore the kernel altogether (at compile time).
562 */
563
564struct nfs2_fh {
565 char data[32];
566};
567struct nfs3_fh {
568 unsigned short size;
569 unsigned char data[64];
570};
571
572struct nfs_mount_data {
573 int version; /* 1 */
574 int fd; /* 1 */
575 struct nfs2_fh old_root; /* 1 */
576 int flags; /* 1 */
577 int rsize; /* 1 */
578 int wsize; /* 1 */
579 int timeo; /* 1 */
580 int retrans; /* 1 */
581 int acregmin; /* 1 */
582 int acregmax; /* 1 */
583 int acdirmin; /* 1 */
584 int acdirmax; /* 1 */
585 struct sockaddr_in addr; /* 1 */
586 char hostname[256]; /* 1 */
587 int namlen; /* 2 */
588 unsigned int bsize; /* 3 */
589 struct nfs3_fh root; /* 4 */
590};
591
592/* bits in the flags field */
593enum {
594 NFS_MOUNT_SOFT = 0x0001, /* 1 */
595 NFS_MOUNT_INTR = 0x0002, /* 1 */
596 NFS_MOUNT_SECURE = 0x0004, /* 1 */
597 NFS_MOUNT_POSIX = 0x0008, /* 1 */
598 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
599 NFS_MOUNT_NOAC = 0x0020, /* 1 */
600 NFS_MOUNT_TCP = 0x0040, /* 2 */
601 NFS_MOUNT_VER3 = 0x0080, /* 3 */
602 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
603 NFS_MOUNT_NONLM = 0x0200 /* 3 */
604};
605
606
607/*
608 * We need to translate between nfs status return values and
609 * the local errno values which may not be the same.
610 *
611 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
612 * "after #include <errno.h> the symbol errno is reserved for any use,
613 * it cannot even be used as a struct tag or field name".
614 */
615
616#ifndef EDQUOT
617#define EDQUOT ENOSPC
618#endif
619
620// Convert each NFSERR_BLAH into EBLAH
621
622static const struct {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000623 short stat;
624 short errnum;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000625} nfs_errtbl[] = {
626 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
627 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
628 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
629 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
630};
631
632static char *nfs_strerror(int status)
633{
634 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000635
636 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
637 if (nfs_errtbl[i].stat == status)
638 return strerror(nfs_errtbl[i].errnum);
639 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000640 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000641}
642
643static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
644{
645 if (!xdr_opaque(xdrs, objp, FHSIZE))
646 return FALSE;
647 return TRUE;
648}
649
650static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
651{
652 if (!xdr_u_int(xdrs, &objp->fhs_status))
653 return FALSE;
654 switch (objp->fhs_status) {
655 case 0:
656 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
657 return FALSE;
658 break;
659 default:
660 break;
661 }
662 return TRUE;
663}
664
665static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
666{
667 if (!xdr_string(xdrs, objp, MNTPATHLEN))
668 return FALSE;
669 return TRUE;
670}
671
672static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
673{
674 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
675 return FALSE;
676 return TRUE;
677}
678
679static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
680{
681 if (!xdr_fhandle3(xdrs, &objp->fhandle))
682 return FALSE;
683 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
684 sizeof (int), (xdrproc_t) xdr_int))
685 return FALSE;
686 return TRUE;
687}
688
689static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
690{
691 if (!xdr_enum(xdrs, (enum_t *) objp))
692 return FALSE;
693 return TRUE;
694}
695
696static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
697{
698 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
699 return FALSE;
700 switch (objp->fhs_status) {
701 case MNT_OK:
702 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
703 return FALSE;
704 break;
705 default:
706 break;
707 }
708 return TRUE;
709}
710
711#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
712
Denis Vlasenkob9256052007-09-28 10:29:17 +0000713static smalluint nfs_mount_version;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000714
715/*
716 * Unfortunately, the kernel prints annoying console messages
717 * in case of an unexpected nfs mount version (instead of
718 * just returning some error). Therefore we'll have to try
719 * and figure out what version the kernel expects.
720 *
721 * Variables:
722 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
723 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
724 * nfs_mount_version: version this source and running kernel can handle
725 */
726static void
727find_kernel_nfs_mount_version(void)
728{
Denis Vlasenkob9256052007-09-28 10:29:17 +0000729 int kernel_version;
730
731 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000732 return;
733
734 nfs_mount_version = 4; /* default */
735
736 kernel_version = get_linux_version_code();
737 if (kernel_version) {
738 if (kernel_version < KERNEL_VERSION(2,1,32))
739 nfs_mount_version = 1;
740 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
741 (kernel_version >= KERNEL_VERSION(2,3,0) &&
742 kernel_version < KERNEL_VERSION(2,3,99)))
743 nfs_mount_version = 3;
744 /* else v4 since 2.3.99pre4 */
745 }
746}
747
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +0000748static void
Denis Vlasenkob9256052007-09-28 10:29:17 +0000749get_mountport(struct pmap *pm_mnt,
750 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +0000751 long unsigned prog,
752 long unsigned version,
753 long unsigned proto,
754 long unsigned port)
755{
756 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000757
758 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +0000759/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
760 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000761 pmap = pmap_getmaps(server_addr);
762
763 if (version > MAX_NFSPROT)
764 version = MAX_NFSPROT;
765 if (!prog)
766 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000767 pm_mnt->pm_prog = prog;
768 pm_mnt->pm_vers = version;
769 pm_mnt->pm_prot = proto;
770 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000771
Denis Vlasenko25098f72006-09-14 15:46:33 +0000772 while (pmap) {
773 if (pmap->pml_map.pm_prog != prog)
774 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000775 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000776 goto next;
777 if (version > 2 && pmap->pml_map.pm_vers != version)
778 goto next;
779 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
780 goto next;
781 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
Denis Vlasenkob9256052007-09-28 10:29:17 +0000782 (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto) ||
Denis Vlasenko25098f72006-09-14 15:46:33 +0000783 (port && pmap->pml_map.pm_port != port))
784 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000785 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
786 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +0000787 pmap = pmap->pml_next;
788 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000789 if (!pm_mnt->pm_vers)
790 pm_mnt->pm_vers = MOUNTVERS;
791 if (!pm_mnt->pm_port)
792 pm_mnt->pm_port = MOUNTPORT;
793 if (!pm_mnt->pm_prot)
794 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000795}
796
Denis Vlasenkof0000652007-09-04 18:30:26 +0000797#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +0000798static int daemonize(void)
799{
800 int fd;
801 int pid = fork();
802 if (pid < 0) /* error */
803 return -errno;
804 if (pid > 0) /* parent */
805 return 0;
806 /* child */
807 fd = xopen(bb_dev_null, O_RDWR);
808 dup2(fd, 0);
809 dup2(fd, 1);
810 dup2(fd, 2);
Denis Vlasenko9af7c9d2007-01-19 21:19:35 +0000811 while (fd > 2) close(fd--);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000812 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +0000813 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000814 logmode = LOGMODE_SYSLOG;
815 return 1;
816}
Denis Vlasenkof0000652007-09-04 18:30:26 +0000817#else
818static inline int daemonize(void) { return -ENOSYS; }
819#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000820
821// TODO
822static inline int we_saw_this_host_before(const char *hostname)
823{
824 return 0;
825}
826
827/* RPC strerror analogs are terminally idiotic:
828 * *mandatory* prefix and \n at end.
829 * This hopefully helps. Usage:
830 * error_msg_rpc(clnt_*error*(" ")) */
831static void error_msg_rpc(const char *msg)
832{
Denis Vlasenko23514fe2006-09-19 14:07:52 +0000833 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000834 while (msg[0] == ' ' || msg[0] == ':') msg++;
835 len = strlen(msg);
836 while (len && msg[len-1] == '\n') len--;
837 bb_error_msg("%.*s", len, msg);
838}
839
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +0000840// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko25098f72006-09-14 15:46:33 +0000841static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
842{
843 CLIENT *mclient;
844 char *hostname;
845 char *pathname;
846 char *mounthost;
847 struct nfs_mount_data data;
848 char *opt;
849 struct hostent *hp;
850 struct sockaddr_in server_addr;
851 struct sockaddr_in mount_server_addr;
852 int msock, fsock;
853 union {
854 struct fhstatus nfsv2;
855 struct mountres3 nfsv3;
856 } status;
857 int daemonized;
858 char *s;
859 int port;
860 int mountport;
861 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +0000862#if BB_MMU
863 int bg = 0;
864#else
865 enum { bg = 0 };
866#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000867 int soft;
868 int intr;
869 int posix;
870 int nocto;
871 int noac;
872 int nolock;
873 int retry;
874 int tcp;
875 int mountprog;
876 int mountvers;
877 int nfsprog;
878 int nfsvers;
879 int retval;
880
881 find_kernel_nfs_mount_version();
882
883 daemonized = 0;
884 mounthost = NULL;
885 retval = ETIMEDOUT;
886 msock = fsock = -1;
887 mclient = NULL;
888
889 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
890
891 filteropts = xstrdup(filteropts); /* going to trash it later... */
892
893 hostname = xstrdup(mp->mnt_fsname);
894 /* mount_main() guarantees that ':' is there */
895 s = strchr(hostname, ':');
896 pathname = s + 1;
897 *s = '\0';
898 /* Ignore all but first hostname in replicated mounts
899 until they can be fully supported. (mack@sgi.com) */
900 s = strchr(hostname, ',');
901 if (s) {
902 *s = '\0';
903 bb_error_msg("warning: multiple hostnames not supported");
904 }
905
906 server_addr.sin_family = AF_INET;
907 if (!inet_aton(hostname, &server_addr.sin_addr)) {
908 hp = gethostbyname(hostname);
909 if (hp == NULL) {
910 bb_herror_msg("%s", hostname);
911 goto fail;
912 }
913 if (hp->h_length > sizeof(struct in_addr)) {
914 bb_error_msg("got bad hp->h_length");
915 hp->h_length = sizeof(struct in_addr);
916 }
917 memcpy(&server_addr.sin_addr,
918 hp->h_addr, hp->h_length);
919 }
920
921 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
922
923 /* add IP address to mtab options for use when unmounting */
924
925 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
926 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
927 } else {
928 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
929 mp->mnt_opts[0] ? "," : "",
930 inet_ntoa(server_addr.sin_addr));
931 free(mp->mnt_opts);
932 mp->mnt_opts = tmp;
933 }
934
935 /* Set default options.
936 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
937 * let the kernel decide.
938 * timeo is filled in after we know whether it'll be TCP or UDP. */
939 memset(&data, 0, sizeof(data));
940 data.retrans = 3;
941 data.acregmin = 3;
942 data.acregmax = 60;
943 data.acdirmin = 30;
944 data.acdirmax = 60;
945 data.namlen = NAME_MAX;
946
Denis Vlasenko25098f72006-09-14 15:46:33 +0000947 soft = 0;
948 intr = 0;
949 posix = 0;
950 nocto = 0;
951 nolock = 0;
952 noac = 0;
953 retry = 10000; /* 10000 minutes ~ 1 week */
954 tcp = 0;
955
956 mountprog = MOUNTPROG;
957 mountvers = 0;
958 port = 0;
959 mountport = 0;
960 nfsprog = 100003;
961 nfsvers = 0;
962
963 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +0000964 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +0000965 char *opteq = strchr(opt, '=');
966 if (opteq) {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000967 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000968 /* 0 */ "rsize\0"
969 /* 1 */ "wsize\0"
970 /* 2 */ "timeo\0"
971 /* 3 */ "retrans\0"
972 /* 4 */ "acregmin\0"
973 /* 5 */ "acregmax\0"
974 /* 6 */ "acdirmin\0"
975 /* 7 */ "acdirmax\0"
976 /* 8 */ "actimeo\0"
977 /* 9 */ "retry\0"
978 /* 10 */ "port\0"
979 /* 11 */ "mountport\0"
980 /* 12 */ "mounthost\0"
981 /* 13 */ "mountprog\0"
982 /* 14 */ "mountvers\0"
983 /* 15 */ "nfsprog\0"
984 /* 16 */ "nfsvers\0"
985 /* 17 */ "vers\0"
986 /* 18 */ "proto\0"
987 /* 19 */ "namlen\0"
988 /* 20 */ "addr\0";
Denis Vlasenko13858992006-10-08 12:49:22 +0000989 int val = xatoi_u(opteq + 1);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000990 *opteq = '\0';
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000991 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +0000992 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +0000993 data.rsize = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +0000994 break;
995 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +0000996 data.wsize = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +0000997 break;
998 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +0000999 data.timeo = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001000 break;
1001 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001002 data.retrans = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001003 break;
1004 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001005 data.acregmin = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001006 break;
1007 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001008 data.acregmax = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001009 break;
1010 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001011 data.acdirmin = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001012 break;
1013 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001014 data.acdirmax = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001015 break;
1016 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001017 data.acregmin = val;
1018 data.acregmax = val;
1019 data.acdirmin = val;
1020 data.acdirmax = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001021 break;
1022 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001023 retry = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001024 break;
1025 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001026 port = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001027 break;
1028 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001029 mountport = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001030 break;
1031 case 12: // "mounthost"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001032 mounthost = xstrndup(opteq+1,
Denis Vlasenko5af906e2006-11-05 18:05:09 +00001033 strcspn(opteq+1," \t\n\r,"));
Denis Vlasenko68f21872006-10-26 01:47:34 +00001034 break;
1035 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001036 mountprog = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001037 break;
1038 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001039 mountvers = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001040 break;
1041 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001042 nfsprog = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001043 break;
1044 case 16: // "nfsvers"
1045 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001046 nfsvers = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001047 break;
1048 case 18: // "proto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001049 if (!strncmp(opteq+1, "tcp", 3))
1050 tcp = 1;
1051 else if (!strncmp(opteq+1, "udp", 3))
1052 tcp = 0;
1053 else
1054 bb_error_msg("warning: unrecognized proto= option");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001055 break;
1056 case 19: // "namlen"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001057 if (nfs_mount_version >= 2)
1058 data.namlen = val;
1059 else
1060 bb_error_msg("warning: option namlen is not supported\n");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001061 break;
1062 case 20: // "addr" - ignore
1063 break;
1064 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001065 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1066 goto fail;
1067 }
1068 }
1069 else {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001070 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001071 "bg\0"
1072 "fg\0"
1073 "soft\0"
1074 "hard\0"
1075 "intr\0"
1076 "posix\0"
1077 "cto\0"
1078 "ac\0"
1079 "tcp\0"
1080 "udp\0"
1081 "lock\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001082 int val = 1;
1083 if (!strncmp(opt, "no", 2)) {
1084 val = 0;
1085 opt += 2;
1086 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001087 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001088 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001089#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001090 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001091#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001092 break;
1093 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001094#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001095 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001096#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001097 break;
1098 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001099 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001100 break;
1101 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001102 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001103 break;
1104 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001105 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001106 break;
1107 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001108 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001109 break;
1110 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001111 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001112 break;
1113 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001114 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001115 break;
1116 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001117 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001118 break;
1119 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001120 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001121 break;
1122 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001123 if (nfs_mount_version >= 3)
1124 nolock = !val;
1125 else
1126 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001127 break;
1128 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001129 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1130 goto fail;
1131 }
1132 }
1133 }
1134 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1135
1136 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1137 | (intr ? NFS_MOUNT_INTR : 0)
1138 | (posix ? NFS_MOUNT_POSIX : 0)
1139 | (nocto ? NFS_MOUNT_NOCTO : 0)
1140 | (noac ? NFS_MOUNT_NOAC : 0);
1141 if (nfs_mount_version >= 2)
1142 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1143 if (nfs_mount_version >= 3)
1144 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1145 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1146 bb_error_msg("NFSv%d not supported", nfsvers);
1147 goto fail;
1148 }
1149 if (nfsvers && !mountvers)
1150 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1151 if (nfsvers && nfsvers < mountvers) {
1152 mountvers = nfsvers;
1153 }
1154
1155 /* Adjust options if none specified */
1156 if (!data.timeo)
1157 data.timeo = tcp ? 70 : 7;
1158
Denis Vlasenko25098f72006-09-14 15:46:33 +00001159 data.version = nfs_mount_version;
1160
1161 if (vfsflags & MS_REMOUNT)
1162 goto do_mount;
1163
1164 /*
1165 * If the previous mount operation on the same host was
1166 * backgrounded, and the "bg" for this mount is also set,
1167 * give up immediately, to avoid the initial timeout.
1168 */
1169 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001170 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001171 if (daemonized <= 0) { /* parent or error */
1172 retval = -daemonized;
1173 goto ret;
1174 }
1175 }
1176
1177 /* create mount daemon client */
1178 /* See if the nfs host = mount host. */
1179 if (mounthost) {
1180 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1181 mount_server_addr.sin_family = AF_INET;
1182 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1183 } else {
1184 hp = gethostbyname(mounthost);
1185 if (hp == NULL) {
1186 bb_herror_msg("%s", mounthost);
1187 goto fail;
1188 } else {
1189 if (hp->h_length > sizeof(struct in_addr)) {
1190 bb_error_msg("got bad hp->h_length?");
1191 hp->h_length = sizeof(struct in_addr);
1192 }
1193 mount_server_addr.sin_family = AF_INET;
1194 memcpy(&mount_server_addr.sin_addr,
1195 hp->h_addr, hp->h_length);
1196 }
1197 }
1198 }
1199
1200 /*
1201 * The following loop implements the mount retries. When the mount
1202 * times out, and the "bg" option is set, we background ourself
1203 * and continue trying.
1204 *
1205 * The case where the mount point is not present and the "bg"
1206 * option is set, is treated as a timeout. This is done to
1207 * support nested mounts.
1208 *
1209 * The "retry" count specified by the user is the number of
1210 * minutes to retry before giving up.
1211 */
1212 {
1213 struct timeval total_timeout;
1214 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001215 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001216 time_t t;
1217 time_t prevt;
1218 time_t timeout;
1219
1220 retry_timeout.tv_sec = 3;
1221 retry_timeout.tv_usec = 0;
1222 total_timeout.tv_sec = 20;
1223 total_timeout.tv_usec = 0;
1224 timeout = time(NULL) + 60 * retry;
1225 prevt = 0;
1226 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001227 retry:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001228 /* be careful not to use too many CPU cycles */
1229 if (t - prevt < 30)
1230 sleep(30);
1231
Denis Vlasenkob9256052007-09-28 10:29:17 +00001232 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001233 mountprog,
1234 mountvers,
1235 proto,
1236 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001237 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001238
1239 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001240 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001241 msock = RPC_ANYSOCK;
1242
Denis Vlasenkob9256052007-09-28 10:29:17 +00001243 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001244 case IPPROTO_UDP:
1245 mclient = clntudp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001246 pm_mnt.pm_prog,
1247 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001248 retry_timeout,
1249 &msock);
1250 if (mclient)
1251 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001252 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001253 msock = RPC_ANYSOCK;
1254 case IPPROTO_TCP:
1255 mclient = clnttcp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001256 pm_mnt.pm_prog,
1257 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001258 &msock, 0, 0);
1259 break;
1260 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001261 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001262 }
1263 if (!mclient) {
1264 if (!daemonized && prevt == 0)
1265 error_msg_rpc(clnt_spcreateerror(" "));
1266 } else {
1267 enum clnt_stat clnt_stat;
1268 /* try to mount hostname:pathname */
1269 mclient->cl_auth = authunix_create_default();
1270
1271 /* make pointers in xdr_mountres3 NULL so
1272 * that xdr_array allocates memory for us
1273 */
1274 memset(&status, 0, sizeof(status));
1275
Denis Vlasenkob9256052007-09-28 10:29:17 +00001276 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001277 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1278 (xdrproc_t) xdr_dirpath,
1279 (caddr_t) &pathname,
1280 (xdrproc_t) xdr_mountres3,
1281 (caddr_t) &status,
1282 total_timeout);
1283 else
1284 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1285 (xdrproc_t) xdr_dirpath,
1286 (caddr_t) &pathname,
1287 (xdrproc_t) xdr_fhstatus,
1288 (caddr_t) &status,
1289 total_timeout);
1290
1291 if (clnt_stat == RPC_SUCCESS)
1292 goto prepare_kernel_data; /* we're done */
1293 if (errno != ECONNREFUSED) {
1294 error_msg_rpc(clnt_sperror(mclient, " "));
1295 goto fail; /* don't retry */
1296 }
1297 /* Connection refused */
1298 if (!daemonized && prevt == 0) /* print just once */
1299 error_msg_rpc(clnt_sperror(mclient, " "));
1300 auth_destroy(mclient->cl_auth);
1301 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001302 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001303 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001304 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001305 }
1306
1307 /* Timeout. We are going to retry... maybe */
1308
1309 if (!bg)
1310 goto fail;
1311 if (!daemonized) {
1312 daemonized = daemonize();
1313 if (daemonized <= 0) { /* parent or error */
1314 retval = -daemonized;
1315 goto ret;
1316 }
1317 }
1318 prevt = t;
1319 t = time(NULL);
1320 if (t >= timeout)
1321 /* TODO error message */
1322 goto fail;
1323
1324 goto retry;
1325 }
1326
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001327 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001328
1329 if (nfsvers == 2) {
1330 if (status.nfsv2.fhs_status != 0) {
1331 bb_error_msg("%s:%s failed, reason given by server: %s",
1332 hostname, pathname,
1333 nfs_strerror(status.nfsv2.fhs_status));
1334 goto fail;
1335 }
1336 memcpy(data.root.data,
1337 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1338 NFS_FHSIZE);
1339 data.root.size = NFS_FHSIZE;
1340 memcpy(data.old_root.data,
1341 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1342 NFS_FHSIZE);
1343 } else {
1344 fhandle3 *my_fhandle;
1345 if (status.nfsv3.fhs_status != 0) {
1346 bb_error_msg("%s:%s failed, reason given by server: %s",
1347 hostname, pathname,
1348 nfs_strerror(status.nfsv3.fhs_status));
1349 goto fail;
1350 }
1351 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1352 memset(data.old_root.data, 0, NFS_FHSIZE);
1353 memset(&data.root, 0, sizeof(data.root));
1354 data.root.size = my_fhandle->fhandle3_len;
1355 memcpy(data.root.data,
1356 (char *) my_fhandle->fhandle3_val,
1357 my_fhandle->fhandle3_len);
1358
1359 data.flags |= NFS_MOUNT_VER3;
1360 }
1361
1362 /* create nfs socket for kernel */
1363
1364 if (tcp) {
1365 if (nfs_mount_version < 3) {
1366 bb_error_msg("NFS over TCP is not supported");
1367 goto fail;
1368 }
1369 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1370 } else
1371 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1372 if (fsock < 0) {
1373 bb_perror_msg("nfs socket");
1374 goto fail;
1375 }
1376 if (bindresvport(fsock, 0) < 0) {
1377 bb_perror_msg("nfs bindresvport");
1378 goto fail;
1379 }
1380 if (port == 0) {
1381 server_addr.sin_port = PMAPPORT;
1382 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1383 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1384 if (port == 0)
1385 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001386 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001387 server_addr.sin_port = htons(port);
1388
1389 /* prepare data structure for kernel */
1390
1391 data.fd = fsock;
1392 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1393 strncpy(data.hostname, hostname, sizeof(data.hostname));
1394
1395 /* clean up */
1396
1397 auth_destroy(mclient->cl_auth);
1398 clnt_destroy(mclient);
1399 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001400 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001401
1402 if (bg) {
1403 /* We must wait until mount directory is available */
1404 struct stat statbuf;
1405 int delay = 1;
1406 while (stat(mp->mnt_dir, &statbuf) == -1) {
1407 if (!daemonized) {
1408 daemonized = daemonize();
1409 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001410 // FIXME: parent doesn't close fsock - ??!
Denis Vlasenko25098f72006-09-14 15:46:33 +00001411 retval = -daemonized;
1412 goto ret;
1413 }
1414 }
1415 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1416 delay *= 2;
1417 if (delay > 30)
1418 delay = 30;
1419 }
1420 }
1421
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001422 do_mount: /* perform actual mount */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001423
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001424 mp->mnt_type = (char*)"nfs";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001425 retval = mount_it_now(mp, vfsflags, (char*)&data);
1426 goto ret;
1427
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001428 fail: /* abort */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001429
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001430 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001431 if (mclient) {
1432 auth_destroy(mclient->cl_auth);
1433 clnt_destroy(mclient);
1434 }
1435 close(msock);
1436 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001437 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001438 close(fsock);
1439
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001440 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001441 free(hostname);
1442 free(mounthost);
1443 free(filteropts);
1444 return retval;
1445}
1446
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001447#else /* !ENABLE_FEATURE_MOUNT_NFS */
1448
1449/* Never called. Call should be optimized out. */
1450int nfsmount(struct mntent *mp, int vfsflags, char *filteropts);
1451
1452#endif /* !ENABLE_FEATURE_MOUNT_NFS */
1453
1454// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1455// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001456// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001457static int singlemount(struct mntent *mp, int ignore_busy)
1458{
1459 int rc = -1, vfsflags;
1460 char *loopFile = 0, *filteropts = 0;
1461 llist_t *fl = 0;
1462 struct stat st;
1463
1464 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1465
1466 // Treat fstype "auto" as unspecified.
1467
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001468 if (mp->mnt_type && strcmp(mp->mnt_type,"auto") == 0)
1469 mp->mnt_type = 0;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001470
Denis Vlasenko2535f122007-09-15 13:28:30 +00001471 // Might this be a virtual filesystem?
1472
1473 if (ENABLE_FEATURE_MOUNT_HELPERS
Denis Vlasenkoc03e8722007-12-26 20:56:55 +00001474 && (strchr(mp->mnt_fsname, '#'))
Denis Vlasenko2535f122007-09-15 13:28:30 +00001475 ) {
1476 char *s, *p, *args[35];
1477 int n = 0;
1478 for (s = p = mp->mnt_fsname; *s && n < 35-3; ++s) {
1479 if (s[0] == '#' && s[1] != '#') {
1480 *s = '\0';
1481 args[n++] = p;
1482 p = s + 1;
1483 }
1484 }
1485 args[n++] = p;
1486 args[n++] = mp->mnt_dir;
1487 args[n] = NULL;
1488 rc = wait4pid(xspawn(args));
1489 goto report_error;
1490 }
1491
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001492 // Might this be an CIFS filesystem?
1493
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001494 if (ENABLE_FEATURE_MOUNT_CIFS
1495 && (!mp->mnt_type || strcmp(mp->mnt_type,"cifs") == 0)
1496 && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\')
1497 && mp->mnt_fsname[0]==mp->mnt_fsname[1]
1498 ) {
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001499 len_and_sockaddr *lsa;
1500 char *ip, *dotted;
1501 char *s;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001502
1503 rc = 1;
1504 // Replace '/' with '\' and verify that unc points to "//server/share".
1505
1506 for (s = mp->mnt_fsname; *s; ++s)
1507 if (*s == '/') *s = '\\';
1508
1509 // get server IP
1510
1511 s = strrchr(mp->mnt_fsname, '\\');
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001512 if (s <= mp->mnt_fsname+1) goto report_error;
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001513 *s = '\0';
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001514 lsa = host2sockaddr(mp->mnt_fsname+2, 0);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001515 *s = '\\';
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001516 if (!lsa) goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001517
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001518 // insert ip=... option into string flags.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001519
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001520 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001521 ip = xasprintf("ip=%s", dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001522 parse_mount_options(ip, &filteropts);
1523
1524 // compose new unc '\\server-ip\share'
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001525 // (s => slash after hostname)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001526
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001527 mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001528
1529 // lock is required
1530 vfsflags |= MS_MANDLOCK;
1531
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001532 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001533 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001534 if (ENABLE_FEATURE_CLEAN_UP) {
1535 free(mp->mnt_fsname);
1536 free(ip);
1537 free(dotted);
1538 free(lsa);
1539 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001540 goto report_error;
1541 }
1542
1543 // Might this be an NFS filesystem?
1544
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001545 if (ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001546 && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001547 && strchr(mp->mnt_fsname, ':') != NULL
1548 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001549 rc = nfsmount(mp, vfsflags, filteropts);
1550 goto report_error;
1551 }
1552
1553 // Look at the file. (Not found isn't a failure for remount, or for
1554 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001555 // (We use stat, not lstat, in order to allow
1556 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001557
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001558 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001559 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1560 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001561 // Do we need to allocate a loopback device for it?
1562
1563 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1564 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001565 mp->mnt_fsname = NULL; /* will receive malloced loop dev name */
1566 if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) {
1567 if (errno == EPERM || errno == EACCES)
1568 bb_error_msg(bb_msg_perm_denied_are_you_root);
1569 else
1570 bb_perror_msg("cannot setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001571 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001572 }
1573
1574 // Autodetect bind mounts
1575
1576 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1577 vfsflags |= MS_BIND;
1578 }
1579
1580 /* If we know the fstype (or don't need to), jump straight
1581 * to the actual mount. */
1582
1583 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1584 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001585 else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001586 // Loop through filesystem types until mount succeeds
1587 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001588
1589 /* Initialize list of block backed filesystems. This has to be
1590 * done here so that during "mount -a", mounts after /proc shows up
1591 * can autodetect. */
1592
1593 if (!fslist) {
1594 fslist = get_block_backed_filesystems();
1595 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1596 atexit(delete_block_backed_filesystems);
1597 }
1598
1599 for (fl = fslist; fl; fl = fl->link) {
1600 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001601 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001602 if (!rc) break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001603 }
1604 }
1605
1606 // If mount failed, clean up loop file (if any).
1607
1608 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1609 del_loop(mp->mnt_fsname);
1610 if (ENABLE_FEATURE_CLEAN_UP) {
1611 free(loopFile);
1612 free(mp->mnt_fsname);
1613 }
1614 }
1615
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001616 report_error:
1617 if (ENABLE_FEATURE_CLEAN_UP)
1618 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001619
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001620 if (errno == EBUSY && ignore_busy)
1621 return 0;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001622 if (rc < 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001623 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001624 return rc;
1625}
1626
1627// Parse options, if necessary parse fstab/mtab, and call singlemount for
1628// each directory to be mounted.
1629
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001630static const char must_be_root[] ALIGN1 = "you must be root";
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001631
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001632int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001633int mount_main(int argc, char **argv)
1634{
Denis Vlasenkoda3cec92006-09-24 01:01:01 +00001635 enum { OPT_ALL = 0x10 };
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001636
1637 char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001638 char *opt_o;
1639 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001640 FILE *fstab;
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001641 int i, j, rc = 0;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001642 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001643 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001644 SKIP_DESKTOP(const int nonroot = 0;)
1645 USE_DESKTOP( int nonroot = (getuid() != 0);)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001646
1647 /* parse long options, like --bind and --move. Note that -o option
1648 * and --option are synonymous. Yes, this means --remount,rw works. */
1649
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001650 for (i = j = 0; i < argc; i++) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001651 if (argv[i][0] == '-' && argv[i][1] == '-') {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001652 append_mount_options(&cmdopts, argv[i]+2);
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001653 } else argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001654 }
Denis Vlasenkoda3cec92006-09-24 01:01:01 +00001655 argv[j] = 0;
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001656 argc = j;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001657
1658 // Parse remaining options
1659
Denis Vlasenkofb48f6c2007-08-29 11:49:41 +00001660 opt = getopt32(argv, "o:t:rwanfvsi", &opt_o, &fstype);
Denis Vlasenkoda3cec92006-09-24 01:01:01 +00001661 if (opt & 0x1) append_mount_options(&cmdopts, opt_o); // -o
1662 //if (opt & 0x2) // -t
1663 if (opt & 0x4) append_mount_options(&cmdopts, "ro"); // -r
1664 if (opt & 0x8) append_mount_options(&cmdopts, "rw"); // -w
1665 //if (opt & 0x10) // -a
Denis Vlasenkob1726782006-09-29 14:43:20 +00001666 if (opt & 0x20) USE_FEATURE_MTAB_SUPPORT(useMtab = 0); // -n
1667 if (opt & 0x40) USE_FEATURE_MTAB_SUPPORT(fakeIt = 1); // -f
Denis Vlasenko546cd182006-10-02 18:52:49 +00001668 //if (opt & 0x80) // -v: verbose (ignore)
1669 //if (opt & 0x100) // -s: sloppy (ignore)
Denis Vlasenkofb48f6c2007-08-29 11:49:41 +00001670 //if (opt & 0x200) // -i: don't call mount.<fstype> (ignore)
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001671 argv += optind;
1672 argc -= optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001673
1674 // Three or more non-option arguments? Die with a usage message.
1675
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001676 if (argc > 2) bb_show_usage();
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001677
1678 // If we have no arguments, show currently mounted filesystems
1679
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001680 if (!argc) {
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001681 if (!(opt & OPT_ALL)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001682 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1683
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001684 if (!mountTable) bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001685
Denis Vlasenko2535f122007-09-15 13:28:30 +00001686 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
1687 sizeof(getmntent_buf)))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001688 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001689 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00001690 // util-linux 2.12a happily shows rootfs...
1691 //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001692
1693 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1694 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1695 mtpair->mnt_dir, mtpair->mnt_type,
1696 mtpair->mnt_opts);
1697 }
1698 if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
1699 return EXIT_SUCCESS;
1700 }
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001701 } else storage_path = bb_simplify_path(argv[0]);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001702
1703 // When we have two arguments, the second is the directory and we can
1704 // skip looking at fstab entirely. We can always abspath() the directory
1705 // argument when we get it.
1706
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001707 if (argc == 2) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001708 if (nonroot)
1709 bb_error_msg_and_die(must_be_root);
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001710 mtpair->mnt_fsname = argv[0];
1711 mtpair->mnt_dir = argv[1];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001712 mtpair->mnt_type = fstype;
1713 mtpair->mnt_opts = cmdopts;
1714 rc = singlemount(mtpair, 0);
1715 goto clean_up;
1716 }
1717
Denis Vlasenko546cd182006-10-02 18:52:49 +00001718 i = parse_mount_options(cmdopts, 0);
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001719 if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
1720 bb_error_msg_and_die(must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00001721
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001722 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko546cd182006-10-02 18:52:49 +00001723
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001724 if (ENABLE_FEATURE_MOUNT_FLAGS
1725 && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
1726 ) {
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001727 rc = mount("", argv[0], "", i, "");
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +00001728 if (rc) bb_simple_perror_msg_and_die(argv[0]);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001729 goto clean_up;
1730 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001731
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001732 // Open either fstab or mtab
1733
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001734 fstabname = "/etc/fstab";
1735 if (i & MS_REMOUNT) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001736 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001737 }
1738 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001739 if (!fstab)
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001740 bb_perror_msg_and_die("cannot read %s", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001741
1742 // Loop through entries until we find what we're looking for.
1743
Denis Vlasenko546cd182006-10-02 18:52:49 +00001744 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001745 for (;;) {
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001746 struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001747
1748 // Get next fstab entry
1749
Denis Vlasenko2535f122007-09-15 13:28:30 +00001750 if (!getmntent_r(fstab, mtcur, getmntent_buf
1751 + (mtcur==mtpair ? sizeof(getmntent_buf)/2 : 0),
1752 sizeof(getmntent_buf)/2))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001753 {
1754 // Were we looking for something specific?
1755
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001756 if (argc) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001757
1758 // If we didn't find anything, complain.
1759
1760 if (!mtnext->mnt_fsname)
1761 bb_error_msg_and_die("can't find %s in %s",
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001762 argv[0], fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001763
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001764 mtcur = mtnext;
1765 if (nonroot) {
1766 // fstab must have "users" or "user"
1767 if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS))
1768 bb_error_msg_and_die(must_be_root);
1769 }
1770
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001771 // Mount the last thing we found.
1772
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001773 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001774 append_mount_options(&(mtcur->mnt_opts), cmdopts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001775 rc = singlemount(mtcur, 0);
1776 free(mtcur->mnt_opts);
1777 }
1778 goto clean_up;
1779 }
1780
1781 /* If we're trying to mount something specific and this isn't it,
1782 * skip it. Note we must match both the exact text in fstab (ala
1783 * "proc") or a full path from root */
1784
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001785 if (argc) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001786
1787 // Is this what we're looking for?
1788
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001789 if (strcmp(argv[0], mtcur->mnt_fsname) &&
1790 strcmp(storage_path, mtcur->mnt_fsname) &&
1791 strcmp(argv[0], mtcur->mnt_dir) &&
1792 strcmp(storage_path, mtcur->mnt_dir)) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001793
1794 // Remember this entry. Something later may have overmounted
1795 // it, and we want the _last_ match.
1796
1797 mtcur = mtnext;
1798
1799 // If we're mounting all.
1800
1801 } else {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001802 // Do we need to match a filesystem type?
Denis Vlasenkobf295dd2007-04-05 21:57:47 +00001803 if (fstype && match_fstype(mtcur, fstype)) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001804
1805 // Skip noauto and swap anyway.
1806
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001807 if (parse_mount_options(mtcur->mnt_opts, 0)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001808 & (MOUNT_NOAUTO | MOUNT_SWAP)) continue;
1809
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001810 // No, mount -a won't mount anything,
1811 // even user mounts, for mere humans.
1812
1813 if (nonroot)
1814 bb_error_msg_and_die(must_be_root);
1815
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001816 // Mount this thing.
1817
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001818 // NFS mounts want this to be xrealloc-able
1819 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001820 if (singlemount(mtcur, 1)) {
1821 /* Count number of failed mounts */
1822 rc++;
1823 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001824 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001825 }
1826 }
1827 if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab);
1828
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001829 clean_up:
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001830
1831 if (ENABLE_FEATURE_CLEAN_UP) {
1832 free(storage_path);
1833 free(cmdopts);
1834 }
1835
1836 return rc;
1837}