Beispiel #1
0
/**
 * mnt_context_do_mount
 * @cxt: context
 *
 * Call mount(2) or mount.type helper. Unnecessary for mnt_context_mount().
 *
 * Note that this function could be called only once. If you want to mount
 * another source or target than you have to call mnt_reset_context().
 *
 * If you want to call mount(2) for the same source and target with a diffrent
 * mount flags or fstype then call mnt_context_reset_status() and then try
 * again mnt_context_do_mount().
 *
 * WARNING: non-zero return code does not mean that mount(2) syscall or
 *          mount.type helper wasn't successfully called.
 *
 *          Check mnt_context_get_status() after error!
*
 * Returns: 0 on success;
 *         >0 in case of mount(2) error (returns syscall errno),
 *         <0 in case of other errors.
 */
int mnt_context_do_mount(struct libmnt_context *cxt)
{
	const char *type;
	int res;

	assert(cxt);
	assert(cxt->fs);
	assert(cxt->helper_exec_status == 1);
	assert(cxt->syscall_status == 1);
	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
	assert((cxt->flags & MNT_FL_PREPARED));
	assert((cxt->action == MNT_ACT_MOUNT));

	DBG(CXT, mnt_debug_h(cxt, "mount: do mount"));

	if (!(cxt->flags & MNT_FL_MOUNTDATA))
		cxt->mountdata = (char *) mnt_fs_get_fs_options(cxt->fs);

	type = mnt_fs_get_fstype(cxt->fs);
	if (type)
		res = do_mount(cxt, NULL);
	else
		res = do_mount_by_pattern(cxt, cxt->fstype_pattern);

	if (mnt_context_get_status(cxt)
	    && !mnt_context_is_fake(cxt)
	    && !cxt->helper) {
		/*
		 * Mounted by mount(2), do some post-mount checks
		 *
		 * 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 ((cxt->mountflags & MS_BIND)
		    && (cxt->mountflags & MS_RDONLY)
		    && !mnt_is_readonly(mnt_context_get_target(cxt)))

			mnt_context_set_mflags(cxt,
					cxt->mountflags & ~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 (!(cxt->mountflags & (MS_RDONLY | MS_PROPAGATION | MS_MOVE))
		    && mnt_is_readonly(mnt_context_get_target(cxt)))

			mnt_context_set_mflags(cxt,
					cxt->mountflags | MS_RDONLY);
	}

	return res;
}
Beispiel #2
0
/* returns: error = -1, success = 0 , unknown = 1 */
static int is_vers4(struct libmnt_context *cxt)
{
    struct libmnt_fs *fs = mnt_context_get_fs(cxt);
    struct libmnt_table *tb = NULL;
    const char *src = mnt_context_get_source(cxt),
                *tgt = mnt_context_get_target(cxt);
    int rc = 1;

    if (!src || !tgt)
        return -1;

    if (!mnt_fs_is_kernel(fs)) {
        struct libmnt_table *tb = mnt_new_table_from_file("/proc/mounts");

        if (!tb)
            return -1;
        fs = mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD);
    }

    if (fs) {
        const char *type = mnt_fs_get_fstype(fs);
        if (type && strcmp(type, "nfs4") == 0)
            rc = 0;
    }
    mnt_free_table(tb);
    return rc;
}
Beispiel #3
0
/**
 * mnt_context_prepare_umount:
 * @cxt: mount context
 *
 * Prepare context for umounting, unnecessary for mnt_context_umount().
 *
 * Returns: 0 on success, and negative number in case of error.
 */
int mnt_context_prepare_umount(struct libmnt_context *cxt)
{
	int rc;

	assert(cxt);
	assert(cxt->fs);
	assert(cxt->helper_exec_status == 1);
	assert(cxt->syscall_status == 1);

	if (!cxt || !cxt->fs || mnt_fs_is_swaparea(cxt->fs))
		return -EINVAL;
	if (!mnt_context_get_source(cxt) && !mnt_context_get_target(cxt))
		return -EINVAL;
	if (cxt->flags & MNT_FL_PREPARED)
		return 0;

	free(cxt->helper);	/* be paranoid */
	cxt->helper = NULL;
	cxt->action = MNT_ACT_UMOUNT;

	rc = lookup_umount_fs(cxt);
	if (!rc)
		rc = mnt_context_merge_mflags(cxt);
	if (!rc)
		rc = evaluate_permissions(cxt);

	if (!rc && !cxt->helper) {

		if (cxt->user_mountflags & MNT_MS_HELPER)
			/* on helper= mount option based helper */
			rc = prepare_helper_from_options(cxt, "helper");

		if (!rc && !cxt->helper)
			/* on fstype based helper */
			rc = mnt_context_prepare_helper(cxt, "umount", NULL);
	}

	if (!rc && (cxt->user_mountflags & MNT_MS_LOOP))
		/* loop option explicitly specified in mtab, detach this loop */
		mnt_context_enable_loopdel(cxt, TRUE);

	if (!rc && mnt_context_is_loopdel(cxt) && cxt->fs) {
		const char *src = mnt_fs_get_srcpath(cxt->fs);

		if (src && (!is_loopdev(src) || loopdev_is_autoclear(src)))
			mnt_context_enable_loopdel(cxt, FALSE);
	}

	if (rc) {
		DBG(CXT, mnt_debug_h(cxt, "umount: preparing failed"));
		return rc;
	}
	cxt->flags |= MNT_FL_PREPARED;
	return rc;
}
Beispiel #4
0
/**
 * mnt_context_umount:
 * @cxt: umount context
 *
 * High-level, umounts filesystem by umount(2) or fork()+exec(/sbin/umount.type).
 *
 * This is similar to:
 *
 *	mnt_context_prepare_umount(cxt);
 *	mnt_context_do_umount(cxt);
 *	mnt_context_finalize_umount(cxt);
 *
 * See also mnt_context_disable_helpers().
 *
 * WARNING: non-zero return code does not mean that umount(2) syscall or
 *          umount.type helper wasn't successfully called.
 *
 *          Check mnt_context_get_status() after error!
 *
 * Returns: 0 on success;
 *         >0 in case of umount(2) error (returns syscall errno),
 *         <0 in case of other errors.
 */
int mnt_context_umount(struct libmnt_context *cxt)
{
	int rc;

	assert(cxt);
	assert(cxt->fs);
	assert(cxt->helper_exec_status == 1);
	assert(cxt->syscall_status == 1);

	DBG(CXT, mnt_debug_h(cxt, "umount: %s", mnt_context_get_target(cxt)));

	rc = mnt_context_prepare_umount(cxt);
	if (!rc)
		rc = mnt_context_prepare_update(cxt);
	if (!rc)
		rc = mnt_context_do_umount(cxt);
	if (!rc)
		rc = mnt_context_update_tabs(cxt);
	return rc;
}
Beispiel #5
0
int mnt_context_setup_loopdev(struct libmnt_context *cxt)
{
	const char *backing_file, *optstr, *loopdev = NULL;
	char *val = NULL, *loopval = NULL;
	size_t len;
	struct loopdev_cxt lc;
	int rc = 0, lo_flags = 0;
	uint64_t offset = 0, sizelimit = 0;

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

	backing_file = mnt_fs_get_srcpath(cxt->fs);
	if (!backing_file)
		return -EINVAL;

	DBG(LOOP, ul_debugobj(cxt, "trying to setup device for %s", backing_file));

	if (cxt->mountflags & MS_RDONLY) {
		DBG(LOOP, ul_debugobj(cxt, "enabling READ-ONLY flag"));
		lo_flags |= LO_FLAGS_READ_ONLY;
	}

	optstr = mnt_fs_get_user_options(cxt->fs);

	/*
	 * loop=
	 */
	if (rc == 0 && (cxt->user_mountflags & MNT_MS_LOOP) &&
	    mnt_optstr_get_option(optstr, "loop", &val, &len) == 0 && val) {
		loopval = strndup(val, len);
		rc = loopval ? 0 : -ENOMEM;
	}

	/*
	 * offset=
	 */
	if (rc == 0 && (cxt->user_mountflags & MNT_MS_OFFSET) &&
	    mnt_optstr_get_option(optstr, "offset", &val, &len) == 0) {
		rc = mnt_parse_offset(val, len, &offset);
		if (rc) {
			DBG(LOOP, ul_debugobj(cxt, "failed to parse offset="));
			rc = -MNT_ERR_MOUNTOPT;
		}
	}

	/*
	 * sizelimit=
	 */
	if (rc == 0 && (cxt->user_mountflags & MNT_MS_SIZELIMIT) &&
	    mnt_optstr_get_option(optstr, "sizelimit", &val, &len) == 0) {
		rc = mnt_parse_offset(val, len, &sizelimit);
		if (rc) {
			DBG(LOOP, ul_debugobj(cxt, "failed to parse sizelimit="));
			rc = -MNT_ERR_MOUNTOPT;
		}
	}

	/*
	 * encryption=
	 */
	if (rc == 0 && (cxt->user_mountflags & MNT_MS_ENCRYPTION) &&
	    mnt_optstr_get_option(optstr, "encryption", &val, &len) == 0) {
		DBG(LOOP, ul_debugobj(cxt, "encryption no longer supported"));
		rc = -MNT_ERR_MOUNTOPT;
	}

	if (rc == 0 && is_mounted_same_loopfile(cxt,
				mnt_context_get_target(cxt),
				backing_file, offset))
		rc = -EBUSY;

	if (rc)
		goto done_no_deinit;

	/* It is possible to mount the same file more times. If we set more
	 * than one loop device referring to the same file, kernel has no
	 * mechanism to detect it. To prevent data corruption, the same loop
	 * device has to be recycled.
	*/
	if (backing_file) {
		rc = loopcxt_init(&lc, 0);
		if (rc)
			goto done_no_deinit;

		rc = loopcxt_find_overlap(&lc, backing_file, offset, sizelimit);
		switch (rc) {
		case 0: /* not found */
			DBG(LOOP, ul_debugobj(cxt, "not found overlaping loopdev"));
			loopcxt_deinit(&lc);
			break;

		case 1:	/* overlap */
			DBG(LOOP, ul_debugobj(cxt, "overlaping %s detected",
						loopcxt_get_device(&lc)));
			rc = -MNT_ERR_LOOPOVERLAP;
			goto done;

		case 2: /* overlap -- full size and offset match (reuse) */
		{
			uint32_t lc_encrypt_type;

			DBG(LOOP, ul_debugobj(cxt, "re-using existing loop device %s",
				loopcxt_get_device(&lc)));

			/* Once a loop is initialized RO, there is no
			 * way to change its parameters. */
			if (loopcxt_is_readonly(&lc)
			    && !(lo_flags & LO_FLAGS_READ_ONLY)) {
				DBG(LOOP, ul_debugobj(cxt, "%s is read-only",
						loopcxt_get_device(&lc)));
				rc = -EROFS;
				goto done;
			}

			/* This is no more supported, but check to be safe. */
			if (loopcxt_get_encrypt_type(&lc, &lc_encrypt_type) == 0
			    && lc_encrypt_type != LO_CRYPT_NONE) {
				DBG(LOOP, ul_debugobj(cxt, "encryption no longer supported for device %s",
					loopcxt_get_device(&lc)));
				rc = -MNT_ERR_LOOPOVERLAP;
				goto done;
			}
			rc = 0;
			goto success;
		}
		default: /* error */
			goto done;
		}
	}

	DBG(LOOP, ul_debugobj(cxt, "not found; create a new loop device"));
	rc = loopcxt_init(&lc, 0);
	if (rc)
		goto done_no_deinit;
	if (loopval) {
		rc = loopcxt_set_device(&lc, loopval);
		if (rc == 0)
			loopdev = loopcxt_get_device(&lc);
	}
	if (rc)
		goto done;

	/* 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) ||
	    !mnt_context_mtab_writable(cxt)) {
		DBG(LOOP, ul_debugobj(cxt, "enabling AUTOCLEAR flag"));
		lo_flags |= LO_FLAGS_AUTOCLEAR;
	}

	do {
		/* found free device */
		if (!loopdev) {
			rc = loopcxt_find_unused(&lc);
			if (rc)
				goto done;
			DBG(LOOP, ul_debugobj(cxt, "trying to use %s",
						loopcxt_get_device(&lc)));
		}

		/* set device attributes
		 * -- note that loopcxt_find_unused() resets "lc"
		 */
		rc = loopcxt_set_backing_file(&lc, backing_file);

		if (!rc && offset)
			rc = loopcxt_set_offset(&lc, offset);
		if (!rc && sizelimit)
			rc = loopcxt_set_sizelimit(&lc, sizelimit);
		if (!rc)
			loopcxt_set_flags(&lc, lo_flags);
		if (rc) {
			DBG(LOOP, ul_debugobj(cxt, "failed to set loop attributes"));
			goto done;
		}

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

		if (loopdev || rc != -EBUSY) {
			DBG(LOOP, ul_debugobj(cxt, "failed to setup device"));
			rc = -MNT_ERR_LOOPDEV;
			goto done;
		}
		DBG(LOOP, ul_debugobj(cxt, "device stolen...trying again"));
	} while (1);

success:
	if (!rc)
		rc = mnt_fs_set_source(cxt->fs, loopcxt_get_device(&lc));

	if (!rc) {
		/* success */
		cxt->flags |= MNT_FL_LOOPDEV_READY;

		if ((cxt->user_mountflags & MNT_MS_LOOP) &&
		    loopcxt_is_autoclear(&lc)) {
			/*
			 * autoclear flag accepted by the kernel, don't store
			 * the "loop=" option to mtab.
			 */
			DBG(LOOP, ul_debugobj(cxt, "removing unnecessary loop= from mtab"));
			cxt->user_mountflags &= ~MNT_MS_LOOP;
			mnt_optstr_remove_option(&cxt->fs->user_optstr, "loop");
		}

		if (!(cxt->mountflags & MS_RDONLY) &&
		    loopcxt_is_readonly(&lc))
			/*
			 * mount planned read-write, but loopdev is read-only,
			 * let's fix mount options...
			 */
			mnt_context_set_mflags(cxt, cxt->mountflags | MS_RDONLY);

		/* we have to keep the device open until mount(1),
		 * otherwise it will be auto-cleared by kernel
		 */
		cxt->loopdev_fd = loopcxt_get_fd(&lc);
		if (cxt->loopdev_fd < 0) {
			DBG(LOOP, ul_debugobj(cxt, "failed to get loopdev FD"));
			rc = -errno;
		} else
			loopcxt_set_fd(&lc, -1, 0);
	}
done:
	loopcxt_deinit(&lc);
done_no_deinit:
	free(loopval);
	return rc;
}
Beispiel #6
0
int mnt_context_setup_loopdev(struct libmnt_context *cxt)
{
	const char *backing_file, *optstr, *loopdev = NULL;
	char *val = NULL;
	size_t len;
	struct loopdev_cxt lc;
	int rc = 0, lo_flags = 0;
	uint64_t offset = 0, sizelimit = 0;

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

	if (!cxt)
		return -EINVAL;

	backing_file = mnt_fs_get_srcpath(cxt->fs);
	if (!backing_file)
		return -EINVAL;

	DBG(CXT, ul_debugobj(cxt, "trying to setup loopdev for %s", backing_file));

	if (cxt->mountflags & MS_RDONLY) {
		DBG(CXT, ul_debugobj(cxt, "enabling READ-ONLY flag"));
		lo_flags |= LO_FLAGS_READ_ONLY;
	}

	rc = loopcxt_init(&lc, 0);
	if (rc)
		return rc;

	ON_DBG(CXT, loopcxt_enable_debug(&lc, 1));

	optstr = mnt_fs_get_user_options(cxt->fs);

	/*
	 * loop=
	 */
	if (rc == 0 && (cxt->user_mountflags & MNT_MS_LOOP) &&
	    mnt_optstr_get_option(optstr, "loop", &val, &len) == 0 && val) {

		val = strndup(val, len);
		rc = val ? loopcxt_set_device(&lc, val) : -ENOMEM;
		free(val);

		if (rc == 0)
			loopdev = loopcxt_get_device(&lc);
	}

	/*
	 * offset=
	 */
	if (rc == 0 && (cxt->user_mountflags & MNT_MS_OFFSET) &&
	    mnt_optstr_get_option(optstr, "offset", &val, &len) == 0) {
		rc = mnt_parse_offset(val, len, &offset);
		if (rc) {
			DBG(CXT, ul_debugobj(cxt, "failed to parse offset="));
			rc = -MNT_ERR_MOUNTOPT;
		}
	}

	/*
	 * sizelimit=
	 */
	if (rc == 0 && (cxt->user_mountflags & MNT_MS_SIZELIMIT) &&
	    mnt_optstr_get_option(optstr, "sizelimit", &val, &len) == 0) {
		rc = mnt_parse_offset(val, len, &sizelimit);
		if (rc) {
			DBG(CXT, ul_debugobj(cxt, "failed to parse sizelimit="));
			rc = -MNT_ERR_MOUNTOPT;
		}
	}

	/*
	 * encryption=
	 */
	if (rc == 0 && (cxt->user_mountflags & MNT_MS_ENCRYPTION) &&
	    mnt_optstr_get_option(optstr, "encryption", &val, &len) == 0) {
		DBG(CXT, ul_debugobj(cxt, "encryption no longer supported"));
		rc = -MNT_ERR_MOUNTOPT;
	}

	if (rc == 0 && is_mounted_same_loopfile(cxt,
				mnt_context_get_target(cxt),
				backing_file, offset))
		rc = -EBUSY;

	if (rc)
		goto done;

	/* 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) ||
	    !mnt_context_mtab_writable(cxt)) {
		DBG(CXT, ul_debugobj(cxt, "enabling AUTOCLEAR flag"));
		lo_flags |= LO_FLAGS_AUTOCLEAR;
	}

	do {
		/* found free device */
		if (!loopdev) {
			rc = loopcxt_find_unused(&lc);
			if (rc)
				goto done;
			DBG(CXT, ul_debugobj(cxt, "trying to use %s",
						loopcxt_get_device(&lc)));
		}

		/* set device attributes
		 * -- note that loopcxt_find_unused() resets "lc"
		 */
		rc = loopcxt_set_backing_file(&lc, backing_file);

		if (!rc && offset)
			rc = loopcxt_set_offset(&lc, offset);
		if (!rc && sizelimit)
			rc = loopcxt_set_sizelimit(&lc, sizelimit);
		if (!rc)
			loopcxt_set_flags(&lc, lo_flags);
		if (rc) {
			DBG(CXT, ul_debugobj(cxt, "failed to set loopdev attributes"));
			goto done;
		}

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

		if (loopdev || rc != -EBUSY) {
			DBG(CXT, ul_debugobj(cxt, "failed to setup device"));
			rc = -MNT_ERR_LOOPDEV;
			goto done;
		}
		DBG(CXT, ul_debugobj(cxt, "loopdev stolen...trying again"));
	} while (1);

	if (!rc)
		rc = mnt_fs_set_source(cxt->fs, loopcxt_get_device(&lc));

	if (!rc) {
		/* success */
		cxt->flags |= MNT_FL_LOOPDEV_READY;

		if ((cxt->user_mountflags & MNT_MS_LOOP) &&
		    loopcxt_is_autoclear(&lc)) {
			/*
			 * autoclear flag accepted by the kernel, don't store
			 * the "loop=" option to mtab.
			 */
			cxt->user_mountflags &= ~MNT_MS_LOOP;
			mnt_optstr_remove_option(&cxt->fs->user_optstr, "loop");
		}

		if (!(cxt->mountflags & MS_RDONLY) &&
		    loopcxt_is_readonly(&lc))
			/*
			 * mount planned read-write, but loopdev is read-only,
			 * let's fix mount options...
			 */
			mnt_context_set_mflags(cxt, cxt->mountflags | MS_RDONLY);

		/* we have to keep the device open until mount(1),
		 * otherwise it will be auto-cleared by kernel
		 */
		cxt->loopdev_fd = loopcxt_get_fd(&lc);
		loopcxt_set_fd(&lc, -1, 0);
	}
done:
	loopcxt_deinit(&lc);
	return rc;
}