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