Commit message: mount: (deprecated) drop --guess-fstype

The option is undocumented and unnecessary.

Signed-off-by: Karel Zak <kzak@redhat.com>
CVE: CVE-2013-0157
CWE: CWE-200
Vulnerability description: (a) mount and (b) umount in util-linux 2.14.1, 2.17.2, and probably other versions allow local users to determine the existence of restricted directories by (1) using the --guess-fstype command-line option or (2) attempting to mount a non-existent device, which generates different error messages depending on whether the directory exists.

Project: util-linux

mount_one (const char *spec, const char *node, const char *types,
	   const char *fstabopts, char *cmdlineopts, int freq, int pass) {
	const char *nspec = NULL;
	char *opts;

	/* Substitute values in opts, if required */
	opts = usersubst(fstabopts);

	/* Merge the fstab and command line options.  */
	opts = append_opt(opts, cmdlineopts, NULL);

	if (types == NULL && !mounttype && !is_existing_file(spec)) {
		if (strchr (spec, ':') != NULL) {
			types = "nfs";
			if (verbose)
				printf(_("mount: no type was given - "
					 "I'll assume nfs because of "
					 "the colon\n"));
		} else if(!strncmp(spec, "//", 2)) {
			types = "cifs";
			if (verbose)
				printf(_("mount: no type was given - "
					 "I'll assume cifs because of "
					 "the // prefix\n"));
		}
	}

	/* Handle possible LABEL= and UUID= forms of spec */
	if (types == NULL || (strncmp(types, "9p", 2) &&
			      strncmp(types, "nfs", 3) &&
			      strncmp(types, "cifs", 4) &&
			      strncmp(types, "smbfs", 5))) {
		if (!is_pseudo_fs(types))
			nspec = spec_to_devname(spec);
		if (nspec)
			spec = nspec;
	}

	return try_mount_one (spec, node, types, opts, freq, pass, 0);
}

Project: util-linux

main(int argc, char *argv[]) {
	int c, result = 0, specseen;
	char *options = NULL, *test_opts = NULL, *node;
	const char *spec = NULL;
	char *label = NULL;
	char *uuid = NULL;
	char *types = NULL;
	char *p;
	struct mntentchn *mc;
	int fd;

	sanitize_env();
	setlocale(LC_ALL, "");
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);
	atexit(close_stdout);

	progname = argv[0];
	if ((p = strrchr(progname, '/')) != NULL)
		progname = p+1;

	umask(022);

	/* People report that a mount called from init without console
	   writes error messages to /etc/mtab
	   Let us try to avoid getting fd's 0,1,2 */
	while((fd = open("/dev/null", O_RDWR)) == 0 || fd == 1 || fd == 2) ;
	if (fd > 2)
		close(fd);

	fsprobe_init();

#ifdef DO_PS_FIDDLING
	initproctitle(argc, argv);
#endif

	while ((c = getopt_long (argc, argv, "aBfFhilL:Mno:O:p:rRsU:vVwt:",
				 longopts, NULL)) != -1) {
		switch (c) {
		case 'a':	       /* mount everything in fstab */
			++mount_all;
			break;
		case 'B': /* bind */
			mounttype = MS_BIND;
			break;
		case 'f':	       /* fake: don't actually call mount(2) */
			++fake;
			break;
		case 'F':
			++optfork;
			break;
		case 'h':		/* help */
			usage (stdout, 0);
			break;
		case 'i':
			external_allowed = 0;
			break;
		case 'l':
			list_with_volumelabel = 1;
			break;
		case 'L':
			label = optarg;
			break;
		case 'M': /* move */
			mounttype = MS_MOVE;
			break;
		case 'n':		/* do not write /etc/mtab */
			++nomtab;
			break;
		case 'o':		/* specify mount options */
			options = append_opt(options, optarg, NULL);
			break;
		case 'O':		/* with -t: mount only if (not) opt */
			test_opts = append_opt(test_opts, optarg, NULL);
			break;
		case 'p':		/* fd on which to read passwd */
                        error("mount: %s", _("--pass-fd is no longer supported"));
			break;
		case 'r':		/* mount readonly */
			readonly = 1;
			readwrite = 0;
			break;
		case 'R': /* rbind */
			mounttype = (MS_BIND | MS_REC);
			break;
		case 's':		/* allow sloppy mount options */
			sloppy = 1;
			break;
		case 't':		/* specify file system types */
			types = optarg;
			break;
		case 'U':
			uuid = optarg;
			break;
		case 'v':		/* be chatty - more so if repeated */
			++verbose;
			break;
		case 'V':		/* version */
			print_version(EXIT_SUCCESS);
			break;
		case 'w':		/* mount read/write */
			readwrite = 1;
			readonly = 0;
			break;
		case 0:
			break;

		case 136:
			mounttype = MS_SHARED;
			break;

		case 137:
			mounttype = MS_SLAVE;
			break;

		case 138:
			mounttype = MS_PRIVATE;
			break;

		case 139:
			mounttype = MS_UNBINDABLE;
			break;

		case 140:
			mounttype = (MS_SHARED | MS_REC);
			break;

		case 141:
			mounttype = (MS_SLAVE | MS_REC);
			break;

		case 142:
			mounttype = (MS_PRIVATE | MS_REC);
			break;

		case 143:
			mounttype = (MS_UNBINDABLE | MS_REC);
			break;
		case 144:
			nocanonicalize = 1;
			break;
		case '?':
		default:
			usage (stderr, EX_USAGE);
		}
	}

	if (verbose > 2) {
		printf("mount: fstab path: \"%s\"\n", _PATH_MNTTAB);
		printf("mount: mtab path:  \"%s\"\n", _PATH_MOUNTED);
		printf("mount: lock path:  \"%s\"\n", _PATH_MOUNTED_LOCK);
		printf("mount: temp path:  \"%s\"\n", _PATH_MOUNTED_TMP);
		printf("mount: UID:        %u\n", getuid());
		printf("mount: eUID:       %u\n", geteuid());
	}

	argc -= optind;
	argv += optind;

	specseen = (uuid || label) ? 1 : 0;	/* yes, .. i know */

	if (argc+specseen == 0 && !mount_all) {
		if (options || mounttype)
			usage (stderr, EX_USAGE);
		return print_all (types);
	}

	{
		const uid_t ruid = getuid();
		const uid_t euid = geteuid();

		/* if we're really root and aren't running setuid */
		if (((uid_t)0 == ruid) && (ruid == euid)) {
			restricted = 0;
		}

		if (restricted &&
		    (types || options || readwrite || nomtab || mount_all ||
		     nocanonicalize || fake || mounttype ||
		     (argc + specseen) != 1)) {

			if (ruid == 0 && euid != 0)
				/* user is root, but setuid to non-root */
				die (EX_USAGE, _("mount: only root can do that "
					"(effective UID is %u)"), euid);

			die (EX_USAGE, _("mount: only root can do that"));
		}
	}

	atexit(unlock_mtab);

	switch (argc+specseen) {
	case 0:
		/* mount -a */
		result = do_mount_all (types, options, test_opts);
		if (result == 0 && verbose && !fake)
			error(_("nothing was mounted"));
		break;

	case 1:
		/* mount [-nfrvw] [-o options] special | node
		 * mount -L label  (or -U uuid)
		 * (/etc/fstab is necessary)
		 */
		if (types != NULL)
			usage (stderr, EX_USAGE);

		if (uuid || label)
			mc = getfs(NULL, uuid, label);
		else
			mc = getfs(*argv, NULL, NULL);

		if (!mc) {
			if (uuid || label)
				die (EX_USAGE, _("mount: no such partition found"));

			die (EX_USAGE,
			     _("mount: can't find %s in %s or %s"),
			     *argv, _PATH_MNTTAB, _PATH_MOUNTED);
		}

		result = mount_one (xstrdup (mc->m.mnt_fsname),
				    xstrdup (mc->m.mnt_dir),
				    xstrdup (mc->m.mnt_type),
				    mc->m.mnt_opts, options, 0, 0);
		break;

	case 2:
		/* mount special node  (/etc/fstab is not necessary) */
		if (specseen) {
			/* mount -L label node   (or -U uuid) */
			spec = uuid ?	fsprobe_get_devname_by_uuid(uuid) :
					fsprobe_get_devname_by_label(label);
			node = argv[0];
		} else {
			/* mount special node */
			spec = argv[0];
			node = argv[1];
		}
		if (!spec)
			die (EX_USAGE, _("mount: no such partition found"));

		result = mount_one (spec, node, types, NULL, options, 0, 0);
		break;

	default:
		usage (stderr, EX_USAGE);
	}

	if (result == EX_SOMEOK)
		result = 0;

	fsprobe_exit();

	exit (result);
}

Project: util-linux

create_mtab (void) {
	struct mntentchn *fstab;
	struct my_mntent mnt;
	int flags;
	mntFILE *mfp;

	lock_mtab();

	mfp = my_setmntent (_PATH_MOUNTED, "a+");
	if (mfp == NULL || mfp->mntent_fp == NULL) {
		int errsv = errno;
		die (EX_FILEIO, _("mount: can't open %s for writing: %s"),
		     _PATH_MOUNTED, strerror (errsv));
	}

	/* Find the root entry by looking it up in fstab */
	if ((fstab = getfs_by_dir ("/")) || (fstab = getfs_by_dir ("root"))) {
		char *extra_opts;
		parse_opts (fstab->m.mnt_opts, &flags, &extra_opts);
		mnt.mnt_dir = "/";
		mnt.mnt_fsname = spec_to_devname(fstab->m.mnt_fsname);
		mnt.mnt_type = fstab->m.mnt_type;
		mnt.mnt_opts = fix_opts_string (flags, extra_opts, NULL, 0);
		mnt.mnt_freq = mnt.mnt_passno = 0;
		free(extra_opts);

		if (my_addmntent (mfp, &mnt) == 1) {
			int errsv = errno;
			die (EX_FILEIO, _("mount: error writing %s: %s"),
			     _PATH_MOUNTED, strerror (errsv));
		}
	}
	if (fchmod (fileno (mfp->mntent_fp), 0644) < 0)
		if (errno != EROFS) {
			int errsv = errno;
			die (EX_FILEIO,
			     _("mount: error changing mode of %s: %s"),
			     _PATH_MOUNTED, strerror (errsv));
		}
	my_endmntent (mfp);

	unlock_mtab();

	reset_mtab_info();
}

Project: util-linux

try_mount_one (const char *spec0, const char *node0, const char *types0,
	       const char *opts0, int freq, int pass, int ro) {
  int res = 0, status = 0, special = 0;
  int mnt5_res = 0;		/* only for gcc */
  int mnt_err;
  int flags;
  char *extra_opts;		/* written in mtab */
  char *mount_opts;		/* actually used on system call */
  const char *opts, *spec, *node, *types;
  char *user = 0;
  int loop = 0;
  const char *loopdev = 0, *loopfile = 0;
  struct stat statbuf;

  /* copies for freeing on exit */
  const char *opts1, *spec1, *node1, *types1;

  if (verbose > 2) {
	  printf("mount: spec:  \"%s\"\n", spec0);
	  printf("mount: node:  \"%s\"\n", node0);
	  printf("mount: types: \"%s\"\n", types0);
	  printf("mount: opts:  \"%s\"\n", opts0);
  }

  spec = spec1 = xstrdup(spec0);
  node = node1 = xstrdup(node0);
  types = types1 = xstrdup(types0);
  opts = opts1 = xstrdup(opts0);

  parse_opts (opts, &flags, &extra_opts);
  mount_opts = xstrdup(extra_opts);

  /* quietly succeed for fstab entries that don't get mounted automatically */
  if (mount_all && (flags & MS_NOAUTO))
      goto out;

  restricted_check(spec, node, &flags, &user);

  /* The "mount -f" checks for for existing record in /etc/mtab (with
   * regular non-fake mount this is usually done by kernel)
   */
  if (!(flags & MS_REMOUNT) && fake && mounted (spec, node, NULL))
      die(EX_USAGE, _("mount: according to mtab, "
                      "%s is already mounted on %s\n"),
		      spec, node);

  if (opt_speed)
      cdrom_setspeed(spec);

  if (!(flags & MS_REMOUNT)) {
      /*
       * Don't set up a (new) loop device if we only remount - this left
       * stale assignments of files to loop devices. Nasty when used for
       * encryption.
       */
      res = loop_check(&spec, &types, &flags, &loop, &loopdev, &loopfile, node);
      if (res)
	  goto out;
  }

  if (loop)
      opt_loopdev = loopdev;

  if (flags & (MS_BIND | MS_MOVE | MS_PROPAGATION))
      types = "none";

#ifdef HAVE_LIBSELINUX
  if (flags & MS_REMOUNT) {
      /*
       * Linux kernel does not accept any selinux context option on remount
       */
      if (mount_opts) {
          char *tmp = mount_opts;
          mount_opts = remove_context_options(mount_opts);
          my_free(tmp);
      }

  } else if (types && strcmp(types, "tmpfs") == 0 && is_selinux_enabled() > 0 &&
	   !has_context_option(mount_opts)) {
      /*
       * Add rootcontext= mount option for tmpfs
       * https://bugzilla.redhat.com/show_bug.cgi?id=476964
       */
      security_context_t sc = NULL;

      if (getfilecon(node, &sc) > 0 && strcmp("unlabeled", sc))
	      append_context("rootcontext=", (char *) sc, &mount_opts);
      freecon(sc);
  }
#endif

  /*
   * Call mount.TYPE for types that require a separate mount program.
   * For the moment these types are ncpfs and smbfs. Maybe also vxfs.
   * All such special things must occur isolated in the types string.
   */
  if (check_special_mountprog(spec, node, types, flags, mount_opts, &status)) {
      res = status;
      goto out;
  }

  block_signals (SIG_BLOCK);

  if (!fake) {
    mnt5_res = guess_fstype_and_mount (spec, node, &types, flags & ~MS_NOSYS,
				       mount_opts, &special, &status);

    if (special) {
      block_signals (SIG_UNBLOCK);
      res = status;
      goto out;
    }
  }

  /* Kernel allows to use MS_RDONLY for bind mounts, but the read-only request
   * could be silently ignored. Check it to avoid 'ro' in mtab and 'rw' in
   * /proc/mounts.
   */
  if (!fake && mnt5_res == 0 &&
      (flags & MS_BIND) && (flags & MS_RDONLY) && !is_readonly(node)) {

      printf(_("mount: warning: %s seems to be mounted read-write.\n"), node);
      flags &= ~MS_RDONLY;
  }

  /* Kernel can silently add MS_RDONLY flag when mounting file system that
   * does not have write support. Check this to avoid 'ro' in /proc/mounts
   * and 'rw' in mtab.
   */
  if (!fake && mnt5_res == 0 &&
      !(flags & (MS_RDONLY | MS_PROPAGATION | MS_MOVE)) &&
      is_readonly(node)) {

      printf(_("mount: warning: %s seems to be mounted read-only.\n"), node);
      flags |= MS_RDONLY;
  }

  if (fake || mnt5_res == 0) {
      char *mo = fix_opts_string (flags & ~MS_NOMTAB, extra_opts, user, 0);
      const char *tp = types ? types : "unknown";

      /* Mount succeeded, report this (if verbose) and write mtab entry.  */
      if (!(mounttype & MS_PROPAGATION))
	      update_mtab_entry(loop ? loopfile : spec,
			node,
			tp,
			mo,
			flags,
			freq,
			pass);

      block_signals (SIG_UNBLOCK);
      free(mo);

      res = 0;
      goto out;
  }

  mnt_err = errno;

  if (loop)
	loopdev_delete(spec);

  block_signals (SIG_UNBLOCK);

  /* Mount failed, complain, but don't die.  */

  if (types == 0) {
    if (restricted)
      error (_("mount: I could not determine the filesystem type, "
	       "and none was specified"));
    else
      error (_("mount: you must specify the filesystem type"));
  } else if (mnt5_res != -1) {
      /* should not happen */
      error (_("mount: mount failed"));
  } else {
   switch (mnt_err) {
    case EPERM:
      if (geteuid() == 0) {
	   if (stat (node, &statbuf) || !S_ISDIR(statbuf.st_mode))
		error (_("mount: mount point %s is not a directory"), node);
	   else
		error (_("mount: permission denied"));
      } else
	error (_("mount: must be superuser to use mount"));
      break;
    case EBUSY:
      if (flags & MS_REMOUNT) {
	error (_("mount: %s is busy"), node);
      } else if (!strcmp(types, "proc") && !strcmp(node, "/proc")) {
	/* heuristic: if /proc/version exists, then probably proc is mounted */
	if (stat ("/proc/version", &statbuf))   /* proc mounted? */
	   error (_("mount: %s is busy"), node);   /* no */
	else if (!mount_all || verbose)            /* yes, don't mention it */
	   error (_("mount: proc already mounted"));
      } else {
	error (_("mount: %s already mounted or %s busy"), spec, node);
	already (spec, node);
      }
      break;
    case ENOENT:
      if (lstat (node, &statbuf))
	   error (_("mount: mount point %s does not exist"), node);
      else if (stat (node, &statbuf))
	   error (_("mount: mount point %s is a symbolic link to nowhere"),
		  node);
      else if (stat (spec, &statbuf)) {
	   if (opt_nofail)
		goto out;
	   error (_("mount: special device %s does not exist"), spec);
      } else {
	   errno = mnt_err;
	   perror("mount");
      }
      break;
    case ENOTDIR:
      if (stat (node, &statbuf) || ! S_ISDIR(statbuf.st_mode))
	   error (_("mount: mount point %s is not a directory"), node);
      else if (stat (spec, &statbuf) && errno == ENOTDIR) {
	   if (opt_nofail)
              goto out;
	   error (_("mount: special device %s does not exist\n"
		    "       (a path prefix is not a directory)\n"), spec);
      } else {
	   errno = mnt_err;
	   perror("mount");
      }
      break;
    case EINVAL:
    { int fd;
      unsigned long long size = 0;

      if (flags & MS_REMOUNT) {
	error (_("mount: %s not mounted or bad option"), node);
      } else {
	error (_("mount: wrong fs type, bad option, bad superblock on %s,\n"
	       "       missing codepage or helper program, or other error"),
	       spec);

	if (stat(spec, &statbuf) < 0) {
	  if (errno == ENOENT)         /* network FS? */
	    error(_(
	       "       (for several filesystems (e.g. nfs, cifs) you might\n"
	       "       need a /sbin/mount.<type> helper program)"));

	} else if (S_ISBLK(statbuf.st_mode)
	                 && (fd = open(spec, O_RDONLY | O_NONBLOCK)) >= 0) {

	  if (blkdev_get_size(fd, &size) == 0) {
	    if (size == 0 && !loop)
	      error(_(
		 "       (could this be the IDE device where you in fact use\n"
		 "       ide-scsi so that sr0 or sda or so is needed?)"));

	    if (size && size <= 2)
	      error(_(
		  "       (aren't you trying to mount an extended partition,\n"
		  "       instead of some logical partition inside?)"));

	    close(fd);
	  }
	}
	error(_(
		"       In some cases useful info is found in syslog - try\n"
		"       dmesg | tail  or so\n"));
      }
      break;
    }
    case EMFILE:
      error (_("mount table full")); break;
    case EIO:
      error (_("mount: %s: can't read superblock"), spec); break;
    case ENODEV:
    {
      int pfs = known_fstype_in_procfs(types);

      if (pfs == 1 || !strcmp(types, "guess"))
        error(_("mount: %s: unknown device"), spec);
      else if (pfs == 0) {
	char *lowtype, *p;
	int u;

	error (_("mount: unknown filesystem type '%s'"), types);

	/* maybe this loser asked for FAT or ISO9660 or isofs */
	lowtype = xstrdup(types);
	u = 0;
	for(p=lowtype; *p; p++) {
	  if(tolower(*p) != *p) {
	    *p = tolower(*p);
	    u++;
	  }
	}
	if (u && known_fstype_in_procfs(lowtype) == 1)
	  error (_("mount: probably you meant %s"), lowtype);
	else if (!strncmp(lowtype, "iso", 3) &&
			known_fstype_in_procfs("iso9660") == 1)
	  error (_("mount: maybe you meant 'iso9660'?"));
	else if (!strncmp(lowtype, "fat", 3) &&
			known_fstype_in_procfs("vfat") == 1)
	  error (_("mount: maybe you meant 'vfat'?"));
	free(lowtype);
      } else
	error (_("mount: %s has wrong device number or fs type %s not supported"),
	       spec, types);
      break;
    }
    case ENOTBLK:
      if (opt_nofail)
        goto out;
      if (stat (spec, &statbuf)) /* strange ... */
	error (_("mount: %s is not a block device, and stat fails?"), spec);
      else if (S_ISBLK(statbuf.st_mode))
        error (_("mount: the kernel does not recognize %s as a block device\n"
	       "       (maybe `modprobe driver'?)"), spec);
      else if (S_ISREG(statbuf.st_mode))
	error (_("mount: %s is not a block device (maybe try `-o loop'?)"),
		 spec);
      else
	error (_("mount: %s is not a block device"), spec);
      break;
    case ENXIO:
      if (opt_nofail)
        goto out;
      error (_("mount: %s is not a valid block device"), spec); break;
    case EACCES:  /* pre-linux 1.1.38, 1.1.41 and later */
    case EROFS:   /* linux 1.1.38 and later */
    { char *bd = (loop ? "" : _("block device "));
      if (ro || (flags & MS_RDONLY)) {
          error (_("mount: cannot mount %s%s read-only"),
		 bd, spec);
          break;
      } else if (readwrite) {
	  error (_("mount: %s%s is write-protected but explicit `-w' flag given"),
		 bd, spec);
	  break;
      } else if (flags & MS_REMOUNT) {
	  error (_("mount: cannot remount %s%s read-write, is write-protected"),
		 bd, spec);
	  break;
      } else {
	 opts = opts0;
	 types = types0;

         if (opts) {
	     char *opts2 = append_opt(xstrdup(opts), "ro", NULL);
	     my_free(opts1);
	     opts = opts1 = opts2;
         } else
             opts = "ro";
	 if (types && !strcmp(types, "guess"))
	     types = 0;
         error (_("mount: %s%s is write-protected, mounting read-only"),
		bd, spec0);
	 res = try_mount_one (spec0, node0, types, opts, freq, pass, 1);
	 goto out;
      }
      break;
    }
    case ENOMEDIUM:
      error(_("mount: no medium found on %s"), spec);
      break;
    default:
      error ("mount: %s", strerror (mnt_err)); break;
    }
  }
  res = EX_FAIL;

 out:

#if defined(HAVE_LIBSELINUX) && defined(HAVE_SECURITY_GET_INITIAL_CONTEXT)
  if (res != EX_FAIL && verbose && is_selinux_enabled() > 0) {
      security_context_t raw = NULL, def = NULL;

      if (getfilecon(node, &raw) > 0 &&
		     security_get_initial_context("file", &def) == 0) {

	  if (!selinux_file_context_cmp(raw, def))
	      printf(_("mount: %s does not contain SELinux labels.\n"
                   "       You just mounted an file system that supports labels which does not\n"
                   "       contain labels, onto an SELinux box. It is likely that confined\n"
                   "       applications will generate AVC messages and not be allowed access to\n"
                   "       this file system.  For more details see restorecon(8) and mount(8).\n"),
                   node);
      }
      freecon(raw);
      freecon(def);
  }
#endif

  my_free(mount_opts);
  my_free(extra_opts);
  my_free(spec1);
  my_free(node1);
  my_free(opts1);
  my_free(types1);

  return res;
}

Project: util-linux

static char *remove_context_options(char *opts)
{
	char *begin = NULL, *end = NULL, *p;
	int open_quote = 0, changed = 0;

	if (!opts)
		return NULL;

	opts = xstrdup(opts);

	for (p = opts; p && *p; p++) {
		if (!begin)
			begin = p;		/* begin of the option item */
		if (*p == '"')
			open_quote ^= 1;	/* reverse the status */
		if (open_quote)
			continue;		/* still in quoted block */
		if (*p == ',')
			end = p;		/* terminate the option item */
		else if (*(p + 1) == '\0')
			end = p + 1;		/* end of optstr */
		if (!begin || !end)
			continue;

		if (strncmp(begin, "context=", 8) == 0 ||
		    strncmp(begin, "fscontext=", 10) == 0 ||
		    strncmp(begin, "defcontext=", 11) == 0 ||
	            strncmp(begin, "rootcontext=", 12) == 0 ||
		    strncmp(begin, "seclabel", 8) == 0) {
			size_t sz;

			if ((begin == opts || *(begin - 1) == ',') && *end == ',')
				end++;
			sz = strlen(end);

			memmove(begin, end, sz + 1);
			if (!*begin && *(begin - 1) == ',')
				*(begin - 1) = '\0';

			p = begin;
			changed = 1;
		}
		begin = end = NULL;
	}

	if (changed && verbose)
		printf (_("mount: SELinux *context= options are ignore on remount.\n"));

	return opts;
}

Project: util-linux

do_mount (struct mountargs *args, int *special, int *status) {
	int ret;
	if (check_special_mountprog(args->spec, args->node, args->type,
	                            args->flags, args->data, status)) {
		*special = 1;
		ret = 0;
	} else {
		ret = do_mount_syscall(args);
	}
	if (ret == 0)
		mountcount++;
	return ret;
}

Project: util-linux

loop_check(const char **spec, const char **type, int *flags,
	   int *loop, const char **loopdev, const char **loopfile,
	   const char *node) {
  int looptype;
  uintmax_t offset = 0, sizelimit = 0;
  struct loopdev_cxt lc;
  char *pwd = NULL;
  int ret = EX_FAIL;

  /*
   * In the case of a loop mount, either type is of the form lo@/dev/loop5
   * or the option "-o loop=/dev/loop5" or just "-o loop" is given, or
   * mount just has to figure things out for itself from the fact that
   * spec is not a block device. We do not test for a block device
   * immediately: maybe later other types of mountable objects will occur.
   */

  *loopdev = opt_loopdev;

  looptype = (*type && strncmp("lo@", *type, 3) == 0);
  if (looptype) {
    if (*loopdev)
      error(_("mount: loop device specified twice"));
    *loopdev = *type + 3;
    *type = opt_vfstype;
  } else if (opt_vfstype) {
    if (*type)
      error(_("mount: type specified twice"));
    else
      *type = opt_vfstype;
  }

  *loop = ((*flags & MS_LOOP) || *loopdev || opt_offset || opt_sizelimit);
  *loopfile = *spec;

  /* Automatically create a loop device from a regular file if a filesystem
   * is not specified or the filesystem is known for libblkid (these
   * filesystems work with block devices only).
   *
   * Note that there is not a restriction (on kernel side) that prevents regular
   * file as a mount(2) source argument. A filesystem that is able to mount
   * regular files could be implemented.
   */
  if (!*loop && !(*flags & (MS_BIND | MS_MOVE | MS_PROPAGATION)) &&
      (!*type || strcmp(*type, "auto") == 0 || fsprobe_known_fstype(*type))) {

    struct stat st;
    if (stat(*loopfile, &st) == 0)
      *loop = S_ISREG(st.st_mode);
  }

  if (*loop) {
    *flags |= MS_LOOP;
    if (fake) {
      if (verbose)
	printf(_("mount: skipping the setup of a loop device\n"));
    } else {
      int loop_opts = 0;

      /* since 2.6.37 we don't have to store backing filename to mtab
       * because kernel provides the name in /sys
       */
      if (get_linux_version() >= KERNEL_VERSION(2, 6, 37) ||
	  mtab_is_writable() == 0) {

	if (verbose)
	  printf(_("mount: enabling autoclear loopdev flag\n"));
	loop_opts = LO_FLAGS_AUTOCLEAR;
      }

      if (*flags & MS_RDONLY)
        loop_opts |= LO_FLAGS_READ_ONLY;

      if (opt_offset && parse_offset(&opt_offset, &offset)) {
        error(_("mount: invalid offset '%s' specified"), opt_offset);
        return EX_FAIL;
      }
      if (opt_sizelimit && parse_offset(&opt_sizelimit, &sizelimit)) {
        error(_("mount: invalid sizelimit '%s' specified"), opt_sizelimit);
        return EX_FAIL;
      }

      if (is_mounted_same_loopfile(node, *loopfile, offset)) {
        error(_("mount: according to mtab %s is already mounted on %s as loop"), *loopfile, node);
        return EX_FAIL;
      }

      if (opt_encryption) {
        error("mount: %s", _("encryption not supported, use cryptsetup(8) instead"));
        return EX_FAIL;
      }

      loopcxt_init(&lc, 0);
      /*loopcxt_enable_debug(&lc, 1);*/

      if (*loopdev && **loopdev)
	loopcxt_set_device(&lc, *loopdev);	/* use loop=<devname> */

      do {
	int rc;

        if ((!*loopdev || !**loopdev) && loopcxt_find_unused(&lc) == 0)
	    *loopdev = loopcxt_strdup_device(&lc);

	if (!*loopdev) {
	  error(_("mount: failed to found free loop device"));
	  loopcxt_deinit(&lc);
	  goto err;	/* no more loop devices */
	}
	if (verbose)
	  printf(_("mount: going to use the loop device %s\n"), *loopdev);

	rc = loopcxt_set_backing_file(&lc, *loopfile);

	if (!rc && offset)
	  rc = loopcxt_set_offset(&lc, offset);
	if (!rc && sizelimit)
	  rc = loopcxt_set_sizelimit(&lc, sizelimit);
	if (!rc)
	  loopcxt_set_flags(&lc, loop_opts);

	if (rc) {
	   error(_("mount: %s: failed to set loopdev attributes"), *loopdev);
	   loopcxt_deinit(&lc);
	   goto err;
	}

	/* setup the device */
	rc = loopcxt_setup_device(&lc);
	if (!rc)
	  break;	/* success */

	if (rc != -EBUSY) {
	  error(_("mount: %s: failed setting up loop device: %m"), *loopfile);
	  if (!opt_loopdev) {
	    my_free(*loopdev);
	    *loopdev = NULL;
	  }
	  loopcxt_deinit(&lc);
	  goto err;
	}

	if (!opt_loopdev) {
	  if (verbose)
	    printf(_("mount: stolen loop=%s ...trying again\n"), *loopdev);
	    my_free(*loopdev);
	    *loopdev = NULL;
	    continue;
	}
	error(_("mount: stolen loop=%s"), *loopdev);
	loopcxt_deinit(&lc);
	goto err;

      } while (!*loopdev);

      if (verbose > 1)
	printf(_("mount: setup loop device successfully\n"));
      *spec = *loopdev;

      if (loopcxt_is_readonly(&lc))
        *flags |= MS_RDONLY;

      if (loopcxt_is_autoclear(&lc))
        /* Prevent recording loop dev in mtab for cleanup on umount */
        *loop = 0;

      /* We have to keep the device open until mount(2), otherwise it will
       * be auto-cleared by kernel (because LO_FLAGS_AUTOCLEAR) */
      loopcxt_set_fd(&lc, -1, 0);
      loopcxt_deinit(&lc);
    }
  }

  ret = 0;
err:
  if (pwd) {
    char *p = pwd;
    while (*p)
      *p++ = '\0';
    free(pwd);
  }
  return ret;
}

Project: util-linux

fix_opts_string (int flags, const char *extra_opts,
		 const char *user, int inv_user)
{
	const struct opt_map *om;
	const struct string_opt_map *m;
	char *new_opts;

	new_opts = append_opt(NULL, (flags & MS_RDONLY) ? "ro" : "rw", NULL);
	for (om = opt_map; om->opt != NULL; om++) {
		if (om->skip)
			continue;
		if (om->inv || !om->mask || (flags & om->mask) != om->mask)
			continue;
		if (om->mask == MS_COMMENT && !(comment_flags & om->cmask))
			continue;
		new_opts = append_opt(new_opts, om->opt, NULL);
		flags &= ~om->mask;
	}
	for (m = &string_opt_map[0]; m->tag; m++) {
		if (!m->skip && *(m->valptr))
			new_opts = append_opt(new_opts, m->tag, *(m->valptr));
	}
	if (extra_opts && *extra_opts)
		new_opts = append_opt(new_opts, extra_opts, NULL);

	if (user)
		new_opts = append_opt(new_opts, "user=", user);

	if (inv_user) {
		for (om = opt_map; om->opt != NULL; om++) {
			if (om->mask && om->inv
			    && (inv_user & om->mask) == om->mask) {
				new_opts = append_opt(new_opts, om->opt, NULL);
				inv_user &= ~om->mask;
			}
		}
	}

	return new_opts;
}

Project: util-linux

clear_flags_opts(void) {
	invuser_flags = 0;
	comment_flags = 0;
	opt_nofail = 0;
}

Project: util-linux

do_mount_all (char *types, char *options, char *test_opts) {
	struct mntentchn *mc, *mc0, *mtmp;
	int status = 0;
	struct stat statbuf;
	struct child {
		pid_t pid;
		char *group;
		struct mntentchn *mec;
		struct mntentchn *meclast;
		struct child *nxt;
	} childhead, *childtail, *cp;
	char major[22];
	char *g, *colon;

	/* build a chain of what we have to do, or maybe
	   several chains, one for each major or NFS host */
	childhead.nxt = 0;
	childtail = &childhead;
	mc0 = fstab_head();
	for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
		if (has_noauto (mc->m.mnt_opts))
			continue;
		if (matching_type (mc->m.mnt_type, types)
		    && matching_opts (mc->m.mnt_opts, test_opts)
		    && !streq (mc->m.mnt_dir, "/")
		    && !streq (mc->m.mnt_dir, "root")
		    && !is_fstab_entry_mounted(mc, verbose)) {

			mtmp = (struct mntentchn *) xmalloc(sizeof(*mtmp));
			*mtmp = *mc;
			mtmp->nxt = 0;
			g = NULL;
			if (optfork) {
				if (stat(mc->m.mnt_fsname, &statbuf) == 0 &&
				    S_ISBLK(statbuf.st_mode)) {
					sprintf(major, "#%x",
						DISKMAJOR(statbuf.st_rdev));
					g = major;
				}
				if (strcmp(mc->m.mnt_type, "nfs") == 0) {
					g = xstrdup(mc->m.mnt_fsname);
					colon = strchr(g, ':');
					if (colon)
						*colon = '\0';
				}
			}
			if (g) {
				for (cp = childhead.nxt; cp; cp = cp->nxt)
					if (cp->group &&
					    strcmp(cp->group, g) == 0) {
						cp->meclast->nxt = mtmp;
						cp->meclast = mtmp;
						goto fnd;
					}
			}
			cp = (struct child *) xmalloc(sizeof *cp);
			cp->nxt = 0;
			cp->mec = cp->meclast = mtmp;
			cp->group = xstrdup(g);
			cp->pid = 0;
			childtail->nxt = cp;
			childtail = cp;
		fnd:;

		}
	}

	/* now do everything */
	for (cp = childhead.nxt; cp; cp = cp->nxt) {
		pid_t p = -1;
		if (optfork) {
			p = fork();
			if (p == -1) {
				int errsv = errno;
				error(_("mount: cannot fork: %s"),
				      strerror (errsv));
			}
			else if (p != 0)
				cp->pid = p;
		}

		/* if child, or not forked, do the mounting */
		if (p == 0 || p == -1) {
			for (mc = cp->mec; mc; mc = mc->nxt) {
				status |= mount_one (mc->m.mnt_fsname,
						     mc->m.mnt_dir,
						     mc->m.mnt_type,
						     mc->m.mnt_opts,
						     options, 0, 0);
			}
			if (mountcount)
				status |= EX_SOMEOK;
			if (p == 0)
				exit(status);
		}
	}

	/* wait for children, if any */
	while ((cp = childhead.nxt) != NULL) {
		childhead.nxt = cp->nxt;
		if (cp->pid) {
			int ret;
		keep_waiting:
			if(waitpid(cp->pid, &ret, 0) == -1) {
				if (errno == EINTR)
					goto keep_waiting;
				perror("waitpid");
			} else if (WIFEXITED(ret))
				status |= WEXITSTATUS(ret);
			else
				status |= EX_SYSERR;
		}
	}
	if (mountcount)
		status |= EX_SOMEOK;
	return status;
}

Project: util-linux

is_mounted_same_loopfile(const char *node0, const char *loopfile, unsigned long long offset)
{
	struct mntentchn *mnt = NULL;
	char *node;
	int res = 0;

	node = canonicalize(node0);

	/* Search for mountpoint node in mtab,
	 * procceed if any of these has the loop option set or
	 * the device is a loop device
	 */
	mnt = getmntdirbackward(node, mnt);
	if (!mnt) {
		free(node);
		return 0;
	}
	for(; mnt && res == 0; mnt = getmntdirbackward(node, mnt)) {
		char *p;

		if (strncmp(mnt->m.mnt_fsname, "/dev/loop", 9) == 0)
			res = loopdev_is_used((char *) mnt->m.mnt_fsname,
					loopfile, offset, LOOPDEV_FL_OFFSET);

		else if (mnt->m.mnt_opts &&
			 (p = strstr(mnt->m.mnt_opts, "loop=")))
		{
			char *dev = xstrdup(p+5);
			if ((p = strchr(dev, ',')))
				*p = '\0';
			res =  loopdev_is_used(dev,
					loopfile, offset, LOOPDEV_FL_OFFSET);
			free(dev);
		}
	}

	free(node);
	return res;
}

Project: util-linux

parse_opts (const char *options, int *flags, char **extra_opts) {
	*flags = 0;
	*extra_opts = NULL;

	clear_string_opts();
	clear_flags_opts();

	if (options != NULL) {
		char *opts = xstrdup(options);
		int open_quote = 0;
		char *opt, *p;

		for (p=opts, opt=NULL; p && *p; p++) {
			if (!opt)
				opt = p;		/* begin of the option item */
			if (*p == '"')
				open_quote ^= 1;	/* reverse the status */
			if (open_quote)
				continue;		/* still in quoted block */
			if (*p == ',')
				*p = '\0';		/* terminate the option item */
			/* end of option item or last item */
			if (*p == '\0' || *(p+1) == '\0') {
				if (!parse_string_opt(opt))
					parse_opt(opt, flags, &invuser_flags, extra_opts);
				opt = NULL;
			}
		}
		free(opts);
	}

	if (readonly)
		*flags |= MS_RDONLY;
	if (readwrite)
		*flags &= ~MS_RDONLY;

	*flags |= mounttype;

	/* The propagation flags should not be used together with any
	 * other flags (except MS_REC and MS_SILENT) */
	if (*flags & MS_PROPAGATION)
		*flags &= (MS_PROPAGATION | MS_REC | MS_SILENT);
}

Project: util-linux

print_one (const struct my_mntent *me) {

	char *fsname = NULL;

	if (mount_quiet)
		return;

	/* users assume backing file name rather than /dev/loopN in
	 * mount(8) output if the device has been initialized by mount(8).
	 */
	if (strncmp(me->mnt_fsname, "/dev/loop", 9) == 0 &&
	    loopdev_is_autoclear(me->mnt_fsname))
		fsname = loopdev_get_backing_file(me->mnt_fsname);

	if (!fsname)
		fsname = (char *) me->mnt_fsname;

	printf ("%s on %s", fsname, me->mnt_dir);
	if (me->mnt_type != NULL && *(me->mnt_type) != '\0')
		printf (" type %s", me->mnt_type);
	if (me->mnt_opts != NULL)
		printf (" (%s)", me->mnt_opts);
	if (list_with_volumelabel && is_pseudo_fs(me->mnt_type) == 0) {
		const char *devname = spec_to_devname(me->mnt_fsname);

		if (devname) {
			const char *label;

			label = fsprobe_get_label_by_devname(devname);
			my_free(devname);

			if (label) {
				printf (" [%s]", label);
				my_free(label);
			}
		}
	}
	printf ("\n");
}

Project: util-linux

parse_opt(char *opt, int *mask, int *inv_user, char **extra_opts) {
	const struct opt_map *om;

	for (om = opt_map; om->opt != NULL; om++)
		if (streq (opt, om->opt)) {
			if (om->inv)
				*mask &= ~om->mask;
			else
				*mask |= om->mask;
			if (om->inv && ((*mask & MS_USER) || (*mask & MS_USERS))
			    && (om->mask & MS_SECURE))
				*inv_user |= om->mask;
			if ((om->mask == MS_USER || om->mask == MS_USERS)
			    && !om->inv)
				*mask |= MS_SECURE;
			if ((om->mask == MS_OWNER || om->mask == MS_GROUP)
			    && !om->inv)
				*mask |= MS_OWNERSECURE;
#ifdef MS_SILENT
			if (om->mask == MS_SILENT && om->inv)  {
				mount_quiet = 1;
				verbose = 0;
			}
#endif
			if (om->mask == MS_COMMENT) {
				comment_flags |= om->cmask;
				if (om->cmask == MS_COMMENT_NOFAIL)
					opt_nofail = 1;
			}
			return;
		}

	/* convert nonnumeric ids to numeric */
	if (!strncmp(opt, "uid=", 4) && !isdigit(opt[4])) {
		struct passwd *pw = getpwnam(opt+4);

		if (pw) {
			*extra_opts = append_numopt(*extra_opts,
						"uid=", pw->pw_uid);
			return;
		}
	}
	if (!strncmp(opt, "gid=", 4) && !isdigit(opt[4])) {
		struct group *gr = getgrnam(opt+4);

		if (gr) {
			*extra_opts = append_numopt(*extra_opts,
						"gid=", gr->gr_gid);
			return;
		}
	}

#ifdef HAVE_LIBSELINUX
	if (strncmp(opt, "context=", 8) == 0 && *(opt+8)) {
		if (append_context("context=", opt+8, extra_opts) == 0)
			return;
	}
	if (strncmp(opt, "fscontext=", 10) == 0 && *(opt+10)) {
		if (append_context("fscontext=", opt+10, extra_opts) == 0)
			return;
	}
	if (strncmp(opt, "defcontext=", 11) == 0 && *(opt+11)) {
		if (append_context("defcontext=", opt+11, extra_opts) == 0)
			return;
	}
	if (strncmp(opt, "rootcontext=", 12) == 0 && *(opt+12)) {
		if (append_context("rootcontext=", opt+12, extra_opts) == 0)
			return;
	}
#endif
	*extra_opts = append_opt(*extra_opts, opt, NULL);
}

Project: util-linux

is_fstab_entry_mounted(struct mntentchn *mc, int verbose)
{
	struct stat st;

	if (mounted(mc->m.mnt_fsname, mc->m.mnt_dir, mc))
		goto yes;

	/* extra care for loop devices */
	if ((mc->m.mnt_opts && strstr(mc->m.mnt_opts, "loop=")) ||
	    (stat(mc->m.mnt_fsname, &st) == 0 && S_ISREG(st.st_mode))) {

		char *p = get_option_value(mc->m.mnt_opts, "offset=");
		uintmax_t offset = 0;

		if (p && strtosize(p, &offset) != 0) {
			if (verbose)
				printf(_("mount: ignore %s "
					"(unparsable offset= option)\n"),
					mc->m.mnt_fsname);
			return -1;
		}
		free(p);
		if (is_mounted_same_loopfile(mc->m.mnt_dir, mc->m.mnt_fsname, offset))
			goto yes;
	}

	return 0;
yes:
	if (verbose)
		printf(_("mount: %s already mounted on %s\n"),
			       mc->m.mnt_fsname, mc->m.mnt_dir);
	return 1;
}

Project: util-linux

mounted (const char *spec0, const char *node0,
	 struct mntentchn *fstab_mc __attribute__((__unused__))) {

	struct mntentchn *mc, *mc0;
	const char *spec, *node;
	int ret = 0;

	/* Handle possible UUID= and LABEL= in spec */
	spec = spec_to_devname(spec0);
	if (!spec)
		return ret;

	node = canonicalize(node0);


	mc0 = mtab_head();
	for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
		if (streq (spec, mc->m.mnt_fsname) &&
		    streq (node, mc->m.mnt_dir)) {
			ret = 1;
			break;
		}

	my_free(spec);
	my_free(node);

	return ret;
}

Project: util-linux

getfs(const char *spec, const char *uuid, const char *label)
{
	struct mntentchn *mc = NULL;
	const char *devname = NULL;

	if (!spec && !uuid && !label)
		return NULL;

	/*
	 * A) 99% of all cases, the spec on cmdline matches
	 *    with spec in fstab
	 */
	if (uuid)
		mc = getfs_by_uuid(uuid);
	else if (label)
		mc = getfs_by_label(label);
	else {
		mc = getfs_by_dir(spec);

		if (!mc)
			mc = getfs_by_spec(spec);
	}
	if (mc)
		return mc;

	/*
	 * B) UUID or LABEL on cmdline, but devname in fstab
	 */
	if (uuid)
		devname = fsprobe_get_devname_by_uuid(uuid);
	else if (label)
		devname = fsprobe_get_devname_by_label(label);
	else
		devname = spec_to_devname(spec);

	if (devname)
		mc = getfs_by_devname(devname);

	/*
	 * C) mixed
	 */
	if (!mc && devname) {
		const char *id = NULL;

		if (!label && (!spec || strncmp(spec, "LABEL=", 6))) {
			id = fsprobe_get_label_by_devname(devname);
			if (id)
				mc = getfs_by_label(id);
		}
		if (!mc && !uuid && (!spec || strncmp(spec, "UUID=", 5))) {
			id = fsprobe_get_uuid_by_devname(devname);
			if (id)
				mc = getfs_by_uuid(id);
		}
		my_free(id);

		if (mc) {
			/* use real device name to avoid repetitional
			 * conversion from LABEL/UUID to devname
			 */
			my_free(mc->m.mnt_fsname);
			mc->m.mnt_fsname = xstrdup(devname);
		}
	}

	/*
	 * D) remount -- try /etc/mtab
	 *    Earlier mtab was tried first, but this would sometimes try the
	 *    wrong mount in case mtab had the root device entry wrong.  Try
	 *    the last occurrence first, since that is the visible mount.
	 */
	if (!mc && (devname || spec))
		mc = getmntfilebackward (devname ? devname : spec, NULL);

	my_free(devname);
	return mc;
}

Project: util-linux

cdrom_setspeed(const char *spec) {
#define CDROM_SELECT_SPEED      0x5322  /* Set the CD-ROM speed */
	if (opt_speed) {
		int cdrom;
		int speed = atoi(opt_speed);

		if ((cdrom = open(spec, O_RDONLY | O_NONBLOCK)) < 0)
			die(EX_FAIL,
			    _("mount: cannot open %s for setting speed"),
			    spec);
		if (ioctl(cdrom, CDROM_SELECT_SPEED, speed) < 0)
			die(EX_FAIL, _("mount: cannot set speed: %m"));
		close(cdrom);
	}
}

Project: util-linux

check_special_mountprog(const char *spec, const char *node, const char *type, int flags,
			char *extra_opts, int *status) {
	char search_path[] = FS_SEARCH_PATH;
	char *path, mountprog[150];
	struct stat statbuf;
	int res;

	if (!external_allowed)
		return 0;

	if (type == NULL || strcmp(type, "none") == 0)
		return 0;

	path = strtok(search_path, ":");
	while (path) {
		int type_opt = 0;

		res = snprintf(mountprog, sizeof(mountprog), "%s/mount.%s",
			       path, type);
		path = strtok(NULL, ":");
		if (res < 0 || (size_t) res >= sizeof(mountprog))
			continue;

		res = stat(mountprog, &statbuf);
		if (res == -1 && errno == ENOENT && strchr(type, '.')) {
			/* If type ends with ".subtype" try without it */
			*strrchr(mountprog, '.') = '\0';
			type_opt = 1;
			res = stat(mountprog, &statbuf);
		}
		if (res)
			continue;

		if (verbose)
			fflush(stdout);

		switch (fork()) {
		case 0: { /* child */
			char *oo, *mountargs[12];
			int i = 0;

			if (setgid(getgid()) < 0)
				die(EX_FAIL, _("mount: cannot set group id: %m"));

			if (setuid(getuid()) < 0)
				die(EX_FAIL, _("mount: cannot set user id: %m"));

			oo = fix_opts_string(flags, extra_opts, NULL, invuser_flags);
			mountargs[i++] = mountprog;			/* 1 */
			mountargs[i++] = (char *) spec;			/* 2 */
			mountargs[i++] = (char *) node;			/* 3 */
			if (sloppy && strncmp(type, "nfs", 3) == 0)
				mountargs[i++] = "-s";			/* 4 */
			if (fake)
				mountargs[i++] = "-f";			/* 5 */
			if (nomtab)
				mountargs[i++] = "-n";			/* 6 */
			if (verbose)
				mountargs[i++] = "-v";			/* 7 */
			if (oo && *oo) {
				mountargs[i++] = "-o";			/* 8 */
				mountargs[i++] = oo;			/* 9 */
			}
			if (type_opt) {
				mountargs[i++] = "-t";			/* 10 */
				mountargs[i++] = (char *) type;		/* 11 */
			}
			mountargs[i] = NULL;				/* 12 */

			if (verbose > 2) {
				i = 0;
				while (mountargs[i]) {
					printf("mount: external mount: argv[%d] = \"%s\"\n",
						i, mountargs[i]);
					i++;
				}
				fflush(stdout);
			}

			execv(mountprog, mountargs);
			exit(1);	/* exec failed */
		}

		default: { /* parent */
			int st;
			wait(&st);
			*status = (WIFEXITED(st) ? WEXITSTATUS(st) : EX_SYSERR);
			return 1;
		}

		case -1: { /* error */
			int errsv = errno;
			error(_("mount: cannot fork: %s"), strerror(errsv));
		}
		}
	}

	return 0;
}