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