mount.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  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. /* todo:
  12. * bb_getopt_ulflags();
  13. */
  14. /* Design notes: There is no spec for this. Remind me to write one.
  15. mount_main() calls singlemount() which calls mount_it_now().
  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. #include <limits.h>
  21. #include <stdlib.h>
  22. #include <unistd.h>
  23. #include <errno.h>
  24. #include <string.h>
  25. #include <stdio.h>
  26. #include <mntent.h>
  27. #include <ctype.h>
  28. #include <sys/mount.h>
  29. #include <fcntl.h> // for CONFIG_FEATURE_MOUNT_LOOP
  30. #include <sys/ioctl.h> // for CONFIG_FEATURE_MOUNT_LOOP
  31. #include "busybox.h"
  32. // These two aren't always defined in old headers
  33. #ifndef MS_BIND
  34. #define MS_BIND 4096
  35. #endif
  36. #ifndef MS_MOVE
  37. #define MS_MOVE 8192
  38. #endif
  39. #ifndef MS_SILENT
  40. #define MS_SILENT 32768
  41. #endif
  42. // Not real flags, but we want to be able to check for this.
  43. #define MOUNT_NOAUTO (1<<29)
  44. #define MOUNT_SWAP (1<<30)
  45. /* Standard mount options (from -o options or --options), with corresponding
  46. * flags */
  47. struct {
  48. const char *name;
  49. long flags;
  50. } static const mount_options[] = {
  51. // NOP flags.
  52. {"loop", 0},
  53. {"defaults", 0},
  54. {"quiet", 0},
  55. // vfs flags
  56. {"ro", MS_RDONLY},
  57. {"rw", ~MS_RDONLY},
  58. {"nosuid", MS_NOSUID},
  59. {"suid", ~MS_NOSUID},
  60. {"dev", ~MS_NODEV},
  61. {"nodev", MS_NODEV},
  62. {"exec", ~MS_NOEXEC},
  63. {"noexec", MS_NOEXEC},
  64. {"sync", MS_SYNCHRONOUS},
  65. {"async", ~MS_SYNCHRONOUS},
  66. {"atime", ~MS_NOATIME},
  67. {"noatime", MS_NOATIME},
  68. {"diratime", ~MS_NODIRATIME},
  69. {"nodiratime", MS_NODIRATIME},
  70. {"loud", ~MS_SILENT},
  71. // action flags
  72. {"remount", MS_REMOUNT},
  73. {"bind", MS_BIND},
  74. {"move", MS_MOVE},
  75. {"noauto",MOUNT_NOAUTO},
  76. {"swap",MOUNT_SWAP}
  77. };
  78. /* Append mount options to string */
  79. static void append_mount_options(char **oldopts, char *newopts)
  80. {
  81. if(*oldopts && **oldopts) {
  82. char *temp=bb_xasprintf("%s,%s",*oldopts,newopts);
  83. free(*oldopts);
  84. *oldopts=temp;
  85. } else {
  86. if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
  87. *oldopts = bb_xstrdup(newopts);
  88. }
  89. }
  90. /* Use the mount_options list to parse options into flags.
  91. * Return list of unrecognized options in *strflags if strflags!=NULL */
  92. static int parse_mount_options(char *options, char **unrecognized)
  93. {
  94. int flags = MS_SILENT;
  95. // Loop through options
  96. for (;;) {
  97. int i;
  98. char *comma = strchr(options, ',');
  99. if (comma) *comma = 0;
  100. // Find this option in mount_options
  101. for (i = 0; i < (sizeof(mount_options) / sizeof(*mount_options)); i++) {
  102. if (!strcasecmp(mount_options[i].name, options)) {
  103. long fl = mount_options[i].flags;
  104. if(fl < 0) flags &= fl;
  105. else flags |= fl;
  106. break;
  107. }
  108. }
  109. // If unrecognized not NULL, append unrecognized mount options */
  110. if (unrecognized
  111. && i == (sizeof(mount_options) / sizeof(*mount_options)))
  112. {
  113. // Add it to strflags, to pass on to kernel
  114. i = *unrecognized ? strlen(*unrecognized) : 0;
  115. *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2);
  116. // Comma separated if it's not the first one
  117. if (i) (*unrecognized)[i++] = ',';
  118. strcpy((*unrecognized)+i, options);
  119. }
  120. // Advance to next option, or finish
  121. if(comma) {
  122. *comma = ',';
  123. options = ++comma;
  124. } else break;
  125. }
  126. return flags;
  127. }
  128. // Return a list of all block device backed filesystems
  129. static llist_t *get_block_backed_filesystems(void)
  130. {
  131. char *fs, *buf,
  132. *filesystems[] = {"/etc/filesystems", "/proc/filesystems", 0};
  133. llist_t *list = 0;
  134. int i;
  135. FILE *f;
  136. for(i = 0; filesystems[i]; i++) {
  137. if(!(f = fopen(filesystems[i], "r"))) continue;
  138. for(fs = buf = 0; (fs = buf = bb_get_chomped_line_from_file(f));
  139. free(buf))
  140. {
  141. if(!strncmp(buf,"nodev",5) && isspace(buf[5])) continue;
  142. while(isspace(*fs)) fs++;
  143. if(*fs=='#' || *fs=='*') continue;
  144. if(!*fs) continue;
  145. list=llist_add_to_end(list,bb_xstrdup(fs));
  146. }
  147. if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
  148. }
  149. return list;
  150. }
  151. llist_t *fslist = 0;
  152. #if ENABLE_FEATURE_CLEAN_UP
  153. static void delete_block_backed_filesystems(void)
  154. {
  155. llist_free(fslist);
  156. }
  157. #else
  158. void delete_block_backed_filesystems(void);
  159. #endif
  160. #if ENABLE_FEATURE_MTAB_SUPPORT
  161. static int useMtab;
  162. static int fakeIt;
  163. #else
  164. #define useMtab 0
  165. #define fakeIt 0
  166. #endif
  167. // Perform actual mount of specific filesystem at specific location.
  168. static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
  169. {
  170. int rc;
  171. if (fakeIt) { return 0; }
  172. // Mount, with fallback to read-only if necessary.
  173. for(;;) {
  174. rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
  175. vfsflags, filteropts);
  176. if(!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
  177. break;
  178. bb_error_msg("%s is write-protected, mounting read-only",
  179. mp->mnt_fsname);
  180. vfsflags |= MS_RDONLY;
  181. }
  182. // Abort entirely if permission denied.
  183. if (rc && errno == EPERM)
  184. bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
  185. /* If the mount was successful, and we're maintaining an old-style
  186. * mtab file by hand, add the new entry to it now. */
  187. if(ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc) {
  188. FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
  189. int i;
  190. if(!mountTable)
  191. bb_error_msg("No %s\n",bb_path_mtab_file);
  192. // Add vfs string flags
  193. for(i=0; mount_options[i].flags != MS_REMOUNT; i++)
  194. if (mount_options[i].flags > 0)
  195. append_mount_options(&(mp->mnt_opts),
  196. // Shut up about the darn const. It's not important. I don't care.
  197. (char *)mount_options[i].name);
  198. // Remove trailing / (if any) from directory we mounted on
  199. i = strlen(mp->mnt_dir);
  200. if(i>1 && mp->mnt_dir[i-1] == '/') mp->mnt_dir[i-1] = 0;
  201. // Write and close.
  202. if(!mp->mnt_type || !*mp->mnt_type) mp->mnt_type="--bind";
  203. addmntent(mountTable, mp);
  204. endmntent(mountTable);
  205. if (ENABLE_FEATURE_CLEAN_UP)
  206. if(strcmp(mp->mnt_type,"--bind")) mp->mnt_type = 0;
  207. }
  208. return rc;
  209. }
  210. // Mount one directory. Handles NFS, loopback, autobind, and filesystem type
  211. // detection. Returns 0 for success, nonzero for failure.
  212. static int singlemount(struct mntent *mp)
  213. {
  214. int rc = 1, vfsflags;
  215. char *loopFile = 0, *filteropts = 0;
  216. llist_t *fl = 0;
  217. struct stat st;
  218. vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
  219. // Treat fstype "auto" as unspecified.
  220. if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0;
  221. // Might this be an NFS filesystem?
  222. if (ENABLE_FEATURE_MOUNT_NFS &&
  223. (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) &&
  224. strchr(mp->mnt_fsname, ':') != NULL)
  225. {
  226. if (nfsmount(mp->mnt_fsname, mp->mnt_dir, &vfsflags, &filteropts, 1)) {
  227. bb_perror_msg("nfsmount failed");
  228. return 1;
  229. } else {
  230. // Strangely enough, nfsmount() doesn't actually mount() anything.
  231. mp->mnt_type = "nfs";
  232. rc = mount_it_now(mp, vfsflags, filteropts);
  233. if (ENABLE_FEATURE_CLEAN_UP) free(filteropts);
  234. return rc;
  235. }
  236. }
  237. // Look at the file. (Not found isn't a failure for remount, or for
  238. // a synthetic filesystem like proc or sysfs.)
  239. if (lstat(mp->mnt_fsname, &st));
  240. else if (!(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
  241. // Do we need to allocate a loopback device for it?
  242. if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
  243. loopFile = bb_simplify_path(mp->mnt_fsname);
  244. mp->mnt_fsname = 0;
  245. switch(set_loop(&(mp->mnt_fsname), loopFile, 0)) {
  246. case 0:
  247. case 1:
  248. break;
  249. default:
  250. bb_error_msg( errno == EPERM || errno == EACCES
  251. ? bb_msg_perm_denied_are_you_root
  252. : "Couldn't setup loop device");
  253. return errno;
  254. }
  255. // Autodetect bind mounts
  256. } else if (S_ISDIR(st.st_mode) && !mp->mnt_type) vfsflags |= MS_BIND;
  257. }
  258. /* If we know the fstype (or don't need to), jump straight
  259. * to the actual mount. */
  260. if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
  261. rc = mount_it_now(mp, vfsflags, filteropts);
  262. // Loop through filesystem types until mount succeeds or we run out
  263. else {
  264. /* Initialize list of block backed filesystems. This has to be
  265. * done here so that during "mount -a", mounts after /proc shows up
  266. * can autodetect. */
  267. if (!fslist) {
  268. fslist = get_block_backed_filesystems();
  269. if (ENABLE_FEATURE_CLEAN_UP && fslist)
  270. atexit(delete_block_backed_filesystems);
  271. }
  272. for (fl = fslist; fl; fl = fl->link) {
  273. mp->mnt_type = fl->data;
  274. if (!(rc = mount_it_now(mp,vfsflags, filteropts))) break;
  275. mp->mnt_type = 0;
  276. }
  277. }
  278. if (ENABLE_FEATURE_CLEAN_UP) free(filteropts);
  279. // If mount failed, clean up loop file (if any).
  280. if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
  281. del_loop(mp->mnt_fsname);
  282. if (ENABLE_FEATURE_CLEAN_UP) {
  283. free(loopFile);
  284. free(mp->mnt_fsname);
  285. }
  286. }
  287. return rc;
  288. }
  289. // Parse options, if necessary parse fstab/mtab, and call singlemount for
  290. // each directory to be mounted.
  291. int mount_main(int argc, char **argv)
  292. {
  293. char *cmdopts = bb_xstrdup(""), *fstabname, *fstype=0, *storage_path=0;
  294. FILE *fstab;
  295. int i, opt, all = FALSE, rc = 1;
  296. struct mntent mtpair[2], *mtcur = mtpair;
  297. /* parse long options, like --bind and --move. Note that -o option
  298. * and --option are synonymous. Yes, this means --remount,rw works. */
  299. for (i = opt = 0; i < argc; i++) {
  300. if (argv[i][0] == '-' && argv[i][1] == '-') {
  301. append_mount_options(&cmdopts,argv[i]+2);
  302. } else argv[opt++] = argv[i];
  303. }
  304. argc = opt;
  305. // Parse remaining options
  306. while ((opt = getopt(argc, argv, "o:t:rwavnf")) > 0) {
  307. switch (opt) {
  308. case 'o':
  309. append_mount_options(&cmdopts, optarg);
  310. break;
  311. case 't':
  312. fstype = optarg;
  313. break;
  314. case 'r':
  315. append_mount_options(&cmdopts, "ro");
  316. break;
  317. case 'w':
  318. append_mount_options(&cmdopts, "rw");
  319. break;
  320. case 'a':
  321. all = TRUE;
  322. break;
  323. case 'n':
  324. USE_FEATURE_MTAB_SUPPORT(useMtab = FALSE;)
  325. break;
  326. case 'f':
  327. USE_FEATURE_MTAB_SUPPORT(fakeIt = FALSE;)
  328. break;
  329. case 'v':
  330. break; // ignore -v
  331. default:
  332. bb_show_usage();
  333. }
  334. }
  335. // Three or more non-option arguments? Die with a usage message.
  336. if (optind-argc>2) bb_show_usage();
  337. // If we have no arguments, show currently mounted filesystems
  338. if (optind == argc) {
  339. if (!all) {
  340. FILE *mountTable = setmntent(bb_path_mtab_file, "r");
  341. if(!mountTable) bb_error_msg_and_die("No %s",bb_path_mtab_file);
  342. while (getmntent_r(mountTable,mtpair,bb_common_bufsiz1,
  343. sizeof(bb_common_bufsiz1)))
  344. {
  345. // Don't show rootfs.
  346. if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
  347. if (!fstype || !strcmp(mtpair->mnt_type, fstype))
  348. printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
  349. mtpair->mnt_dir, mtpair->mnt_type,
  350. mtpair->mnt_opts);
  351. }
  352. if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
  353. return EXIT_SUCCESS;
  354. }
  355. }
  356. // When we have two arguments, the second is the directory and we can
  357. // skip looking at fstab entirely. We can always abspath() the directory
  358. // argument when we get it.
  359. if (optind+2 == argc) {
  360. mtpair->mnt_fsname = argv[optind];
  361. mtpair->mnt_dir = argv[optind+1];
  362. mtpair->mnt_type = fstype;
  363. mtpair->mnt_opts = cmdopts;
  364. rc = singlemount(mtpair);
  365. goto clean_up;
  366. }
  367. // If we have at least one argument, it's the storage location
  368. if (optind < argc) storage_path = bb_simplify_path(argv[optind]);
  369. // Open either fstab or mtab
  370. if (parse_mount_options(cmdopts,0) & MS_REMOUNT)
  371. fstabname = (char *)bb_path_mtab_file; // Again with the evil const.
  372. else fstabname="/etc/fstab";
  373. if (!(fstab=setmntent(fstabname,"r")))
  374. bb_perror_msg_and_die("Cannot read %s",fstabname);
  375. // Loop through entries until we find what we're looking for.
  376. memset(mtpair,0,sizeof(mtpair));
  377. for (;;) {
  378. struct mntent *mtnext = mtpair + (mtcur==mtpair ? 1 : 0);
  379. // Get next fstab entry
  380. if (!getmntent_r(fstab, mtcur, bb_common_bufsiz1
  381. + (mtcur==mtpair ? sizeof(bb_common_bufsiz1)/2 : 0),
  382. sizeof(bb_common_bufsiz1)/2))
  383. {
  384. // Were we looking for something specific?
  385. if (optind != argc) {
  386. // If we didn't find anything, complain.
  387. if (!mtnext->mnt_fsname)
  388. bb_error_msg_and_die("Can't find %s in %s",
  389. argv[optind], fstabname);
  390. // Mount the last thing we found.
  391. mtcur = mtnext;
  392. mtcur->mnt_opts=bb_xstrdup(mtcur->mnt_opts);
  393. append_mount_options(&(mtcur->mnt_opts),cmdopts);
  394. rc = singlemount(mtcur);
  395. free(mtcur->mnt_opts);
  396. }
  397. break;
  398. }
  399. /* If we're trying to mount something specific and this isn't it,
  400. * skip it. Note we must match both the exact text in fstab (ala
  401. * "proc") or a full path from root */
  402. if (optind != argc) {
  403. // Is this what we're looking for?
  404. if(strcmp(argv[optind],mtcur->mnt_fsname) &&
  405. strcmp(storage_path,mtcur->mnt_fsname) &&
  406. strcmp(argv[optind],mtcur->mnt_dir) &&
  407. strcmp(storage_path,mtcur->mnt_dir)) continue;
  408. // Remember this entry. Something later may have overmounted
  409. // it, and we want the _last_ match.
  410. mtcur = mtnext;
  411. // If we're mounting all.
  412. } else {
  413. // Do we need to match a filesystem type?
  414. if (fstype && strcmp(mtcur->mnt_type,fstype)) continue;
  415. // Skip noauto and swap anyway.
  416. if (parse_mount_options(mtcur->mnt_opts,0)
  417. & (MOUNT_NOAUTO | MOUNT_SWAP)) continue;
  418. // Mount this thing.
  419. if (singlemount(mtcur)) {
  420. // Don't whine about already mounted fs when mounting all.
  421. // Note: we should probably change return value to indicate
  422. // failure, without causing a duplicate error message.
  423. if (errno != EBUSY) bb_perror_msg("Mounting %s on %s failed",
  424. mtcur->mnt_fsname, mtcur->mnt_dir);
  425. rc = 0;
  426. }
  427. }
  428. }
  429. if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab);
  430. clean_up:
  431. if (ENABLE_FEATURE_CLEAN_UP) {
  432. free(storage_path);
  433. free(cmdopts);
  434. }
  435. if(rc)
  436. bb_perror_msg("Mounting %s on %s failed",
  437. mtcur->mnt_fsname, mtcur->mnt_dir);
  438. return rc;
  439. }