1 /* vi: set sw=4 ts=4: */
2 /*
3 * Mini mount implementation for busybox
4 *
5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net>
8 *
9 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10 */
11 // Design notes: There is no spec for mount. Remind me to write one.
12 //
13 // mount_main() calls singlemount() which calls mount_it_now().
14 //
15 // mount_main() can loop through /etc/fstab for mount -a
16 // singlemount() can loop through /etc/filesystems for fstype detection.
17 // mount_it_now() does the actual mount.
18 //
19
20 //config:config MOUNT
21 //config: bool "mount (23 kb)"
22 //config: default y
23 //config: help
24 //config: All files and filesystems in Unix are arranged into one big directory
25 //config: tree. The 'mount' utility is used to graft a filesystem onto a
26 //config: particular part of the tree. A filesystem can either live on a block
27 //config: device, or it can be accessible over the network, as is the case with
28 //config: NFS filesystems.
29 //config:
30 //config:config FEATURE_MOUNT_FAKE
31 //config: bool "Support -f (fake mount)"
32 //config: default y
33 //config: depends on MOUNT
34 //config: help
35 //config: Enable support for faking a file system mount.
36 //config:
37 //config:config FEATURE_MOUNT_VERBOSE
38 //config: bool "Support -v (verbose)"
39 //config: default y
40 //config: depends on MOUNT
41 //config: help
42 //config: Enable multi-level -v[vv...] verbose messages. Useful if you
43 //config: debug mount problems and want to see what is exactly passed
44 //config: to the kernel.
45 //config:
46 //config:config FEATURE_MOUNT_HELPERS
47 //config: bool "Support mount helpers"
48 //config: default n
49 //config: depends on MOUNT
50 //config: help
51 //config: Enable mounting of virtual file systems via external helpers.
52 //config: E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call
53 //config: "obexfs -b00.11.22.33.44.55 /mnt"
54 //config: Also "mount -t sometype [-o opts] fs /mnt" will try
55 //config: "sometype [-o opts] fs /mnt" if simple mount syscall fails.
56 //config: The idea is to use such virtual filesystems in /etc/fstab.
57 //config:
58 //config:config FEATURE_MOUNT_LABEL
59 //config: bool "Support specifying devices by label or UUID"
60 //config: default y
61 //config: depends on MOUNT
62 //config: select VOLUMEID
63 //config: help
64 //config: This allows for specifying a device by label or uuid, rather than by
65 //config: name. This feature utilizes the same functionality as blkid/findfs.
66 //config:
67 //config:config FEATURE_MOUNT_NFS
68 //config: bool "Support mounting NFS file systems on Linux < 2.6.23"
69 //config: default n
70 //config: depends on MOUNT
71 //config: select FEATURE_SYSLOG
72 //config: help
73 //config: Enable mounting of NFS file systems on Linux kernels prior
74 //config: to version 2.6.23. Note that in this case mounting of NFS
75 //config: over IPv6 will not be possible.
76 //config:
77 //config: Note that this option links in RPC support from libc,
78 //config: which is rather large (~10 kbytes on uclibc).
79 //config:
80 //config:config FEATURE_MOUNT_CIFS
81 //config: bool "Support mounting CIFS/SMB file systems"
82 //config: default y
83 //config: depends on MOUNT
84 //config: help
85 //config: Enable support for samba mounts.
86 //config:
87 //config:config FEATURE_MOUNT_FLAGS
88 //config: depends on MOUNT
89 //config: bool "Support lots of -o flags"
90 //config: default y
91 //config: help
92 //config: Without this, mount only supports ro/rw/remount. With this, it
93 //config: supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime,
94 //config: noatime, diratime, nodiratime, loud, bind, move, shared, slave,
95 //config: private, unbindable, rshared, rslave, rprivate, and runbindable.
96 //config:
97 //config:config FEATURE_MOUNT_FSTAB
98 //config: depends on MOUNT
99 //config: bool "Support /etc/fstab and -a (mount all)"
100 //config: default y
101 //config: help
102 //config: Support mount all and looking for files in /etc/fstab.
103 //config:
104 //config:config FEATURE_MOUNT_OTHERTAB
105 //config: depends on FEATURE_MOUNT_FSTAB
106 //config: bool "Support -T <alt_fstab>"
107 //config: default y
108 //config: help
109 //config: Support mount -T (specifying an alternate fstab)
110
111 /* On full-blown systems, requires suid for user mounts.
112 * But it's not unthinkable to have it available in non-suid flavor on some systems,
113 * for viewing mount table.
114 * Therefore we use BB_SUID_MAYBE instead of BB_SUID_REQUIRE: */
115 //applet:IF_MOUNT(APPLET(mount, BB_DIR_BIN, IF_DESKTOP(BB_SUID_MAYBE) IF_NOT_DESKTOP(BB_SUID_DROP)))
116
117 //kbuild:lib-$(CONFIG_MOUNT) += mount.o
118
119 //usage:#define mount_trivial_usage
120 //usage: "[OPTIONS] [-o OPT] DEVICE NODE"
121 //usage:#define mount_full_usage "\n\n"
122 //usage: "Mount a filesystem. Filesystem autodetection requires /proc.\n"
123 //usage: "\n -a Mount all filesystems in fstab"
124 //usage: IF_FEATURE_MOUNT_FAKE(
125 //usage: IF_FEATURE_MTAB_SUPPORT(
126 //usage: "\n -f Update /etc/mtab, but don't mount"
127 //usage: )
128 //usage: IF_NOT_FEATURE_MTAB_SUPPORT(
129 //usage: "\n -f Dry run"
130 //usage: )
131 //usage: )
132 //usage: IF_FEATURE_MOUNT_HELPERS(
133 //usage: "\n -i Don't run mount helper"
134 //usage: )
135 //usage: IF_FEATURE_MTAB_SUPPORT(
136 //usage: "\n -n Don't update /etc/mtab"
137 //usage: )
138 //usage: IF_FEATURE_MOUNT_VERBOSE(
139 //usage: "\n -v Verbose"
140 //usage: )
141 ////usage: "\n -s Sloppy (ignored)"
142 //usage: "\n -r Read-only mount"
143 ////usage: "\n -w Read-write mount (default)"
144 //usage: "\n -t FSTYPE[,...] Filesystem type(s)"
145 //usage: IF_FEATURE_MOUNT_OTHERTAB(
146 //usage: "\n -T FILE Read FILE instead of /etc/fstab"
147 //usage: )
148 //usage: "\n -O OPT Mount only filesystems with option OPT (-a only)"
149 //usage: "\n-o OPT:"
150 //usage: IF_FEATURE_MOUNT_LOOP(
151 //usage: "\n loop Ignored (loop devices are autodetected)"
152 //usage: )
153 //usage: IF_FEATURE_MOUNT_FLAGS(
154 //usage: "\n [a]sync Writes are [a]synchronous"
155 //usage: "\n [no]atime Disable/enable updates to inode access times"
156 //usage: "\n [no]diratime Disable/enable atime updates to directories"
157 //usage: "\n [no]relatime Disable/enable atime updates relative to modification time"
158 //usage: "\n [no]dev (Dis)allow use of special device files"
159 //usage: "\n [no]exec (Dis)allow use of executable files"
160 //usage: "\n [no]suid (Dis)allow set-user-id-root programs"
161 //usage: "\n [r]shared Convert [recursively] to a shared subtree"
162 //usage: "\n [r]slave Convert [recursively] to a slave subtree"
163 //usage: "\n [r]private Convert [recursively] to a private subtree"
164 //usage: "\n [un]bindable Make mount point [un]able to be bind mounted"
165 //usage: "\n [r]bind Bind a file or directory [recursively] to another location"
166 //usage: "\n move Relocate an existing mount point"
167 //usage: )
168 //usage: "\n remount Remount a mounted filesystem, changing flags"
169 //usage: "\n ro Same as -r"
170 //usage: "\n"
171 //usage: "\nThere are filesystem-specific -o flags."
172 //usage:
173 //usage:#define mount_example_usage
174 //usage: "$ mount\n"
175 //usage: "/dev/hda3 on / type minix (rw)\n"
176 //usage: "proc on /proc type proc (rw)\n"
177 //usage: "devpts on /dev/pts type devpts (rw)\n"
178 //usage: "$ mount /dev/fd0 /mnt -t msdos -o ro\n"
179 //usage: "$ mount /tmp/diskimage /opt -t ext2 -o loop\n"
180 //usage: "$ mount cd_image.iso mydir\n"
181 //usage:#define mount_notes_usage
182 //usage: "Returns 0 for success, number of failed mounts for -a, or errno for one mount."
183
184 #include <mntent.h>
185 #if ENABLE_FEATURE_SYSLOG
186 #include <syslog.h>
187 #endif
188 #include <sys/mount.h>
189 // Grab more as needed from util-linux's mount/mount_constants.h
190 #ifndef MS_DIRSYNC
191 # define MS_DIRSYNC (1 << 7) // Directory modifications are synchronous
192 #endif
193 #ifndef MS_NOSYMFOLLOW
194 # define MS_NOSYMFOLLOW (1 << 8)
195 #endif
196 #ifndef MS_BIND
197 # define MS_BIND (1 << 12)
198 #endif
199 #ifndef MS_MOVE
200 # define MS_MOVE (1 << 13)
201 #endif
202 #ifndef MS_RECURSIVE
203 # define MS_RECURSIVE (1 << 14)
204 #endif
205 #ifndef MS_SILENT
206 # define MS_SILENT (1 << 15)
207 #endif
208 // The shared subtree stuff, which went in around 2.6.15
209 #ifndef MS_UNBINDABLE
210 # define MS_UNBINDABLE (1 << 17)
211 #endif
212 #ifndef MS_PRIVATE
213 # define MS_PRIVATE (1 << 18)
214 #endif
215 #ifndef MS_SLAVE
216 # define MS_SLAVE (1 << 19)
217 #endif
218 #ifndef MS_SHARED
219 # define MS_SHARED (1 << 20)
220 #endif
221
222 #ifndef MS_RELATIME
223 # define MS_RELATIME (1 << 21)
224 #endif
225 #ifndef MS_STRICTATIME
226 # define MS_STRICTATIME (1 << 24)
227 #endif
228 #ifndef MS_LAZYTIME
229 # define MS_LAZYTIME (1 << 25)
230 #endif
231
232 /* Any ~MS_FOO value has this bit set: */
233 #define BB_MS_INVERTED_VALUE (1u << 31)
234
235 #include "libbb.h"
236 #include "common_bufsiz.h"
237 #if ENABLE_FEATURE_MOUNT_LABEL
238 # include "volume_id.h"
239 #else
240 # define resolve_mount_spec(fsname) ((void)0)
241 #endif
242
243 // Needed for nfs support only
244 #include <sys/utsname.h>
245 #undef TRUE
246 #undef FALSE
247 #if ENABLE_FEATURE_MOUNT_NFS
248 /* This is just a warning of a common mistake. Possibly this should be a
249 * uclibc faq entry rather than in busybox... */
250 # if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
251 # warning "You probably need to build uClibc with UCLIBC_HAS_RPC for NFS support"
252 /* not #error, since user may be using e.g. libtirpc instead.
253 * This might work:
254 * CONFIG_EXTRA_CFLAGS="-I/usr/include/tirpc"
255 * CONFIG_EXTRA_LDLIBS="tirpc"
256 */
257 # endif
258 # include <rpc/rpc.h>
259 # include <rpc/pmap_prot.h>
260 # include <rpc/pmap_clnt.h>
261 #endif
262
263
264 #if defined(__dietlibc__)
265 // 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
266 // dietlibc-0.30 does not have implementation of getmntent_r()
getmntent_r(FILE * stream,struct mntent * result,char * buffer UNUSED_PARAM,int bufsize UNUSED_PARAM)267 static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
268 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
269 {
270 struct mntent* ment = getmntent(stream);
271 return memcpy(result, ment, sizeof(*ment));
272 }
273 #endif
274
275
276 // Not real flags, but we want to be able to check for this.
277 enum {
278 MOUNT_USERS = (1 << 27) * ENABLE_DESKTOP,
279 MOUNT_NOFAIL = (1 << 28) * ENABLE_DESKTOP,
280 MOUNT_NOAUTO = (1 << 29),
281 MOUNT_SWAP = (1 << 30),
282 MOUNT_FAKEFLAGS = MOUNT_USERS | MOUNT_NOFAIL | MOUNT_NOAUTO | MOUNT_SWAP
283 };
284
285
286 #define OPTION_STR "o:*t:rwanfvsiO:" IF_FEATURE_MOUNT_OTHERTAB("T:")
287 enum {
288 OPT_o = (1 << 0),
289 OPT_t = (1 << 1),
290 OPT_r = (1 << 2),
291 OPT_w = (1 << 3),
292 OPT_a = (1 << 4),
293 OPT_n = (1 << 5),
294 OPT_f = (1 << 6),
295 OPT_v = (1 << 7),
296 OPT_s = (1 << 8),
297 OPT_i = (1 << 9),
298 OPT_O = (1 << 10),
299 OPT_T = (1 << 11),
300 };
301
302 #if ENABLE_FEATURE_MTAB_SUPPORT
303 #define USE_MTAB (!(option_mask32 & OPT_n))
304 #else
305 #define USE_MTAB 0
306 #endif
307
308 #if ENABLE_FEATURE_MOUNT_FAKE
309 #define FAKE_IT (option_mask32 & OPT_f)
310 #else
311 #define FAKE_IT 0
312 #endif
313
314 #if ENABLE_FEATURE_MOUNT_HELPERS
315 #define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
316 #else
317 #define HELPERS_ALLOWED 0
318 #endif
319
320
321 // TODO: more "user" flag compatibility.
322 // "user" option (from mount manpage):
323 // Only the user that mounted a filesystem can unmount it again.
324 // If any user should be able to unmount, then use users instead of user
325 // in the fstab line. The owner option is similar to the user option,
326 // with the restriction that the user must be the owner of the special file.
327 // This may be useful e.g. for /dev/fd if a login script makes
328 // the console user owner of this device.
329
330 // Standard mount options (from -o options or --options),
331 // with corresponding flags
332 static const int32_t mount_options[] ALIGN4 = {
333 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
334
335 IF_FEATURE_MOUNT_LOOP(
336 /* "loop" */ 0,
337 )
338
339 IF_FEATURE_MOUNT_FSTAB(
340 /* "defaults" */ 0,
341 /* "quiet" 0 - do not filter out, vfat wants to see it */
342 /* "noauto" */ MOUNT_NOAUTO,
343 /* "sw" */ MOUNT_SWAP,
344 /* "swap" */ MOUNT_SWAP,
345 IF_DESKTOP(/* "user" */ MOUNT_USERS,)
346 IF_DESKTOP(/* "users" */ MOUNT_USERS,)
347 IF_DESKTOP(/* "nofail" */ MOUNT_NOFAIL,)
348 /* "_netdev" */ 0,
349 IF_DESKTOP(/* "comment=" */ 0,) /* systemd uses this in fstab */
350 )
351
352 IF_FEATURE_MOUNT_FLAGS(
353 // vfs flags
354 /* "nosuid" */ MS_NOSUID,
355 /* "suid" */ ~MS_NOSUID,
356 /* "dev" */ ~MS_NODEV,
357 /* "nodev" */ MS_NODEV,
358 /* "exec" */ ~MS_NOEXEC,
359 /* "noexec" */ MS_NOEXEC,
360 /* "sync" */ MS_SYNCHRONOUS,
361 /* "dirsync" */ MS_DIRSYNC,
362 /* "async" */ ~MS_SYNCHRONOUS,
363 /* "atime" */ ~MS_NOATIME,
364 /* "noatime" */ MS_NOATIME,
365 /* "diratime" */ ~MS_NODIRATIME,
366 /* "nodiratime" */ MS_NODIRATIME,
367 /* "relatime" */ MS_RELATIME,
368 /* "norelatime" */ ~MS_RELATIME,
369 /* "strictatime" */ MS_STRICTATIME,
370 /* "nostrictatime"*/ ~MS_STRICTATIME,
371 /* "lazytime" */ MS_LAZYTIME,
372 /* "nolazytime" */ ~MS_LAZYTIME,
373 /* "nosymfollow" */ MS_NOSYMFOLLOW,
374 /* "mand" */ MS_MANDLOCK,
375 /* "nomand" */ ~MS_MANDLOCK,
376 /* "loud" */ ~MS_SILENT,
377
378 // action flags
379 /* "rbind" */ MS_BIND|MS_RECURSIVE,
380 /* "bind" */ MS_BIND,
381 /* "move" */ MS_MOVE,
382 /* "shared" */ MS_SHARED,
383 /* "slave" */ MS_SLAVE,
384 /* "private" */ MS_PRIVATE,
385 /* "unbindable" */ MS_UNBINDABLE,
386 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
387 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
388 /* "rprivate" */ MS_PRIVATE|MS_RECURSIVE,
389 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
390 )
391
392 // Always understood.
393 /* "ro" */ MS_RDONLY, // vfs flag
394 /* "rw" */ ~MS_RDONLY, // vfs flag
395 /* "remount" */ MS_REMOUNT // action flag
396 };
397
398 static const char mount_option_str[] ALIGN1 =
399 IF_FEATURE_MOUNT_LOOP(
400 "loop\0"
401 )
402 IF_FEATURE_MOUNT_FSTAB(
403 "defaults\0"
404 // "quiet\0" - do not filter out, vfat wants to see it
405 "noauto\0"
406 "sw\0"
407 "swap\0"
408 IF_DESKTOP("user\0")
409 IF_DESKTOP("users\0")
410 IF_DESKTOP("nofail\0")
411 "_netdev\0"
412 IF_DESKTOP("comment=\0") /* systemd uses this in fstab */
413 )
414 IF_FEATURE_MOUNT_FLAGS(
415 // vfs flags
416 "nosuid" "\0"
417 "suid" "\0"
418 "dev" "\0"
419 "nodev" "\0"
420 "exec" "\0"
421 "noexec" "\0"
422 "sync" "\0"
423 "dirsync" "\0"
424 "async" "\0"
425 "atime" "\0"
426 "noatime" "\0"
427 "diratime" "\0"
428 "nodiratime" "\0"
429 "relatime" "\0"
430 "norelatime" "\0"
431 "strictatime" "\0"
432 "nostrictatime""\0"
433 "lazytime" "\0"
434 "nolazytime" "\0"
435 "nosymfollow" "\0"
436 "mand" "\0"
437 "nomand" "\0"
438 "loud" "\0"
439
440 // action flags
441 "rbind\0"
442 "bind\0"
443 "move\0"
444 "make-shared\0"
445 "make-slave\0"
446 "make-private\0"
447 "make-unbindable\0"
448 "make-rshared\0"
449 "make-rslave\0"
450 "make-rprivate\0"
451 "make-runbindable\0"
452 )
453
454 // Always understood.
455 "ro\0" // vfs flag
456 "rw\0" // vfs flag
457 "remount\0" // action flag
458 ;
459
460
461 struct globals {
462 #if ENABLE_FEATURE_MOUNT_NFS
463 smalluint nfs_mount_version;
464 #endif
465 #if ENABLE_FEATURE_MOUNT_VERBOSE
466 unsigned verbose;
467 #endif
468 llist_t *fslist;
469 char getmntent_buf[1];
470 } FIX_ALIASING;
471 enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
472 #define G (*(struct globals*)bb_common_bufsiz1)
473 #define nfs_mount_version (G.nfs_mount_version)
474 #if ENABLE_FEATURE_MOUNT_VERBOSE
475 #define verbose (G.verbose )
476 #else
477 #define verbose 0
478 #endif
479 #define fslist (G.fslist )
480 #define getmntent_buf (G.getmntent_buf )
481 #define INIT_G() do { setup_common_bufsiz(); } while (0)
482
483 #if ENABLE_FEATURE_MTAB_SUPPORT
484 /*
485 * update_mtab_entry_on_move() is used to update entry in case of mount --move.
486 * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
487 * input mntent and replace it by new one.
488 */
update_mtab_entry_on_move(const struct mntent * mp)489 static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
490 {
491 struct mntent *entries, *m;
492 int i, count;
493 FILE *mountTable;
494
495 mountTable = setmntent(bb_path_mtab_file, "r");
496 if (!mountTable) {
497 bb_simple_perror_msg(bb_path_mtab_file);
498 return;
499 }
500
501 entries = NULL;
502 count = 0;
503 while ((m = getmntent(mountTable)) != NULL) {
504 entries = xrealloc_vector(entries, 3, count);
505 entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
506 entries[count].mnt_dir = xstrdup(m->mnt_dir);
507 entries[count].mnt_type = xstrdup(m->mnt_type);
508 entries[count].mnt_opts = xstrdup(m->mnt_opts);
509 entries[count].mnt_freq = m->mnt_freq;
510 entries[count].mnt_passno = m->mnt_passno;
511 count++;
512 }
513 endmntent(mountTable);
514
515 mountTable = setmntent(bb_path_mtab_file, "w");
516 if (mountTable) {
517 for (i = 0; i < count; i++) {
518 if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
519 addmntent(mountTable, &entries[i]);
520 else
521 addmntent(mountTable, mp);
522 }
523 endmntent(mountTable);
524 } else if (errno != EROFS)
525 bb_simple_perror_msg(bb_path_mtab_file);
526
527 if (ENABLE_FEATURE_CLEAN_UP) {
528 for (i = 0; i < count; i++) {
529 free(entries[i].mnt_fsname);
530 free(entries[i].mnt_dir);
531 free(entries[i].mnt_type);
532 free(entries[i].mnt_opts);
533 }
534 free(entries);
535 }
536 }
537 #endif
538
539 #if ENABLE_FEATURE_MOUNT_VERBOSE
verbose_mount(const char * source,const char * target,const char * filesystemtype,unsigned long mountflags,const void * data)540 static int verbose_mount(const char *source, const char *target,
541 const char *filesystemtype,
542 unsigned long mountflags, const void *data)
543 {
544 int rc;
545
546 errno = 0;
547 rc = mount(source, target, filesystemtype, mountflags, data);
548 if (verbose >= 2)
549 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
550 source, target, filesystemtype,
551 mountflags, (char*)data, rc);
552 return rc;
553 }
554 #else
555 #define verbose_mount(...) mount(__VA_ARGS__)
556 #endif
557
558 // Append mount options to string
append_mount_options(char ** oldopts,const char * newopts)559 static void append_mount_options(char **oldopts, const char *newopts)
560 {
561 if (*oldopts && **oldopts) {
562 // Do not insert options which are already there
563 while (newopts[0]) {
564 char *p;
565 int len;
566
567 len = strchrnul(newopts, ',') - newopts;
568 p = *oldopts;
569 while (1) {
570 if (!strncmp(p, newopts, len)
571 && (p[len] == ',' || p[len] == '\0'))
572 goto skip;
573 p = strchr(p,',');
574 if (!p) break;
575 p++;
576 }
577 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
578 free(*oldopts);
579 *oldopts = p;
580 skip:
581 newopts += len;
582 while (*newopts == ',') newopts++;
583 }
584 } else {
585 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
586 *oldopts = xstrdup(newopts);
587 }
588 }
589
590 // Use the mount_options list to parse options into flags.
591 // Also update list of unrecognized options if unrecognized != NULL
parse_mount_options(char * options,char ** unrecognized,uint32_t * opt)592 static unsigned long parse_mount_options(char *options, char **unrecognized, uint32_t *opt)
593 {
594 unsigned long flags = MS_SILENT;
595
596 // Loop through options
597 for (;;) {
598 unsigned i;
599 char *comma = strchr(options, ',');
600 const char *option_str = mount_option_str;
601
602 if (comma) *comma = '\0';
603
604 // FIXME: use hasmntopt()
605 // Find this option in mount_options
606 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
607 unsigned opt_len = strlen(option_str);
608
609 if (strncasecmp(option_str, options, opt_len) == 0
610 && (options[opt_len] == '\0'
611 /* or is it "comment=" thingy in fstab? */
612 IF_FEATURE_MOUNT_FSTAB(IF_DESKTOP( || option_str[opt_len-1] == '=' ))
613 )
614 ) {
615 unsigned long fl = mount_options[i];
616 if (fl & BB_MS_INVERTED_VALUE)
617 flags &= fl;
618 else
619 flags |= fl;
620 /* If we see "-o rw" on command line, it's the same as -w:
621 * "do not try to fall back to RO mounts"
622 */
623 if (fl == ~MS_RDONLY && opt)
624 (*opt) |= OPT_w;
625 goto found;
626 }
627 option_str += opt_len + 1;
628 }
629 // We did not recognize this option.
630 // If "unrecognized" is not NULL, append option there.
631 // Note that we should not append *empty* option -
632 // in this case we want to pass NULL, not "", to "data"
633 // parameter of mount(2) syscall.
634 // This is crucial for filesystems that don't accept
635 // any arbitrary mount options, like cgroup fs:
636 // "mount -t cgroup none /mnt"
637 if (options[0] && unrecognized) {
638 // Add it to strflags, to pass on to kernel
639 char *p = *unrecognized;
640 unsigned len = p ? strlen(p) : 0;
641 *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
642
643 // Comma separated if it's not the first one
644 if (len) p[len++] = ',';
645 strcpy(p + len, options);
646 }
647 found:
648 if (!comma)
649 break;
650 // Advance to next option
651 *comma = ',';
652 options = ++comma;
653 }
654
655 return flags;
656 }
657
658 // Return a list of all block device backed filesystems
get_block_backed_filesystems(void)659 static llist_t *get_block_backed_filesystems(void)
660 {
661 static const char filesystems[2][sizeof("/proc/filesystems")] ALIGN1 = {
662 "/etc/filesystems",
663 "/proc/filesystems",
664 };
665 char *fs, *buf;
666 llist_t *list = NULL;
667 int i;
668 FILE *f;
669
670 for (i = 0; i < 2; i++) {
671 f = fopen_for_read(filesystems[i]);
672 if (!f) continue;
673
674 while ((buf = xmalloc_fgetline(f)) != NULL) {
675 if (is_prefixed_with(buf, "nodev") && isspace(buf[5]))
676 goto next;
677 fs = skip_whitespace(buf);
678 if (*fs == '#' || *fs == '*' || !*fs)
679 goto next;
680
681 llist_add_to_end(&list, xstrdup(fs));
682 next:
683 free(buf);
684 }
685 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
686 }
687
688 return list;
689 }
690
691 #if ENABLE_FEATURE_CLEAN_UP
delete_block_backed_filesystems(void)692 static void delete_block_backed_filesystems(void)
693 {
694 llist_free(fslist, free);
695 }
696 #else
697 void delete_block_backed_filesystems(void);
698 #endif
699
700 // Perform actual mount of specific filesystem at specific location.
701 // NB: mp->xxx fields may be trashed on exit
mount_it_now(struct mntent * mp,unsigned long vfsflags,char * filteropts)702 static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts)
703 {
704 int rc = 0;
705
706 vfsflags &= ~(unsigned long)MOUNT_FAKEFLAGS;
707
708 if (FAKE_IT) {
709 if (verbose >= 2)
710 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
711 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
712 vfsflags, filteropts);
713 goto mtab;
714 }
715
716 // Mount, with fallback to read-only if necessary.
717 for (;;) {
718 errno = 0;
719 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
720 vfsflags, filteropts);
721 if (rc == 0)
722 goto mtab; // success
723
724 // mount failed, try helper program
725 // mount.<mnt_type>
726 if (HELPERS_ALLOWED && mp->mnt_type) {
727 char *args[8];
728 int errno_save = errno;
729 args[0] = xasprintf("mount.%s", mp->mnt_type);
730 rc = 1;
731 if (FAKE_IT)
732 args[rc++] = (char *)"-f";
733 if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
734 args[rc++] = (char *)"-n";
735 args[rc++] = mp->mnt_fsname;
736 args[rc++] = mp->mnt_dir;
737 if (filteropts) {
738 args[rc++] = (char *)"-o";
739 args[rc++] = filteropts;
740 }
741 args[rc] = NULL;
742 rc = spawn_and_wait(args);
743 free(args[0]);
744 if (rc == 0)
745 goto mtab; // success
746 errno = errno_save;
747 }
748
749 // Should we retry read-only mount?
750 if (vfsflags & MS_RDONLY)
751 break; // no, already was tried
752 if (option_mask32 & OPT_w)
753 break; // no, "mount -w" never falls back to RO
754 if (errno != EACCES && errno != EROFS)
755 break; // no, error isn't hinting that RO may work
756
757 if (!(vfsflags & MS_SILENT))
758 bb_error_msg("%s is write-protected, mounting read-only",
759 mp->mnt_fsname);
760 vfsflags |= MS_RDONLY;
761 }
762
763 // Abort entirely if permission denied.
764
765 if (rc && errno == EPERM)
766 bb_simple_error_msg_and_die(bb_msg_perm_denied_are_you_root);
767
768 // If the mount was successful, and we're maintaining an old-style
769 // mtab file by hand, add the new entry to it now.
770 mtab:
771 if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
772 char *fsname;
773 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
774 const char *option_str = mount_option_str;
775 int i;
776
777 if (!mountTable) {
778 bb_simple_perror_msg(bb_path_mtab_file);
779 goto ret;
780 }
781
782 // Add vfs string flags
783 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
784 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
785 append_mount_options(&(mp->mnt_opts), option_str);
786 option_str += strlen(option_str) + 1;
787 }
788
789 // Remove trailing / (if any) from directory we mounted on
790 i = strlen(mp->mnt_dir) - 1;
791 while (i > 0 && mp->mnt_dir[i] == '/')
792 mp->mnt_dir[i--] = '\0';
793
794 // Convert to canonical pathnames as needed
795 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
796 fsname = NULL;
797 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
798 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
799 mp->mnt_type = (char*)"bind";
800 }
801 mp->mnt_freq = mp->mnt_passno = 0;
802
803 // Write and close
804 #if ENABLE_FEATURE_MTAB_SUPPORT
805 if (vfsflags & MS_MOVE)
806 update_mtab_entry_on_move(mp);
807 else
808 #endif
809 addmntent(mountTable, mp);
810 endmntent(mountTable);
811
812 if (ENABLE_FEATURE_CLEAN_UP) {
813 free(mp->mnt_dir);
814 free(fsname);
815 }
816 }
817 ret:
818 return rc;
819 }
820
821 #if ENABLE_FEATURE_MOUNT_NFS
822
823 /*
824 * Linux NFS mount
825 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
826 *
827 * Licensed under GPLv2, see file LICENSE in this source tree.
828 *
829 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
830 * numbers to be specified on the command line.
831 *
832 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
833 * Omit the call to connect() for Linux version 1.3.11 or later.
834 *
835 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
836 * Implemented the "bg", "fg" and "retry" mount options for NFS.
837 *
838 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
839 * - added Native Language Support
840 *
841 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
842 * plus NFSv3 stuff.
843 */
844
845 #define MOUNTPORT 635
846 #define MNTPATHLEN 1024
847 #define MNTNAMLEN 255
848 #define FHSIZE 32
849 #define FHSIZE3 64
850
851 typedef char fhandle[FHSIZE];
852
853 typedef struct {
854 unsigned int fhandle3_len;
855 char *fhandle3_val;
856 } fhandle3;
857
858 enum mountstat3 {
859 MNT_OK = 0,
860 MNT3ERR_PERM = 1,
861 MNT3ERR_NOENT = 2,
862 MNT3ERR_IO = 5,
863 MNT3ERR_ACCES = 13,
864 MNT3ERR_NOTDIR = 20,
865 MNT3ERR_INVAL = 22,
866 MNT3ERR_NAMETOOLONG = 63,
867 MNT3ERR_NOTSUPP = 10004,
868 MNT3ERR_SERVERFAULT = 10006,
869 };
870 typedef enum mountstat3 mountstat3;
871
872 struct fhstatus {
873 unsigned int fhs_status;
874 union {
875 fhandle fhs_fhandle;
876 } fhstatus_u;
877 };
878 typedef struct fhstatus fhstatus;
879
880 struct mountres3_ok {
881 fhandle3 fhandle;
882 struct {
883 unsigned int auth_flavours_len;
884 char *auth_flavours_val;
885 } auth_flavours;
886 };
887 typedef struct mountres3_ok mountres3_ok;
888
889 struct mountres3 {
890 mountstat3 fhs_status;
891 union {
892 mountres3_ok mountinfo;
893 } mountres3_u;
894 };
895 typedef struct mountres3 mountres3;
896
897 typedef char *dirpath;
898
899 typedef char *name;
900
901 typedef struct mountbody *mountlist;
902
903 struct mountbody {
904 name ml_hostname;
905 dirpath ml_directory;
906 mountlist ml_next;
907 };
908 typedef struct mountbody mountbody;
909
910 typedef struct groupnode *groups;
911
912 struct groupnode {
913 name gr_name;
914 groups gr_next;
915 };
916 typedef struct groupnode groupnode;
917
918 typedef struct exportnode *exports;
919
920 struct exportnode {
921 dirpath ex_dir;
922 groups ex_groups;
923 exports ex_next;
924 };
925 typedef struct exportnode exportnode;
926
927 struct ppathcnf {
928 int pc_link_max;
929 short pc_max_canon;
930 short pc_max_input;
931 short pc_name_max;
932 short pc_path_max;
933 short pc_pipe_buf;
934 uint8_t pc_vdisable;
935 char pc_xxx;
936 short pc_mask[2];
937 };
938 typedef struct ppathcnf ppathcnf;
939
940 #define MOUNTPROG 100005
941 #define MOUNTVERS 1
942
943 #define MOUNTPROC_NULL 0
944 #define MOUNTPROC_MNT 1
945 #define MOUNTPROC_DUMP 2
946 #define MOUNTPROC_UMNT 3
947 #define MOUNTPROC_UMNTALL 4
948 #define MOUNTPROC_EXPORT 5
949 #define MOUNTPROC_EXPORTALL 6
950
951 #define MOUNTVERS_POSIX 2
952
953 #define MOUNTPROC_PATHCONF 7
954
955 #define MOUNT_V3 3
956
957 #define MOUNTPROC3_NULL 0
958 #define MOUNTPROC3_MNT 1
959 #define MOUNTPROC3_DUMP 2
960 #define MOUNTPROC3_UMNT 3
961 #define MOUNTPROC3_UMNTALL 4
962 #define MOUNTPROC3_EXPORT 5
963
964 enum {
965 #ifndef NFS_FHSIZE
966 NFS_FHSIZE = 32,
967 #endif
968 #ifndef NFS_PORT
969 NFS_PORT = 2049
970 #endif
971 };
972
973 /*
974 * We want to be able to compile mount on old kernels in such a way
975 * that the binary will work well on more recent kernels.
976 * Thus, if necessary we teach nfsmount.c the structure of new fields
977 * that will come later.
978 *
979 * Moreover, the new kernel includes conflict with glibc includes
980 * so it is easiest to ignore the kernel altogether (at compile time).
981 */
982
983 struct nfs2_fh {
984 char data[32];
985 };
986 struct nfs3_fh {
987 unsigned short size;
988 unsigned char data[64];
989 };
990
991 struct nfs_mount_data {
992 int version; /* 1 */
993 int fd; /* 1 */
994 struct nfs2_fh old_root; /* 1 */
995 int flags; /* 1 */
996 int rsize; /* 1 */
997 int wsize; /* 1 */
998 int timeo; /* 1 */
999 int retrans; /* 1 */
1000 int acregmin; /* 1 */
1001 int acregmax; /* 1 */
1002 int acdirmin; /* 1 */
1003 int acdirmax; /* 1 */
1004 struct sockaddr_in addr; /* 1 */
1005 char hostname[256]; /* 1 */
1006 int namlen; /* 2 */
1007 unsigned int bsize; /* 3 */
1008 struct nfs3_fh root; /* 4 */
1009 };
1010
1011 /* bits in the flags field */
1012 enum {
1013 NFS_MOUNT_SOFT = 0x0001, /* 1 */
1014 NFS_MOUNT_INTR = 0x0002, /* 1 */
1015 NFS_MOUNT_SECURE = 0x0004, /* 1 */
1016 NFS_MOUNT_POSIX = 0x0008, /* 1 */
1017 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
1018 NFS_MOUNT_NOAC = 0x0020, /* 1 */
1019 NFS_MOUNT_TCP = 0x0040, /* 2 */
1020 NFS_MOUNT_VER3 = 0x0080, /* 3 */
1021 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
1022 NFS_MOUNT_NONLM = 0x0200, /* 3 */
1023 NFS_MOUNT_NOACL = 0x0800, /* 4 */
1024 NFS_MOUNT_NORDIRPLUS = 0x4000
1025 };
1026
1027
1028 /*
1029 * We need to translate between nfs status return values and
1030 * the local errno values which may not be the same.
1031 *
1032 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
1033 * "after #include <errno.h> the symbol errno is reserved for any use,
1034 * it cannot even be used as a struct tag or field name".
1035 */
1036 #ifndef EDQUOT
1037 # define EDQUOT ENOSPC
1038 #endif
1039 /* Convert each NFSERR_BLAH into EBLAH */
1040 static const uint8_t nfs_err_stat[] ALIGN1 = {
1041 1, 2, 5, 6, 13, 17,
1042 19, 20, 21, 22, 27, 28,
1043 30, 63, 66, 69, 70, 71
1044 };
1045 #if ( \
1046 EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \
1047 ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \
1048 EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
1049 typedef uint8_t nfs_err_type;
1050 #else
1051 typedef uint16_t nfs_err_type;
1052 #endif
1053 static const nfs_err_type nfs_err_errnum[] ALIGN2 = {
1054 EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST,
1055 ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC,
1056 EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
1057 };
nfs_strerror(int status)1058 static char *nfs_strerror(int status)
1059 {
1060 int i;
1061
1062 for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
1063 if (nfs_err_stat[i] == status)
1064 return strerror(nfs_err_errnum[i]);
1065 }
1066 return xasprintf("unknown nfs status return value: %d", status);
1067 }
1068
xdr_fhandle(XDR * xdrs,fhandle objp)1069 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
1070 {
1071 return xdr_opaque(xdrs, objp, FHSIZE);
1072 }
1073
xdr_fhstatus(XDR * xdrs,fhstatus * objp)1074 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
1075 {
1076 if (!xdr_u_int(xdrs, &objp->fhs_status))
1077 return FALSE;
1078 if (objp->fhs_status == 0)
1079 return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
1080 return TRUE;
1081 }
1082
xdr_dirpath(XDR * xdrs,dirpath * objp)1083 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
1084 {
1085 return xdr_string(xdrs, objp, MNTPATHLEN);
1086 }
1087
xdr_fhandle3(XDR * xdrs,fhandle3 * objp)1088 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
1089 {
1090 return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
1091 (unsigned int *) &objp->fhandle3_len,
1092 FHSIZE3);
1093 }
1094
xdr_mountres3_ok(XDR * xdrs,mountres3_ok * objp)1095 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
1096 {
1097 if (!xdr_fhandle3(xdrs, &objp->fhandle))
1098 return FALSE;
1099 return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
1100 &(objp->auth_flavours.auth_flavours_len),
1101 ~0,
1102 sizeof(int),
1103 (xdrproc_t) xdr_int);
1104 }
1105
xdr_mountstat3(XDR * xdrs,mountstat3 * objp)1106 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
1107 {
1108 return xdr_enum(xdrs, (enum_t *) objp);
1109 }
1110
xdr_mountres3(XDR * xdrs,mountres3 * objp)1111 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
1112 {
1113 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
1114 return FALSE;
1115 if (objp->fhs_status == MNT_OK)
1116 return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
1117 return TRUE;
1118 }
1119
1120 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
1121
1122 /*
1123 * Unfortunately, the kernel prints annoying console messages
1124 * in case of an unexpected nfs mount version (instead of
1125 * just returning some error). Therefore we'll have to try
1126 * and figure out what version the kernel expects.
1127 *
1128 * Variables:
1129 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
1130 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
1131 * nfs_mount_version: version this source and running kernel can handle
1132 */
1133 static void
find_kernel_nfs_mount_version(void)1134 find_kernel_nfs_mount_version(void)
1135 {
1136 int kernel_version;
1137
1138 if (nfs_mount_version)
1139 return;
1140
1141 nfs_mount_version = 4; /* default */
1142
1143 kernel_version = get_linux_version_code();
1144 if (kernel_version) {
1145 if (kernel_version < KERNEL_VERSION(2,2,18))
1146 nfs_mount_version = 3;
1147 /* else v4 since 2.3.99pre4 */
1148 }
1149 }
1150
1151 static void
get_mountport(struct pmap * pm_mnt,struct sockaddr_in * server_addr,long unsigned prog,long unsigned version,long unsigned proto,long unsigned port)1152 get_mountport(struct pmap *pm_mnt,
1153 struct sockaddr_in *server_addr,
1154 long unsigned prog,
1155 long unsigned version,
1156 long unsigned proto,
1157 long unsigned port)
1158 {
1159 struct pmaplist *pmap;
1160
1161 server_addr->sin_port = PMAPPORT;
1162 /* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
1163 * I understand it like "IPv6 for this is not 100% ready" */
1164 pmap = pmap_getmaps(server_addr);
1165
1166 if (version > MAX_NFSPROT)
1167 version = MAX_NFSPROT;
1168 if (!prog)
1169 prog = MOUNTPROG;
1170 pm_mnt->pm_prog = prog;
1171 pm_mnt->pm_vers = version;
1172 pm_mnt->pm_prot = proto;
1173 pm_mnt->pm_port = port;
1174
1175 while (pmap) {
1176 if (pmap->pml_map.pm_prog != prog)
1177 goto next;
1178 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
1179 goto next;
1180 if (version > 2 && pmap->pml_map.pm_vers != version)
1181 goto next;
1182 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1183 goto next;
1184 if (pmap->pml_map.pm_vers > MAX_NFSPROT
1185 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1186 || (port && pmap->pml_map.pm_port != port)
1187 ) {
1188 goto next;
1189 }
1190 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1191 next:
1192 pmap = pmap->pml_next;
1193 }
1194 if (!pm_mnt->pm_vers)
1195 pm_mnt->pm_vers = MOUNTVERS;
1196 if (!pm_mnt->pm_port)
1197 pm_mnt->pm_port = MOUNTPORT;
1198 if (!pm_mnt->pm_prot)
1199 pm_mnt->pm_prot = IPPROTO_TCP;
1200 }
1201
1202 #if BB_MMU
daemonize(void)1203 static int daemonize(void)
1204 {
1205 int pid = fork();
1206 if (pid < 0) /* error */
1207 return -errno;
1208 if (pid > 0) /* parent */
1209 return 0;
1210 /* child */
1211 close(0);
1212 xopen(bb_dev_null, O_RDWR);
1213 xdup2(0, 1);
1214 xdup2(0, 2);
1215 setsid();
1216 openlog(applet_name, LOG_PID, LOG_DAEMON);
1217 logmode = LOGMODE_SYSLOG;
1218 return 1;
1219 }
1220 #else
daemonize(void)1221 static inline int daemonize(void)
1222 {
1223 return -ENOSYS;
1224 }
1225 #endif
1226
1227 /* TODO */
we_saw_this_host_before(const char * hostname UNUSED_PARAM)1228 static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
1229 {
1230 return 0;
1231 }
1232
1233 /* RPC strerror analogs are terminally idiotic:
1234 * *mandatory* prefix and \n at end.
1235 * This hopefully helps. Usage:
1236 * error_msg_rpc(clnt_*error*(" ")) */
error_msg_rpc(const char * msg)1237 static void error_msg_rpc(const char *msg)
1238 {
1239 int len;
1240 while (msg[0] == ' ' || msg[0] == ':') msg++;
1241 len = strlen(msg);
1242 while (len && msg[len-1] == '\n') len--;
1243 bb_error_msg("%.*s", len, msg);
1244 }
1245
1246 /* NB: mp->xxx fields may be trashed on exit */
nfsmount(struct mntent * mp,unsigned long vfsflags,char * filteropts)1247 static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1248 {
1249 CLIENT *mclient;
1250 char *hostname;
1251 char *pathname;
1252 char *mounthost;
1253 /* prior to 2.6.23, kernel took NFS options in a form of this struct
1254 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1255 * then data pointer is interpreted as a string. */
1256 struct nfs_mount_data data;
1257 char *opt;
1258 char *tokstate;
1259 struct hostent *hp;
1260 struct sockaddr_in server_addr;
1261 struct sockaddr_in mount_server_addr;
1262 int msock, fsock;
1263 union {
1264 struct fhstatus nfsv2;
1265 struct mountres3 nfsv3;
1266 } status;
1267 int daemonized;
1268 char *s;
1269 int port;
1270 int mountport;
1271 int proto;
1272 #if BB_MMU
1273 smallint bg = 0;
1274 #else
1275 enum { bg = 0 };
1276 #endif
1277 int retry;
1278 int mountprog;
1279 int mountvers;
1280 int nfsprog;
1281 int nfsvers;
1282 int retval;
1283 /* these all are one-bit really. gcc 4.3.1 likes this combination: */
1284 smallint tcp;
1285 smallint soft;
1286 int intr;
1287 int posix;
1288 int nocto;
1289 int noac;
1290 int nordirplus;
1291 int nolock;
1292 int noacl;
1293
1294 find_kernel_nfs_mount_version();
1295
1296 daemonized = 0;
1297 mounthost = NULL;
1298 retval = ETIMEDOUT;
1299 msock = fsock = -1;
1300 mclient = NULL;
1301
1302 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1303
1304 filteropts = xstrdup(filteropts); /* going to trash it later... */
1305
1306 hostname = xstrdup(mp->mnt_fsname);
1307 /* mount_main() guarantees that ':' is there */
1308 s = strchr(hostname, ':');
1309 pathname = s + 1;
1310 *s = '\0';
1311 /* Ignore all but first hostname in replicated mounts
1312 * until they can be fully supported. (mack@sgi.com) */
1313 s = strchr(hostname, ',');
1314 if (s) {
1315 *s = '\0';
1316 bb_simple_error_msg("warning: multiple hostnames not supported");
1317 }
1318
1319 server_addr.sin_family = AF_INET;
1320 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1321 hp = gethostbyname(hostname);
1322 if (hp == NULL) {
1323 bb_simple_herror_msg(hostname);
1324 goto fail;
1325 }
1326 if (hp->h_length != (int)sizeof(struct in_addr)) {
1327 bb_simple_error_msg_and_die("only IPv4 is supported");
1328 }
1329 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1330 }
1331
1332 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1333
1334 /* add IP address to mtab options for use when unmounting */
1335
1336 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1337 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1338 } else {
1339 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1340 mp->mnt_opts[0] ? "," : "",
1341 inet_ntoa(server_addr.sin_addr));
1342 free(mp->mnt_opts);
1343 mp->mnt_opts = tmp;
1344 }
1345
1346 /* Set default options.
1347 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1348 * let the kernel decide.
1349 * timeo is filled in after we know whether it'll be TCP or UDP. */
1350 memset(&data, 0, sizeof(data));
1351 data.retrans = 3;
1352 data.acregmin = 3;
1353 data.acregmax = 60;
1354 data.acdirmin = 30;
1355 data.acdirmax = 60;
1356 data.namlen = NAME_MAX;
1357
1358 soft = 0;
1359 intr = 0;
1360 posix = 0;
1361 nocto = 0;
1362 nolock = 0;
1363 noac = 0;
1364 nordirplus = 0;
1365 noacl = 0;
1366 retry = 10000; /* 10000 minutes ~ 1 week */
1367 tcp = 1; /* nfs-utils uses tcp per default */
1368
1369 mountprog = MOUNTPROG;
1370 mountvers = 0;
1371 port = 0;
1372 mountport = 0;
1373 nfsprog = 100003;
1374 nfsvers = 0;
1375
1376 /* parse options */
1377 if (filteropts) for (opt = strtok_r(filteropts, ",", &tokstate); opt; opt = strtok_r(NULL, ",", &tokstate)) {
1378 char *opteq = strchr(opt, '=');
1379 if (opteq) {
1380 int val, idx;
1381 static const char options[] ALIGN1 =
1382 /* 0 */ "rsize\0"
1383 /* 1 */ "wsize\0"
1384 /* 2 */ "timeo\0"
1385 /* 3 */ "retrans\0"
1386 /* 4 */ "acregmin\0"
1387 /* 5 */ "acregmax\0"
1388 /* 6 */ "acdirmin\0"
1389 /* 7 */ "acdirmax\0"
1390 /* 8 */ "actimeo\0"
1391 /* 9 */ "retry\0"
1392 /* 10 */ "port\0"
1393 /* 11 */ "mountport\0"
1394 /* 12 */ "mounthost\0"
1395 /* 13 */ "mountprog\0"
1396 /* 14 */ "mountvers\0"
1397 /* 15 */ "nfsprog\0"
1398 /* 16 */ "nfsvers\0"
1399 /* 17 */ "vers\0"
1400 /* 18 */ "proto\0"
1401 /* 19 */ "namlen\0"
1402 /* 20 */ "addr\0";
1403
1404 *opteq++ = '\0';
1405 idx = index_in_strings(options, opt);
1406 switch (idx) {
1407 case 12: // "mounthost"
1408 mounthost = xstrndup(opteq,
1409 strcspn(opteq, " \t\n\r,"));
1410 continue;
1411 case 18: // "proto"
1412 if (is_prefixed_with(opteq, "tcp"))
1413 tcp = 1;
1414 else if (is_prefixed_with(opteq, "udp"))
1415 tcp = 0;
1416 else
1417 bb_simple_error_msg("warning: unrecognized proto= option");
1418 continue;
1419 case 20: // "addr" - ignore
1420 continue;
1421 case -1: // unknown
1422 if (vfsflags & MS_REMOUNT)
1423 continue;
1424 }
1425
1426 val = xatoi_positive(opteq);
1427 switch (idx) {
1428 case 0: // "rsize"
1429 data.rsize = val;
1430 continue;
1431 case 1: // "wsize"
1432 data.wsize = val;
1433 continue;
1434 case 2: // "timeo"
1435 data.timeo = val;
1436 continue;
1437 case 3: // "retrans"
1438 data.retrans = val;
1439 continue;
1440 case 4: // "acregmin"
1441 data.acregmin = val;
1442 continue;
1443 case 5: // "acregmax"
1444 data.acregmax = val;
1445 continue;
1446 case 6: // "acdirmin"
1447 data.acdirmin = val;
1448 continue;
1449 case 7: // "acdirmax"
1450 data.acdirmax = val;
1451 continue;
1452 case 8: // "actimeo"
1453 data.acregmin = val;
1454 data.acregmax = val;
1455 data.acdirmin = val;
1456 data.acdirmax = val;
1457 continue;
1458 case 9: // "retry"
1459 retry = val;
1460 continue;
1461 case 10: // "port"
1462 port = val;
1463 continue;
1464 case 11: // "mountport"
1465 mountport = val;
1466 continue;
1467 case 13: // "mountprog"
1468 mountprog = val;
1469 continue;
1470 case 14: // "mountvers"
1471 mountvers = val;
1472 continue;
1473 case 15: // "nfsprog"
1474 nfsprog = val;
1475 continue;
1476 case 16: // "nfsvers"
1477 case 17: // "vers"
1478 nfsvers = val;
1479 continue;
1480 case 19: // "namlen"
1481 //if (nfs_mount_version >= 2)
1482 data.namlen = val;
1483 //else
1484 // bb_error_msg("warning: option namlen is not supported\n");
1485 continue;
1486 default:
1487 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1488 goto fail;
1489 }
1490 }
1491 else { /* not of the form opt=val */
1492 static const char options[] ALIGN1 =
1493 "bg\0"
1494 "fg\0"
1495 "soft\0"
1496 "hard\0"
1497 "intr\0"
1498 "posix\0"
1499 "cto\0"
1500 "ac\0"
1501 "tcp\0"
1502 "udp\0"
1503 "lock\0"
1504 "rdirplus\0"
1505 "acl\0";
1506 int val = 1;
1507 if (is_prefixed_with(opt, "no")) {
1508 val = 0;
1509 opt += 2;
1510 }
1511 switch (index_in_strings(options, opt)) {
1512 case 0: // "bg"
1513 #if BB_MMU
1514 bg = val;
1515 #endif
1516 break;
1517 case 1: // "fg"
1518 #if BB_MMU
1519 bg = !val;
1520 #endif
1521 break;
1522 case 2: // "soft"
1523 soft = val;
1524 break;
1525 case 3: // "hard"
1526 soft = !val;
1527 break;
1528 case 4: // "intr"
1529 intr = val;
1530 break;
1531 case 5: // "posix"
1532 posix = val;
1533 break;
1534 case 6: // "cto"
1535 nocto = !val;
1536 break;
1537 case 7: // "ac"
1538 noac = !val;
1539 break;
1540 case 8: // "tcp"
1541 tcp = val;
1542 break;
1543 case 9: // "udp"
1544 tcp = !val;
1545 break;
1546 case 10: // "lock"
1547 if (nfs_mount_version >= 3)
1548 nolock = !val;
1549 else
1550 bb_simple_error_msg("warning: option nolock is not supported");
1551 break;
1552 case 11: //rdirplus
1553 nordirplus = !val;
1554 break;
1555 case 12: // acl
1556 noacl = !val;
1557 break;
1558 default:
1559 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1560 goto fail;
1561 }
1562 }
1563 }
1564 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1565
1566 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1567 | (intr ? NFS_MOUNT_INTR : 0)
1568 | (posix ? NFS_MOUNT_POSIX : 0)
1569 | (nocto ? NFS_MOUNT_NOCTO : 0)
1570 | (noac ? NFS_MOUNT_NOAC : 0)
1571 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
1572 | (noacl ? NFS_MOUNT_NOACL : 0);
1573 if (nfs_mount_version >= 2)
1574 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1575 if (nfs_mount_version >= 3)
1576 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1577 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1578 bb_error_msg("NFSv%d not supported", nfsvers);
1579 goto fail;
1580 }
1581 if (nfsvers && !mountvers)
1582 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1583 if (nfsvers && nfsvers < mountvers) {
1584 mountvers = nfsvers;
1585 }
1586
1587 /* Adjust options if none specified */
1588 if (!data.timeo)
1589 data.timeo = tcp ? 70 : 7;
1590
1591 data.version = nfs_mount_version;
1592
1593 if (vfsflags & MS_REMOUNT)
1594 goto do_mount;
1595
1596 /*
1597 * If the previous mount operation on the same host was
1598 * backgrounded, and the "bg" for this mount is also set,
1599 * give up immediately, to avoid the initial timeout.
1600 */
1601 if (bg && we_saw_this_host_before(hostname)) {
1602 daemonized = daemonize();
1603 if (daemonized <= 0) { /* parent or error */
1604 retval = -daemonized;
1605 goto ret;
1606 }
1607 }
1608
1609 /* Create mount daemon client */
1610 /* See if the nfs host = mount host. */
1611 if (mounthost) {
1612 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1613 mount_server_addr.sin_family = AF_INET;
1614 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1615 } else {
1616 hp = gethostbyname(mounthost);
1617 if (hp == NULL) {
1618 bb_simple_herror_msg(mounthost);
1619 goto fail;
1620 }
1621 if (hp->h_length != (int)sizeof(struct in_addr)) {
1622 bb_simple_error_msg_and_die("only IPv4 is supported");
1623 }
1624 mount_server_addr.sin_family = AF_INET;
1625 memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1626 }
1627 }
1628
1629 /*
1630 * The following loop implements the mount retries. When the mount
1631 * times out, and the "bg" option is set, we background ourself
1632 * and continue trying.
1633 *
1634 * The case where the mount point is not present and the "bg"
1635 * option is set, is treated as a timeout. This is done to
1636 * support nested mounts.
1637 *
1638 * The "retry" count specified by the user is the number of
1639 * minutes to retry before giving up.
1640 */
1641 {
1642 struct timeval total_timeout;
1643 struct timeval retry_timeout;
1644 struct pmap pm_mnt;
1645 time_t t;
1646 time_t prevt;
1647 time_t timeout;
1648
1649 retry_timeout.tv_sec = 3;
1650 retry_timeout.tv_usec = 0;
1651 total_timeout.tv_sec = 20;
1652 total_timeout.tv_usec = 0;
1653 /* FIXME: use monotonic()? */
1654 timeout = time(NULL) + 60 * retry;
1655 prevt = 0;
1656 t = 30;
1657 retry:
1658 /* Be careful not to use too many CPU cycles */
1659 if (t - prevt < 30)
1660 sleep(30);
1661
1662 get_mountport(&pm_mnt, &mount_server_addr,
1663 mountprog,
1664 mountvers,
1665 proto,
1666 mountport);
1667 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
1668
1669 /* contact the mount daemon via TCP */
1670 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1671 msock = RPC_ANYSOCK;
1672
1673 switch (pm_mnt.pm_prot) {
1674 case IPPROTO_UDP:
1675 mclient = clntudp_create(&mount_server_addr,
1676 pm_mnt.pm_prog,
1677 pm_mnt.pm_vers,
1678 retry_timeout,
1679 &msock);
1680 if (mclient)
1681 break;
1682 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1683 msock = RPC_ANYSOCK;
1684 case IPPROTO_TCP:
1685 mclient = clnttcp_create(&mount_server_addr,
1686 pm_mnt.pm_prog,
1687 pm_mnt.pm_vers,
1688 &msock, 0, 0);
1689 break;
1690 default:
1691 mclient = NULL;
1692 }
1693 if (!mclient) {
1694 if (!daemonized && prevt == 0)
1695 error_msg_rpc(clnt_spcreateerror(" "));
1696 } else {
1697 enum clnt_stat clnt_stat;
1698
1699 /* Try to mount hostname:pathname */
1700 mclient->cl_auth = authunix_create_default();
1701
1702 /* Make pointers in xdr_mountres3 NULL so
1703 * that xdr_array allocates memory for us
1704 */
1705 memset(&status, 0, sizeof(status));
1706
1707 if (pm_mnt.pm_vers == 3)
1708 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1709 (xdrproc_t) xdr_dirpath,
1710 (caddr_t) &pathname,
1711 (xdrproc_t) xdr_mountres3,
1712 (caddr_t) &status,
1713 total_timeout);
1714 else
1715 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1716 (xdrproc_t) xdr_dirpath,
1717 (caddr_t) &pathname,
1718 (xdrproc_t) xdr_fhstatus,
1719 (caddr_t) &status,
1720 total_timeout);
1721
1722 if (clnt_stat == RPC_SUCCESS)
1723 goto prepare_kernel_data; /* we're done */
1724 if (errno != ECONNREFUSED) {
1725 error_msg_rpc(clnt_sperror(mclient, " "));
1726 goto fail; /* don't retry */
1727 }
1728 /* Connection refused */
1729 if (!daemonized && prevt == 0) /* print just once */
1730 error_msg_rpc(clnt_sperror(mclient, " "));
1731 auth_destroy(mclient->cl_auth);
1732 clnt_destroy(mclient);
1733 mclient = NULL;
1734 close(msock);
1735 msock = -1;
1736 }
1737
1738 /* Timeout. We are going to retry... maybe */
1739 if (!bg)
1740 goto fail;
1741 if (!daemonized) {
1742 daemonized = daemonize();
1743 if (daemonized <= 0) { /* parent or error */
1744 retval = -daemonized;
1745 goto ret;
1746 }
1747 }
1748 prevt = t;
1749 t = time(NULL);
1750 if (t >= timeout)
1751 /* TODO error message */
1752 goto fail;
1753
1754 goto retry;
1755 }
1756
1757 prepare_kernel_data:
1758
1759 if (nfsvers == 2) {
1760 if (status.nfsv2.fhs_status != 0) {
1761 bb_error_msg("%s:%s failed, reason given by server: %s",
1762 hostname, pathname,
1763 nfs_strerror(status.nfsv2.fhs_status));
1764 goto fail;
1765 }
1766 memcpy(data.root.data,
1767 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1768 NFS_FHSIZE);
1769 data.root.size = NFS_FHSIZE;
1770 memcpy(data.old_root.data,
1771 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1772 NFS_FHSIZE);
1773 } else {
1774 fhandle3 *my_fhandle;
1775 if (status.nfsv3.fhs_status != 0) {
1776 bb_error_msg("%s:%s failed, reason given by server: %s",
1777 hostname, pathname,
1778 nfs_strerror(status.nfsv3.fhs_status));
1779 goto fail;
1780 }
1781 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1782 memset(data.old_root.data, 0, NFS_FHSIZE);
1783 memset(&data.root, 0, sizeof(data.root));
1784 data.root.size = my_fhandle->fhandle3_len;
1785 memcpy(data.root.data,
1786 (char *) my_fhandle->fhandle3_val,
1787 my_fhandle->fhandle3_len);
1788
1789 data.flags |= NFS_MOUNT_VER3;
1790 }
1791
1792 /* Create nfs socket for kernel */
1793 if (tcp) {
1794 if (nfs_mount_version < 3) {
1795 bb_simple_error_msg("NFS over TCP is not supported");
1796 goto fail;
1797 }
1798 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1799 } else
1800 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1801 if (fsock < 0) {
1802 bb_simple_perror_msg("nfs socket");
1803 goto fail;
1804 }
1805 if (bindresvport(fsock, 0) < 0) {
1806 bb_simple_perror_msg("nfs bindresvport");
1807 goto fail;
1808 }
1809 if (port == 0) {
1810 server_addr.sin_port = PMAPPORT;
1811 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1812 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1813 if (port == 0)
1814 port = NFS_PORT;
1815 }
1816 server_addr.sin_port = htons(port);
1817
1818 /* Prepare data structure for kernel */
1819 data.fd = fsock;
1820 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1821 strncpy(data.hostname, hostname, sizeof(data.hostname));
1822
1823 /* Clean up */
1824 auth_destroy(mclient->cl_auth);
1825 clnt_destroy(mclient);
1826 close(msock);
1827 msock = -1;
1828
1829 if (bg) {
1830 /* We must wait until mount directory is available */
1831 struct stat statbuf;
1832 int delay = 1;
1833 while (stat(mp->mnt_dir, &statbuf) == -1) {
1834 if (!daemonized) {
1835 daemonized = daemonize();
1836 if (daemonized <= 0) { /* parent or error */
1837 /* FIXME: parent doesn't close fsock - ??! */
1838 retval = -daemonized;
1839 goto ret;
1840 }
1841 }
1842 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1843 delay *= 2;
1844 if (delay > 30)
1845 delay = 30;
1846 }
1847 }
1848
1849 /* Perform actual mount */
1850 do_mount:
1851 retval = mount_it_now(mp, vfsflags, (char*)&data);
1852 goto ret;
1853
1854 /* Abort */
1855 fail:
1856 if (msock >= 0) {
1857 if (mclient) {
1858 auth_destroy(mclient->cl_auth);
1859 clnt_destroy(mclient);
1860 }
1861 close(msock);
1862 }
1863 if (fsock >= 0)
1864 close(fsock);
1865
1866 ret:
1867 free(hostname);
1868 free(mounthost);
1869 free(filteropts);
1870 return retval;
1871 }
1872
1873 #else // !ENABLE_FEATURE_MOUNT_NFS
1874
1875 /* Linux 2.6.23+ supports nfs mounts with options passed as a string.
1876 * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
1877 * (However, note that then you lose any chances that NFS over IPv6 would work).
1878 */
nfsmount(struct mntent * mp,unsigned long vfsflags,char * filteropts)1879 static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1880 {
1881 len_and_sockaddr *lsa;
1882 char *opts;
1883 char *end;
1884 char *dotted;
1885 int ret;
1886
1887 # if ENABLE_FEATURE_IPV6
1888 end = strchr(mp->mnt_fsname, ']');
1889 if (end && end[1] == ':')
1890 end++;
1891 else
1892 # endif
1893 /* mount_main() guarantees that ':' is there */
1894 end = strchr(mp->mnt_fsname, ':');
1895
1896 *end = '\0';
1897 lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
1898 *end = ':';
1899 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1900 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1901 opts = xasprintf("%s%saddr=%s",
1902 filteropts ? filteropts : "",
1903 filteropts ? "," : "",
1904 dotted
1905 );
1906 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1907 ret = mount_it_now(mp, vfsflags, opts);
1908 if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1909
1910 return ret;
1911 }
1912
1913 #endif // !ENABLE_FEATURE_MOUNT_NFS
1914
1915 // Find "...,NAME=NUM,..." in the option string, remove "NAME=NUM" option
1916 // and return NUM.
1917 // Return 0 if not found.
1918 // All instances must be parsed and removed (for example, since kernel 5.4
1919 // squashfs: Unknown parameter 'sizelimit'
1920 // will result if loopback mount option "sizelimit=NNN" is not removed
1921 // and squashfs sees it in option string).
cut_out_ull_opt(char * opts,const char * name_eq)1922 static unsigned long long cut_out_ull_opt(char *opts, const char *name_eq)
1923 {
1924 unsigned long long ret = 0;
1925
1926 if (!opts) // allow NULL opts (simplifies callers' work)
1927 return ret;
1928
1929 for (;;) {
1930 char *end;
1931 char *opt;
1932
1933 // Find comma-delimited "NAME="
1934 for (;;) {
1935 opt = strstr(opts, name_eq);
1936 if (!opt)
1937 return ret;
1938 if (opt == opts)
1939 break; // found it (it's first opt)
1940 if (opt[-1] == ',') {
1941 opts = opt - 1;
1942 break; // found it (it's not a first opt)
1943 }
1944 // False positive like "VNAME=", we are at "N".
1945 // - skip it, loop back to searching
1946 opts = opt + 1;
1947 }
1948
1949 ret = bb_strtoull(opt + strlen(name_eq), &end, 0);
1950 if (errno && errno != EINVAL) {
1951 err:
1952 bb_error_msg_and_die("bad option '%s'", opt);
1953 }
1954 if (*end == '\0') {
1955 // It is "[,]NAME=NUM\0" - truncate it and return
1956 *opts = '\0';
1957 return ret;
1958 }
1959 if (*end != ',')
1960 goto err;
1961 // We are at trailing comma
1962 // Remove "NAME=NUM," and loop back to check for duplicate opts
1963 overlapping_strcpy(opt, end + 1);
1964 }
1965 }
1966
1967 // Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1968 // type detection. Returns 0 for success, nonzero for failure.
1969 // NB: mp->xxx fields may be trashed on exit
singlemount(struct mntent * mp,int ignore_busy)1970 static int singlemount(struct mntent *mp, int ignore_busy)
1971 {
1972 int loopfd = -1;
1973 int rc = -1;
1974 unsigned long vfsflags;
1975 char *loopFile = NULL, *filteropts = NULL;
1976 llist_t *fl = NULL;
1977 struct stat st;
1978
1979 errno = 0;
1980
1981 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts, NULL);
1982
1983 // Treat fstype "auto" as unspecified
1984 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1985 mp->mnt_type = NULL;
1986
1987 // Might this be a virtual filesystem?
1988 if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1989 char *args[35];
1990 char *s;
1991 int n;
1992 // fsname: "cmd#arg1#arg2..."
1993 // WARNING: allows execution of arbitrary commands!
1994 // Try "mount 'sh#-c#sh' bogus_dir".
1995 // It is safe ONLY because non-root
1996 // cannot use two-argument mount command
1997 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1998 // "mount: can't find sh#-c#sh in /etc/fstab"
1999 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
2000
2001 s = mp->mnt_fsname;
2002 n = 0;
2003 args[n++] = s;
2004 while (*s && n < 35 - 2) {
2005 if (*s++ == '#' && *s != '#') {
2006 s[-1] = '\0';
2007 args[n++] = s;
2008 }
2009 }
2010 args[n++] = mp->mnt_dir;
2011 args[n] = NULL;
2012 rc = spawn_and_wait(args);
2013 goto report_error;
2014 }
2015
2016 // Might this be an CIFS filesystem?
2017 if (ENABLE_FEATURE_MOUNT_CIFS
2018 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
2019 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
2020 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
2021 ) {
2022 int len;
2023 char c;
2024 char *hostname, *share;
2025 len_and_sockaddr *lsa;
2026
2027 // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
2028
2029 hostname = mp->mnt_fsname + 2;
2030 len = strcspn(hostname, "/\\");
2031 share = hostname + len + 1;
2032 if (len == 0 // 3rd char is a [back]slash (IOW: empty hostname)
2033 || share[-1] == '\0' // no [back]slash after hostname
2034 || share[0] == '\0' // empty share name
2035 ) {
2036 goto report_error;
2037 }
2038 c = share[-1];
2039 share[-1] = '\0';
2040 len = strcspn(share, "/\\");
2041
2042 // "unc=\\hostname\share" option is mandatory
2043 // after CIFS option parsing was rewritten in Linux 3.4.
2044 // Must use backslashes.
2045 // If /dir1/dir2 is present, also add "prefixpath=dir1/dir2"
2046 {
2047 char *unc = xasprintf(
2048 share[len] != '\0' /* "/dir1/dir2" exists? */
2049 ? "unc=\\\\%s\\%.*s,prefixpath=%s"
2050 : "unc=\\\\%s\\%.*s",
2051 hostname,
2052 len, share,
2053 share + len + 1 /* "dir1/dir2" */
2054 );
2055 parse_mount_options(unc, &filteropts, NULL);
2056 if (ENABLE_FEATURE_CLEAN_UP) free(unc);
2057 }
2058
2059 lsa = host2sockaddr(hostname, 0);
2060 share[-1] = c;
2061 if (!lsa)
2062 goto report_error;
2063
2064 // If there is no "ip=..." option yet
2065 if (!is_prefixed_with(filteropts, ",ip="+1)
2066 && !strstr(filteropts, ",ip=")
2067 ) {
2068 char *dotted, *ip;
2069 // Insert "ip=..." option into options
2070 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
2071 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
2072 ip = xasprintf("ip=%s", dotted);
2073 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
2074 // Note: IPv6 scoped addresses ("host%iface", see RFC 4007) should be
2075 // handled by libc in getnameinfo() (inside xmalloc_sockaddr2dotted_noport()).
2076 // Currently, glibc does not support that (has no NI_NUMERICSCOPE),
2077 // musl apparently does. This results in "ip=numericIPv6%iface_name"
2078 // (instead of _numeric_ iface_id) with glibc.
2079 // This probably should be fixed in glibc, not here.
2080 // The workaround is to manually specify correct "ip=ADDR%n" option.
2081 parse_mount_options(ip, &filteropts, NULL);
2082 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
2083 }
2084
2085 mp->mnt_type = (char*)"cifs";
2086 rc = mount_it_now(mp, vfsflags, filteropts);
2087
2088 goto report_error;
2089 }
2090
2091 // Might this be an NFS filesystem?
2092 if (!(vfsflags & (MS_BIND | MS_MOVE))
2093 && (!mp->mnt_type || is_prefixed_with(mp->mnt_type, "nfs"))
2094 ) {
2095 char *colon = strchr(mp->mnt_fsname, ':');
2096 if (colon // looks like "hostname:..."
2097 && strchrnul(mp->mnt_fsname, '/') > colon // "hostname:" has no slashes
2098 ) {
2099 if (!mp->mnt_type)
2100 mp->mnt_type = (char*)"nfs";
2101 rc = nfsmount(mp, vfsflags, filteropts);
2102 goto report_error;
2103 }
2104 }
2105
2106 // Look at the file. (Not found isn't a failure for remount, or for
2107 // a synthetic filesystem like proc or sysfs.)
2108 // (We use stat, not lstat, in order to allow
2109 // mount symlink_to_file_or_blkdev dir)
2110 if (!stat(mp->mnt_fsname, &st)
2111 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
2112 ) {
2113 // Do we need to allocate a loopback device for it?
2114 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
2115 unsigned long long offset;
2116 unsigned long long sizelimit;
2117
2118 loopFile = bb_simplify_path(mp->mnt_fsname);
2119 mp->mnt_fsname = NULL; // will receive malloced loop dev name
2120
2121 // Parse and remove loopback options
2122 offset = cut_out_ull_opt(filteropts, "offset=");
2123 sizelimit = cut_out_ull_opt(filteropts, "sizelimit=");
2124
2125 // mount always creates AUTOCLEARed loopdevs, so that umounting
2126 // drops them without any code in the userspace.
2127 // This happens since circa linux-2.6.25:
2128 // commit 96c5865559cee0f9cbc5173f3c949f6ce3525581
2129 // Date: Wed Feb 6 01:36:27 2008 -0800
2130 // Subject: Allow auto-destruction of loop devices
2131 loopfd = set_loop(&mp->mnt_fsname,
2132 loopFile,
2133 offset,
2134 sizelimit,
2135 ((vfsflags & MS_RDONLY) ? BB_LO_FLAGS_READ_ONLY : 0)
2136 | BB_LO_FLAGS_AUTOCLEAR
2137 );
2138 if (loopfd < 0) {
2139 if (errno == EPERM || errno == EACCES)
2140 bb_simple_error_msg(bb_msg_perm_denied_are_you_root);
2141 else
2142 bb_simple_perror_msg("can't setup loop device");
2143 return loopfd; // was "return errno", but it can be 0 here
2144 }
2145
2146 // Autodetect bind mounts
2147 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
2148 vfsflags |= MS_BIND;
2149 }
2150
2151 // If we know the fstype (or don't need to), jump straight
2152 // to the actual mount.
2153 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
2154 char *next;
2155 for (;;) {
2156 next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
2157 if (next)
2158 *next = '\0';
2159 rc = mount_it_now(mp, vfsflags, filteropts);
2160 if (rc == 0 || !next)
2161 break;
2162 mp->mnt_type = next + 1;
2163 }
2164 } else {
2165 // Loop through filesystem types until mount succeeds
2166 // or we run out
2167
2168 // Initialize list of block backed filesystems.
2169 // This has to be done here so that during "mount -a",
2170 // mounts after /proc shows up can autodetect.
2171 if (!fslist) {
2172 fslist = get_block_backed_filesystems();
2173 if (ENABLE_FEATURE_CLEAN_UP && fslist)
2174 atexit(delete_block_backed_filesystems);
2175 }
2176
2177 for (fl = fslist; fl; fl = fl->link) {
2178 mp->mnt_type = fl->data;
2179 rc = mount_it_now(mp, vfsflags, filteropts);
2180 if (rc == 0)
2181 break;
2182 }
2183 }
2184
2185 // If mount failed, clean up loop file (if any).
2186 // (Newer kernels which support LO_FLAGS_AUTOCLEAR should not need this,
2187 // merely "close(loopfd)" should do it?)
2188 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
2189 del_loop(mp->mnt_fsname);
2190 if (ENABLE_FEATURE_CLEAN_UP) {
2191 free(loopFile);
2192 /* No, "rc != 0" needs it: free(mp->mnt_fsname); */
2193 }
2194 }
2195
2196 report_error:
2197 if (ENABLE_FEATURE_CLEAN_UP)
2198 free(filteropts);
2199
2200 if (loopfd >= 0)
2201 close(loopfd);
2202
2203 if (errno == EBUSY && ignore_busy)
2204 return 0;
2205 if (errno == ENOENT && (vfsflags & MOUNT_NOFAIL))
2206 return 0;
2207 if (rc != 0)
2208 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
2209 return rc;
2210 }
2211
2212 // -O support
2213 // -O interprets a list of filter options which select whether a mount
2214 // point will be mounted: only mounts with options matching *all* filtering
2215 // options will be selected.
2216 // By default each -O filter option must be present in the list of mount
2217 // options, but if it is prefixed by "no" then it must be absent.
2218 // For example,
2219 // -O a,nob,c matches -o a,c but fails to match -o a,b,c
2220 // (and also fails to match -o a because -o c is absent).
2221 //
2222 // It is different from -t in that each option is matched exactly; a leading
2223 // "no" at the beginning of one option does not negate the rest.
match_opt(const char * fs_opt_in,const char * O_opt)2224 static int match_opt(const char *fs_opt_in, const char *O_opt)
2225 {
2226 if (!O_opt)
2227 return 1;
2228
2229 while (*O_opt) {
2230 const char *fs_opt = fs_opt_in;
2231 int O_len;
2232 int match;
2233
2234 // If option begins with "no" then treat as an inverted match:
2235 // matching is a failure
2236 match = 0;
2237 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
2238 match = 1;
2239 O_opt += 2;
2240 }
2241 // Isolate the current O option
2242 O_len = strchrnul(O_opt, ',') - O_opt;
2243 // Check for a match against existing options
2244 while (1) {
2245 if (strncmp(fs_opt, O_opt, O_len) == 0
2246 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
2247 ) {
2248 if (match)
2249 return 0; // "no" prefix, but option found
2250 match = 1; // current O option found, go check next one
2251 break;
2252 }
2253 fs_opt = strchr(fs_opt, ',');
2254 if (!fs_opt)
2255 break;
2256 fs_opt++;
2257 }
2258 if (match == 0)
2259 return 0; // match wanted but not found
2260 if (O_opt[O_len] == '\0') // end?
2261 break;
2262 // Step to the next O option
2263 O_opt += O_len + 1;
2264 }
2265 // If we get here then everything matched
2266 return 1;
2267 }
2268
2269 // Parse options, if necessary parse fstab/mtab, and call singlemount for
2270 // each directory to be mounted.
2271 int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
mount_main(int argc UNUSED_PARAM,char ** argv)2272 int mount_main(int argc UNUSED_PARAM, char **argv)
2273 {
2274 char *cmdopts = xzalloc(1);
2275 char *fstype = NULL;
2276 char *O_optmatch = NULL;
2277 char *storage_path;
2278 llist_t *lst_o = NULL;
2279 const char *fstabname = "/etc/fstab";
2280 FILE *fstab;
2281 int i, j;
2282 int rc = EXIT_SUCCESS;
2283 unsigned long cmdopt_flags;
2284 unsigned opt;
2285 struct mntent mtpair[2], *mtcur = mtpair;
2286 IF_NOT_DESKTOP(const int nonroot = 0;)
2287
2288 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
2289
2290 INIT_G();
2291
2292 // Parse long options, like --bind and --move. Note that -o option
2293 // and --option are synonymous. Yes, this means --remount,rw works.
2294 for (i = j = 1; argv[i]; i++) {
2295 if (argv[i][0] == '-' && argv[i][1] == '-')
2296 append_mount_options(&cmdopts, argv[i] + 2);
2297 else
2298 argv[j++] = argv[i];
2299 }
2300 argv[j] = NULL;
2301
2302 // Parse remaining options
2303 // Max 2 params; -o is a list, -v is a counter
2304 opt = getopt32(argv, "^"
2305 OPTION_STR
2306 "\0" "?2"IF_FEATURE_MOUNT_VERBOSE("vv"),
2307 &lst_o, &fstype, &O_optmatch
2308 IF_FEATURE_MOUNT_OTHERTAB(, &fstabname)
2309 IF_FEATURE_MOUNT_VERBOSE(, &verbose)
2310 );
2311
2312 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
2313 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2314 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
2315 argv += optind;
2316
2317 // If we have no arguments, show currently mounted filesystems
2318 if (!argv[0]) {
2319 if (!(opt & OPT_a)) {
2320 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2321
2322 if (!mountTable)
2323 bb_error_msg_and_die("no %s", bb_path_mtab_file);
2324
2325 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
2326 GETMNTENT_BUFSIZE))
2327 {
2328 // Don't show rootfs. FIXME: why??
2329 // util-linux 2.12a happily shows rootfs...
2330 //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
2331
2332 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
2333 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2334 mtpair->mnt_dir, mtpair->mnt_type,
2335 mtpair->mnt_opts);
2336 }
2337 if (ENABLE_FEATURE_CLEAN_UP)
2338 endmntent(mountTable);
2339 return EXIT_SUCCESS;
2340 }
2341 storage_path = NULL;
2342 } else {
2343 // When we have two arguments, the second is the directory and we can
2344 // skip looking at fstab entirely. We can always abspath() the directory
2345 // argument when we get it.
2346 if (argv[1]) {
2347 if (nonroot)
2348 bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
2349 mtpair->mnt_fsname = argv[0];
2350 mtpair->mnt_dir = argv[1];
2351 mtpair->mnt_type = fstype;
2352 mtpair->mnt_opts = cmdopts;
2353 resolve_mount_spec(&mtpair->mnt_fsname);
2354 rc = singlemount(mtpair, /*ignore_busy:*/ 0);
2355 return rc;
2356 }
2357 storage_path = bb_simplify_path(argv[0]); // malloced
2358 }
2359
2360 // Past this point, we are handling either "mount -a [opts]"
2361 // or "mount [opts] single_param"
2362
2363 cmdopt_flags = parse_mount_options(cmdopts, NULL, &option_mask32);
2364 if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
2365 bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
2366
2367 // If we have a shared subtree flag, don't worry about fstab or mtab.
2368 if (ENABLE_FEATURE_MOUNT_FLAGS
2369 && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
2370 ) {
2371 // verbose_mount(source, target, type, flags, data)
2372 rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
2373 if (rc)
2374 bb_simple_perror_msg_and_die(argv[0]);
2375 return rc;
2376 }
2377
2378 // A malicious user could overmount /usr without this.
2379 if (ENABLE_FEATURE_MOUNT_OTHERTAB && nonroot)
2380 fstabname = "/etc/fstab";
2381 // Open either fstab or mtab
2382 if (cmdopt_flags & MS_REMOUNT) {
2383 // WARNING. I am not sure this matches util-linux's
2384 // behavior. It's possible util-linux does not
2385 // take -o opts from mtab (takes only mount source).
2386 fstabname = bb_path_mtab_file;
2387 }
2388 fstab = setmntent(fstabname, "r");
2389 if (!fstab)
2390 bb_perror_msg_and_die("can't read '%s'", fstabname);
2391
2392 // Loop through entries until we find what we're looking for
2393 memset(mtpair, 0, sizeof(mtpair));
2394 for (;;) {
2395 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
2396
2397 // Get next fstab entry
2398 if (!getmntent_r(fstab, mtcur, getmntent_buf
2399 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
2400 GETMNTENT_BUFSIZE/2)
2401 ) { // End of fstab/mtab is reached
2402 mtcur = mtother; // the thing we found last time
2403 break;
2404 }
2405
2406 // If we're trying to mount something specific and this isn't it,
2407 // skip it. Note we must match the exact text in fstab (ala
2408 // "proc") or a full path from root
2409 if (argv[0]) {
2410
2411 // Is this what we're looking for?
2412 if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2413 && strcmp(storage_path, mtcur->mnt_fsname) != 0
2414 && strcmp(argv[0], mtcur->mnt_dir) != 0
2415 && strcmp(storage_path, mtcur->mnt_dir) != 0
2416 ) {
2417 continue; // no
2418 }
2419
2420 // Remember this entry. Something later may have
2421 // overmounted it, and we want the _last_ match.
2422 mtcur = mtother;
2423
2424 // If we're mounting all
2425 } else {
2426 struct mntent *mp;
2427 // No, mount -a won't mount anything,
2428 // even user mounts, for mere humans
2429 if (nonroot)
2430 bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
2431
2432 // Does type match? (NULL matches always)
2433 if (!fstype_matches(mtcur->mnt_type, fstype))
2434 continue;
2435
2436 // Skip noauto and swap anyway
2437 if ((parse_mount_options(mtcur->mnt_opts, NULL, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2438 // swap is bogus "fstype", parse_mount_options can't check fstypes
2439 || strcasecmp(mtcur->mnt_type, "swap") == 0
2440 ) {
2441 continue;
2442 }
2443
2444 // Does (at least one) option match?
2445 // (NULL matches always)
2446 if (!match_opt(mtcur->mnt_opts, O_optmatch))
2447 continue;
2448
2449 resolve_mount_spec(&mtcur->mnt_fsname);
2450
2451 // NFS mounts want this to be xrealloc-able
2452 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2453
2454 // If nothing is mounted on this directory...
2455 // (otherwise repeated "mount -a" mounts everything again)
2456 mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2457 // We do not check fsname match of found mount point -
2458 // "/" may have fsname of "/dev/root" while fstab
2459 // says "/dev/something_else".
2460 if (mp) {
2461 if (verbose) {
2462 bb_error_msg("according to %s, "
2463 "%s is already mounted on %s",
2464 bb_path_mtab_file,
2465 mp->mnt_fsname, mp->mnt_dir);
2466 }
2467 } else {
2468 // ...mount this thing
2469 if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2470 // Count number of failed mounts
2471 rc++;
2472 }
2473 }
2474 free(mtcur->mnt_opts);
2475 }
2476 }
2477
2478 // End of fstab/mtab is reached.
2479 // Were we looking for something specific?
2480 if (argv[0]) { // yes
2481 unsigned long l;
2482
2483 // If we didn't find anything, complain
2484 if (!mtcur->mnt_fsname)
2485 bb_error_msg_and_die("can't find %s in %s",
2486 argv[0], fstabname);
2487
2488 // What happens when we try to "mount swap_partition"?
2489 // (fstab containts "swap_partition swap swap defaults 0 0")
2490 // util-linux-ng 2.13.1 does this:
2491 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2492 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2493 // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory)
2494 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2495 // exit_group(32) = ?
2496 #if 0
2497 // In case we want to simply skip swap partitions:
2498 l = parse_mount_options(mtcur->mnt_opts, NULL, NULL);
2499 if ((l & MOUNT_SWAP)
2500 // swap is bogus "fstype", parse_mount_options can't check fstypes
2501 || strcasecmp(mtcur->mnt_type, "swap") == 0
2502 ) {
2503 goto ret;
2504 }
2505 #endif
2506 if (nonroot) {
2507 // fstab must have "users" or "user"
2508 l = parse_mount_options(mtcur->mnt_opts, NULL, NULL);
2509 if (!(l & MOUNT_USERS))
2510 bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
2511 }
2512
2513 //util-linux-2.12 does not do this check.
2514 //// If nothing is mounted on this directory...
2515 //// (otherwise repeated "mount FOO" mounts FOO again)
2516 //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2517 //if (mp) {
2518 // bb_error_msg("according to %s, "
2519 // "%s is already mounted on %s",
2520 // bb_path_mtab_file,
2521 // mp->mnt_fsname, mp->mnt_dir);
2522 //} else {
2523 // ...mount the last thing we found
2524 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2525 append_mount_options(&(mtcur->mnt_opts), cmdopts);
2526 resolve_mount_spec(&mtpair->mnt_fsname);
2527 rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2528 if (ENABLE_FEATURE_CLEAN_UP)
2529 free(mtcur->mnt_opts);
2530 //}
2531 }
2532
2533 //ret:
2534 if (ENABLE_FEATURE_CLEAN_UP)
2535 endmntent(fstab);
2536 if (ENABLE_FEATURE_CLEAN_UP) {
2537 free(storage_path);
2538 free(cmdopts);
2539 }
2540
2541 //TODO: exitcode should be ORed mask of (from "man mount"):
2542 // 0 success
2543 // 1 incorrect invocation or permissions
2544 // 2 system error (out of memory, cannot fork, no more loop devices)
2545 // 4 internal mount bug or missing nfs support in mount
2546 // 8 user interrupt
2547 //16 problems writing or locking /etc/mtab
2548 //32 mount failure
2549 //64 some mount succeeded
2550 return rc;
2551 }
2552