Пример #1
0
/* Check, if there already exists a mounted loop device on the mountpoint node
 * with the same parameters.
 */
static int is_mounted_same_loopfile(struct libmnt_context *cxt,
				    const char *target,
				    const char *backing_file,
				    uint64_t offset)
{
	struct libmnt_table *tb;
	struct libmnt_iter itr;
	struct libmnt_fs *fs;
	struct libmnt_cache *cache;

	assert(cxt);
	assert(cxt->fs);
	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));

	if (!target || !backing_file || mnt_context_get_mtab(cxt, &tb))
		return 0;

	DBG(CXT, mnt_debug_h(cxt, "checking if %s mounted on %s",
				backing_file, target));

	cache = mnt_context_get_cache(cxt);
	mnt_reset_iter(&itr, MNT_ITER_BACKWARD);

	/* Search for mountpoint node in mtab, procceed if any of these has the
	 * loop option set or the device is a loop device
	 */
	while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
		const char *src = mnt_fs_get_source(fs);
		const char *opts = mnt_fs_get_user_options(fs);
		char *val;
		size_t len;
		int res = 0;

		if (!src || !mnt_fs_match_target(fs, target, cache))
			continue;

		if (strncmp(src, "/dev/loop", 9) == 0) {
			res = loopdev_is_used((char *) src, backing_file,
					offset, LOOPDEV_FL_OFFSET);

		} else if (opts && (cxt->user_mountflags & MNT_MS_LOOP) &&
		    mnt_optstr_get_option(opts, "loop", &val, &len) == 0 && val) {

			val = strndup(val, len);
			res = loopdev_is_used((char *) val, backing_file,
					offset, LOOPDEV_FL_OFFSET);
			free(val);
		}

		if (res) {
			DBG(CXT, mnt_debug_h(cxt, "%s already mounted", backing_file));
			return 1;
		}
	}

	return 0;
}
Пример #2
0
/* Check if there is something important in the utab file. The parsed utab is
 * stored in context->utab and deallocated by mnt_free_context().
 *
 * This function exists to avoid (if possible) /proc/self/mountinfo usage, so
 * don't use thigs like mnt_resolve_target(), mnt_context_get_mtab() etc here.
 * See lookup_umount_fs() for more details.
 */
static int has_utab_entry(struct libmnt_context *cxt, const char *target)
{
	struct libmnt_cache *cache = NULL;
	struct libmnt_fs *fs;
	struct libmnt_iter itr;
	char *cn = NULL;
	int rc = 0;

	assert(cxt);

	if (!cxt->utab) {
		const char *path = mnt_get_utab_path();

		if (!path || is_file_empty(path))
			return 0;
		cxt->utab = mnt_new_table();
		if (!cxt->utab)
			return 0;
		cxt->utab->fmt = MNT_FMT_UTAB;
		if (mnt_table_parse_file(cxt->utab, path))
			return 0;
	}

	/* paths in utab are canonicalized */
	cache = mnt_context_get_cache(cxt);
	cn = mnt_resolve_path(target, cache);
	mnt_reset_iter(&itr, MNT_ITER_BACKWARD);

	while (mnt_table_next_fs(cxt->utab, &itr, &fs) == 0) {
		if (mnt_fs_streq_target(fs, cn)) {
			rc = 1;
			break;
		}
	}

	if (!cache)
		free(cn);

	return rc;
}
Пример #3
0
static int lookup_umount_fs(struct libmnt_context *cxt)
{
	int rc, loopdev = 0;
	const char *tgt;
	struct libmnt_table *mtab = NULL;
	struct libmnt_fs *fs;
	struct libmnt_cache *cache = NULL;
	char *cn_tgt = NULL;

	assert(cxt);
	assert(cxt->fs);

	DBG(CXT, mnt_debug_h(cxt, "umount: lookup FS"));

	tgt = mnt_fs_get_target(cxt->fs);
	if (!tgt) {
		DBG(CXT, mnt_debug_h(cxt, "umount: undefined target"));
		return -EINVAL;
	}

	/*
	 * The mtab file maybe huge and on systems with utab we have to merge
	 * userspace mount options into /proc/self/mountinfo. This all is
	 * expensive. The mtab filter allows to filter out entries, then
	 * mtab and utab are very tiny files.
	 *
	 * *but*... the filter uses mnt_fs_streq_{target,srcpath} functions
	 * where LABEL, UUID or symlinks are to canonicalized. It means that
	 * it's usable only for canonicalized stuff (e.g. kernel mountinfo).
	 */
	if (!cxt->mtab_writable	&& *tgt == '/' &&
	    !mnt_context_is_force(cxt) && !mnt_context_is_lazy(cxt)) {

		struct stat st;

		if (stat(tgt, &st) == 0 && S_ISDIR(st.st_mode)) {
			/* we'll canonicalized /proc/self/mountinfo */
			cache = mnt_context_get_cache(cxt);
			cn_tgt = mnt_resolve_path(tgt, cache);
			if (cn_tgt)
				mnt_context_set_tabfilter(cxt, mtab_filter, cn_tgt);
		}
	}
	rc = mnt_context_get_mtab(cxt, &mtab);

	if (cn_tgt) {
		mnt_context_set_tabfilter(cxt, NULL, NULL);
		if (!cache)
			free(cn_tgt);
	}

	if (rc) {
		DBG(CXT, mnt_debug_h(cxt, "umount: failed to read mtab"));
		return rc;
	}

try_loopdev:
	fs = mnt_table_find_target(mtab, tgt, MNT_ITER_BACKWARD);
	if (!fs && mnt_context_is_swapmatch(cxt)) {
		/*
		 * Maybe the option is source rather than target (sometimes
		 * people use e.g. "umount /dev/sda1")
		 */
		fs = mnt_table_find_source(mtab, tgt, MNT_ITER_BACKWARD);

		if (fs) {
			struct libmnt_fs *fs1 = mnt_table_find_target(mtab,
							mnt_fs_get_target(fs),
							MNT_ITER_BACKWARD);
			if (!fs1) {
				DBG(CXT, mnt_debug_h(cxt, "mtab is broken?!?!"));
				return -EINVAL;
			}
			if (fs != fs1) {
				/* Something was stacked over `file' on the
				 * same mount point. */
				DBG(CXT, mnt_debug_h(cxt,
						"umount: %s: %s is mounted "
						"over it on the same point",
						tgt, mnt_fs_get_source(fs1)));
				return -EINVAL;
			}
		}
	}

	if (!fs && !loopdev && mnt_context_is_swapmatch(cxt)) {
		/*
		 * Maybe the option is /path/file.img, try to convert to /dev/loopN
		 */
		struct stat st;

		if (stat(tgt, &st) == 0 && S_ISREG(st.st_mode)) {
			char *dev = NULL;
			int count = loopdev_count_by_backing_file(tgt, &dev);

			if (count == 1) {
				DBG(CXT, mnt_debug_h(cxt,
					"umount: %s --> %s (retry)", tgt, dev));
				mnt_fs_set_source(cxt->fs, tgt);
				mnt_fs_set_target(cxt->fs, dev);
				free(dev);
				tgt = mnt_fs_get_target(cxt->fs);

				loopdev = 1;		/* to avoid endless loop */
				goto try_loopdev;

			} else if (count > 1)
				DBG(CXT, mnt_debug_h(cxt,
					"umount: warning: %s is associated "
					"with more than one loopdev", tgt));
		}
	}

	if (!fs) {
		DBG(CXT, mnt_debug_h(cxt, "umount: cannot find %s in mtab", tgt));
		return 0;
	}

	if (fs != cxt->fs) {
		/* copy from mtab to our FS description
		 */
		mnt_fs_set_source(cxt->fs, NULL);
		mnt_fs_set_target(cxt->fs, NULL);

		if (!mnt_copy_fs(cxt->fs, fs)) {
			DBG(CXT, mnt_debug_h(cxt, "umount: failed to copy FS"));
			return -errno;
		}
		DBG(CXT, mnt_debug_h(cxt, "umount: mtab applied"));
	}

	cxt->flags |= MNT_FL_TAB_APPLIED;
	return rc;
}
Пример #4
0
/**
 * mnt_context_find_umount_fs:
 * @cxt: mount context
 * @tgt: mountpoint, device, ...
 * @pfs: returns point to filesystem
 *
 * Returns: 0 on success, <0 on error, 1 if target filesystem not found
 */
int mnt_context_find_umount_fs(struct libmnt_context *cxt,
			       const char *tgt,
			       struct libmnt_fs **pfs)
{
	int rc;
	struct libmnt_table *mtab = NULL;
	struct libmnt_fs *fs;
	char *loopdev = NULL;

	if (pfs)
		*pfs = NULL;

	if (!cxt || !tgt || !pfs)
		return -EINVAL;

	DBG(CXT, ul_debugobj(cxt, "umount: lookup FS for '%s'", tgt));

	if (!*tgt)
		return 1; /* empty string is not an error */

	/*
	 * The mount table may be huge, and on systems with utab we have to merge
	 * userspace mount options into /proc/self/mountinfo. This all is
	 * expensive. The tab filter allows to filter out entries, then
	 * a mount table and utab are very tiny files.
	 *
	 * *but*... the filter uses mnt_fs_streq_{target,srcpath} functions
	 * where LABEL, UUID or symlinks are canonicalized. It means that
	 * it's usable only for canonicalized stuff (e.g. kernel mountinfo).
	 */
	if (!mnt_context_mtab_writable(cxt) && *tgt == '/' &&
	    !mnt_context_is_force(cxt) && !mnt_context_is_lazy(cxt))
		rc = mnt_context_get_mtab_for_target(cxt, &mtab, tgt);
	else
		rc = mnt_context_get_mtab(cxt, &mtab);

	if (rc) {
		DBG(CXT, ul_debugobj(cxt, "umount: failed to read mtab"));
		return rc;
	}

	if (mnt_table_get_nents(mtab) == 0) {
		DBG(CXT, ul_debugobj(cxt, "umount: mtab empty"));
		return 1;
	}

try_loopdev:
	fs = mnt_table_find_target(mtab, tgt, MNT_ITER_BACKWARD);
	if (!fs && mnt_context_is_swapmatch(cxt)) {
		/*
		 * Maybe the option is source rather than target (sometimes
		 * people use e.g. "umount /dev/sda1")
		 */
		fs = mnt_table_find_source(mtab, tgt, MNT_ITER_BACKWARD);

		if (fs) {
			struct libmnt_fs *fs1 = mnt_table_find_target(mtab,
							mnt_fs_get_target(fs),
							MNT_ITER_BACKWARD);
			if (!fs1) {
				DBG(CXT, ul_debugobj(cxt, "mtab is broken?!?!"));
				rc = -EINVAL;
				goto err;
			}
			if (fs != fs1) {
				/* Something was stacked over `file' on the
				 * same mount point. */
				DBG(CXT, ul_debugobj(cxt,
						"umount: %s: %s is mounted "
						"over it on the same point",
						tgt, mnt_fs_get_source(fs1)));
				rc = -EINVAL;
				goto err;
			}
		}
	}

	if (!fs && !loopdev && mnt_context_is_swapmatch(cxt)) {
		/*
		 * Maybe the option is /path/file.img, try to convert to /dev/loopN
		 */
		struct stat st;

		if (mnt_stat_mountpoint(tgt, &st) == 0 && S_ISREG(st.st_mode)) {
			int count;
			struct libmnt_cache *cache = mnt_context_get_cache(cxt);
			const char *bf = cache ? mnt_resolve_path(tgt, cache) : tgt;

			count = loopdev_count_by_backing_file(bf, &loopdev);
			if (count == 1) {
				DBG(CXT, ul_debugobj(cxt,
					"umount: %s --> %s (retry)", tgt, loopdev));
				tgt = loopdev;
				goto try_loopdev;

			} else if (count > 1)
				DBG(CXT, ul_debugobj(cxt,
					"umount: warning: %s is associated "
					"with more than one loopdev", tgt));
		}
	}

	if (pfs)
		*pfs = fs;
	free(loopdev);

	DBG(CXT, ul_debugobj(cxt, "umount fs: %s", fs ? mnt_fs_get_target(fs) :
							"<not found>"));
	return fs ? 0 : 1;
err:
	free(loopdev);
	return rc;
}
Пример #5
0
/*
 * Note that cxt->fs contains relevant mtab entry!
 */
static int evaluate_permissions(struct libmnt_context *cxt)
{
	struct libmnt_table *fstab;
	unsigned long u_flags = 0;
	const char *tgt, *src, *optstr;
	int rc, ok = 0;
	struct libmnt_fs *fs;

	assert(cxt);
	assert(cxt->fs);
	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));

	if (!mnt_context_is_restricted(cxt))
		 return 0;		/* superuser mount */

	DBG(CXT, ul_debugobj(cxt, "umount: evaluating permissions"));

	if (!mnt_context_tab_applied(cxt)) {
		DBG(CXT, ul_debugobj(cxt,
				"cannot find %s in mtab and you are not root",
				mnt_fs_get_target(cxt->fs)));
		goto eperm;
	}

	if (cxt->user_mountflags & MNT_MS_UHELPER) {
		/* on uhelper= mount option based helper */
		rc = prepare_helper_from_options(cxt, "uhelper");
		if (rc)
			return rc;
		if (cxt->helper)
			return 0;	/* we'll call /sbin/umount.<uhelper> */
	}

	/*
	 * User mounts have to be in /etc/fstab
	 */
	rc = mnt_context_get_fstab(cxt, &fstab);
	if (rc)
		return rc;

	tgt = mnt_fs_get_target(cxt->fs);
	src = mnt_fs_get_source(cxt->fs);

	if (mnt_fs_get_bindsrc(cxt->fs)) {
		src = mnt_fs_get_bindsrc(cxt->fs);
		DBG(CXT, ul_debugobj(cxt,
				"umount: using bind source: %s", src));
	}

	/* If fstab contains the two lines
	 *	/dev/sda1 /mnt/zip auto user,noauto  0 0
	 *	/dev/sda4 /mnt/zip auto user,noauto  0 0
	 * then "mount /dev/sda4" followed by "umount /mnt/zip" used to fail.
	 * So, we must not look for the file, but for the pair (dev,file) in fstab.
	  */
	fs = mnt_table_find_pair(fstab, src, tgt, MNT_ITER_FORWARD);
	if (!fs) {
		/*
		 * It's possible that there is /path/file.img in fstab and
		 * /dev/loop0 in mtab -- then we have to check the relation
		 * between loopdev and the file.
		 */
		fs = mnt_table_find_target(fstab, tgt, MNT_ITER_FORWARD);
		if (fs) {
			struct libmnt_cache *cache = mnt_context_get_cache(cxt);
			const char *sp = mnt_fs_get_srcpath(cxt->fs);		/* devname from mtab */
			const char *dev = sp && cache ? mnt_resolve_path(sp, cache) : sp;

			if (!dev || !is_associated_fs(dev, fs))
				fs = NULL;
		}
		if (!fs) {
			DBG(CXT, ul_debugobj(cxt,
					"umount %s: mtab disagrees with fstab",
					tgt));
			goto eperm;
		}
	}

	/*
	 * User mounting and unmounting is allowed only if fstab contains one
	 * of the options `user', `users' or `owner' or `group'.
	 *
	 * The option `users' allows arbitrary users to mount and unmount -
	 * this may be a security risk.
	 *
	 * The options `user', `owner' and `group' only allow unmounting by the
	 * user that mounted (visible in mtab).
	 */
	optstr = mnt_fs_get_user_options(fs);	/* FSTAB mount options! */
	if (!optstr)
		goto eperm;

	if (mnt_optstr_get_flags(optstr, &u_flags,
				mnt_get_builtin_optmap(MNT_USERSPACE_MAP)))
		goto eperm;

	if (u_flags & MNT_MS_USERS) {
		DBG(CXT, ul_debugobj(cxt,
			"umount: promiscuous setting ('users') in fstab"));
		return 0;
	}
	/*
	 * Check user=<username> setting from mtab if there is a user, owner or
	 * group option in /etc/fstab
	 */
	if (u_flags & (MNT_MS_USER | MNT_MS_OWNER | MNT_MS_GROUP)) {

		char *curr_user = NULL;
		char *mtab_user = NULL;
		size_t sz;

		DBG(CXT, ul_debugobj(cxt,
				"umount: checking user=<username> from mtab"));

		curr_user = mnt_get_username(getuid());

		if (!curr_user) {
			DBG(CXT, ul_debugobj(cxt, "umount %s: cannot "
				"convert %d to username", tgt, getuid()));
			goto eperm;
		}

		/* get options from mtab */
		optstr = mnt_fs_get_user_options(cxt->fs);
		if (optstr && !mnt_optstr_get_option(optstr,
					"user", &mtab_user, &sz) && sz)
			ok = !strncmp(curr_user, mtab_user, sz);

		free(curr_user);
	}

	if (ok) {
		DBG(CXT, ul_debugobj(cxt, "umount %s is allowed", tgt));
		return 0;
	}
eperm:
	DBG(CXT, ul_debugobj(cxt, "umount is not allowed for you"));
	return -EPERM;
}
Пример #6
0
/*
 * this has to be called before fix_optstr()
 */
static int evaluate_permissions(struct libmnt_context *cxt)
{
	unsigned long u_flags = 0;

	assert(cxt);
	assert(cxt->fs);
	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));

	if (!cxt)
		return -EINVAL;
	if (!cxt->fs)
		return 0;

	DBG(CXT, mnt_debug_h(cxt, "mount: evaluating permissions"));

	mnt_context_get_user_mflags(cxt, &u_flags);

	if (!mnt_context_is_restricted(cxt)) {
		/*
		 * superuser mount
		 */
		cxt->user_mountflags &= ~MNT_MS_OWNER;
		cxt->user_mountflags &= ~MNT_MS_GROUP;
		cxt->user_mountflags &= ~MNT_MS_USER;
		cxt->user_mountflags &= ~MNT_MS_USERS;
	} else {
		/*
		 * user mount
		 */
		if (!(cxt->flags & MNT_FL_TAB_APPLIED))
		{
			DBG(CXT, mnt_debug_h(cxt, "perms: fstab not applied, ignore user mount"));
			return -EPERM;
		}

		/*
		 * Note that MS_OWNERSECURE and MS_SECURE mount options
		 * are applied by mnt_optstr_get_flags() from mnt_context_merge_mflags()
		 */


		/*
		 * MS_OWNER: Allow owners to mount when fstab contains the
		 * owner option.  Note that this should never be used in a high
		 * security environment, but may be useful to give people at
		 * the console the possibility of mounting a floppy.  MS_GROUP:
		 * Allow members of device group to mount. (Martin Dickopp)
		 */
		if (u_flags & (MNT_MS_OWNER | MNT_MS_GROUP)) {
			struct stat sb;
			struct libmnt_cache *cache = NULL;
			char *xsrc = NULL;
			const char *srcpath = mnt_fs_get_srcpath(cxt->fs);

			if (!srcpath) {					/* Ah... source is TAG */
				cache = mnt_context_get_cache(cxt);
				xsrc = mnt_resolve_spec(
						mnt_context_get_source(cxt),
						cache);
				srcpath = xsrc;
			}
			if (!srcpath) {
				DBG(CXT, mnt_debug_h(cxt, "perms: src undefined"));
				return -EPERM;
			}

			if (strncmp(srcpath, "/dev/", 5) == 0 &&
			    stat(srcpath, &sb) == 0 &&
			    (((u_flags & MNT_MS_OWNER) && getuid() == sb.st_uid) ||
			     ((u_flags & MNT_MS_GROUP) && mnt_in_group(sb.st_gid))))

				cxt->user_mountflags |= MNT_MS_USER;

			if (!cache)
				free(xsrc);
		}

		if (!(cxt->user_mountflags & (MNT_MS_USER | MNT_MS_USERS))) {
			DBG(CXT, mnt_debug_h(cxt, "permissions evaluation ends with -EPERMS"));
			return -EPERM;
		}
	}

	return 0;
}
Пример #7
0
static int mount_main(struct libmnt_context *cxt, int argc, char **argv)
{
    int rc, c;
    struct libmnt_fs *fs;
    char *spec = NULL, *mount_point = NULL, *opts = NULL;

    static const struct option longopts[] = {
        { "fake", 0, 0, 'f' },
        { "help", 0, 0, 'h' },
        { "no-mtab", 0, 0, 'n' },
        { "read-only", 0, 0, 'r' },
        { "ro", 0, 0, 'r' },
        { "verbose", 0, 0, 'v' },
        { "version", 0, 0, 'V' },
        { "read-write", 0, 0, 'w' },
        { "rw", 0, 0, 'w' },
        { "options", 1, 0, 'o' },
        { "sloppy", 0, 0, 's' },
        { NULL, 0, 0, 0 }
    };

    mount_config_init(progname);
    mnt_context_init_helper(cxt, MNT_ACT_MOUNT, 0);

    while ((c = getopt_long(argc, argv, "fhnrVvwo:s", longopts, NULL)) != -1) {

        rc = mnt_context_helper_setopt(cxt, c, optarg);
        if (rc == 0)		/* valid option */
            continue;
        if (rc < 0)		/* error (probably ENOMEM) */
            goto err;
        /* rc==1 means unknow option */
        switch (c) {
        case 'V':
            printf("%s: ("PACKAGE_STRING")\n", progname);
            return EX_SUCCESS;
        case 'h':
        default:
            mount_usage();
            return EX_USAGE;
        }
    }

    if (optind < argc)
        spec = argv[optind++];
    if (optind < argc)
        mount_point = argv[optind++];

    if (!mount_point) {
        nfs_error(_("%s: no mount point provided"), progname);
        goto err;
    }
    if (!spec) {
        nfs_error(_("%s: no mount spec provided"), progname);
        goto err;
    }

    if (geteuid() != 0) {
        nfs_error(_("%s: not installed setuid - "
                    "\"user\" NFS mounts not supported."), progname);
        goto err;
    }

    verbose = mnt_context_is_verbose(cxt);
    sloppy = mnt_context_is_sloppy(cxt);
    nomtab = mnt_context_is_nomtab(cxt);

    if (strcmp(progname, "mount.nfs4") == 0)
        mnt_context_set_fstype(cxt, "nfs4");
    else
        mnt_context_set_fstype(cxt, "nfs");	/* default */

    rc = mnt_context_set_source(cxt, spec);
    if (!rc)
        mnt_context_set_target(cxt, mount_point);
    if (rc) {
        nfs_error(_("%s: failed to set spec or mountpoint: %s"),
                  progname, strerror(errno));
        goto err;
    }

    mount_point = mnt_resolve_path(mount_point,
                                   mnt_context_get_cache(cxt));

    if (chk_mountpoint(mount_point))
        goto err;
    /*
     * Concatenate mount options from the configuration file
     */
    fs = mnt_context_get_fs(cxt);
    if (fs) {
        opts = mnt_fs_strdup_options(fs);

        opts = mount_config_opts(spec, mount_point, opts);
        mnt_fs_set_options(fs, opts);
    }

    rc = mnt_context_prepare_mount(cxt);
    if (rc) {
        nfs_error(_("%s: failed to prepare mount: %s\n"),
                  progname, strerror(-rc));
        goto err;
    }

    rc = try_mount(cxt, FOREGROUND);

    if (rc == EX_BG) {
        printf(_("%s: backgrounding \"%s\"\n"),
               progname, mnt_context_get_source(cxt));
        printf(_("%s: mount options: \"%s\"\n"),
               progname, opts);

        fflush(stdout);

        if (daemon(0, 0)) {
            nfs_error(_("%s: failed to start "
                        "background process: %s\n"),
                      progname, strerror(errno));
            exit(EX_FAIL);
        }

        rc = try_mount(cxt, BACKGROUND);

        if (verbose && rc)
            printf(_("%s: giving up \"%s\"\n"),
                   progname, mnt_context_get_source(cxt));
    }

    mnt_context_set_syscall_status(cxt, rc == EX_SUCCESS ? 0 : -1);
    mnt_context_finalize_mount(cxt);	/* mtab update */
    return rc;
err:
    return EX_FAIL;
}