Beispiel #1
0
/**
 * Resolve bindings (if any) in @guest_path and copy the translated
 * path into @host_path.  Also, this function checks that a non-final
 * component is either a directory (returned value is 0) or a symlink
 * (returned value is 1), otherwise it returns -errno (-ENOENT or
 * -ENOTDIR).
 */
static inline int substitute_binding_stat(Tracee *tracee, Finality finality, unsigned int recursion_level,
					const char guest_path[PATH_MAX], char host_path[PATH_MAX])
{
	struct stat statl;
	int status;

	strcpy(host_path, guest_path);
	status = substitute_binding(tracee, GUEST, host_path);
	if (status < 0)
		return status;

	/* Don't notify extensions during the initialization of a binding.  */
	if (tracee->glue_type == 0) {
		status = notify_extensions(tracee, HOST_PATH, (intptr_t)host_path,
					IS_FINAL(finality) && recursion_level == 0);
		if (status < 0)
			return status;
	}

	statl.st_mode = 0;
	status = lstat(host_path, &statl);

	/* Build the glue between the hostfs and the guestfs during
	 * the initialization of a binding.  */
	if (status < 0 && tracee->glue_type != 0) {
		statl.st_mode = build_glue(tracee, guest_path, host_path, finality);
		if (statl.st_mode == 0)
			status = -1;
	}

	/* Return an error if a non-final component isn't a
	 * directory nor a symlink.  The error is "No such
	 * file or directory" if this component doesn't exist,
	 * otherwise the error is "Not a directory".  */
	if (!IS_FINAL(finality) && !S_ISDIR(statl.st_mode) && !S_ISLNK(statl.st_mode))
		return (status < 0 ? -ENOENT : -ENOTDIR);

	return (S_ISLNK(statl.st_mode) ? 1 : 0);
}
Beispiel #2
0
/**
 * Copy in @result the equivalent of "@tracee->root + canon(@dir_fd +
 * @user_path)".  If @user_path is not absolute then it is relative to
 * the directory referred by the descriptor @dir_fd (AT_FDCWD is for
 * the current working directory).  See the documentation of
 * canonicalize() for the meaning of @deref_final.  This function
 * returns -errno if an error occured, otherwise 0.
 */
int translate_path(Tracee *tracee, char result[PATH_MAX], int dir_fd,
		const char *user_path, bool deref_final)
{
	char guest_path[PATH_MAX];
	int status;

	/* Use "/" as the base if it is an absolute guest path. */
	if (user_path[0] == '/') {
		strcpy(result, "/");
	}
	/* It is relative to a directory referred by a descriptor, see
	 * openat(2) for details. */
	else if (dir_fd != AT_FDCWD) {
		/* /proc/@tracee->pid/fd/@dir_fd -> result.  */
		status = readlink_proc_pid_fd(tracee->pid, dir_fd, result);
		if (status < 0)
			return status;

		/* Named file descriptors may reference special
		 * objects like pipes, sockets, inodes, ...  Such
		 * objects do not belong to the file-system.  */
		if (result[0] != '/')
			return -ENOTDIR;

		/* Remove the leading "root" part of the base
		 * (required!). */
		status = detranslate_path(tracee, result, NULL);
		if (status < 0)
			return status;
	}
	/* It is relative to the current working directory.  */
	else {
		status = getcwd2(tracee, result);
		if (status < 0)
			return status;
	}

	VERBOSE(tracee, 2, "pid %d: translate(\"%s\" + \"%s\")",
		tracee != NULL ? tracee->pid : 0, result, user_path);

	status = notify_extensions(tracee, GUEST_PATH, (intptr_t) result, (intptr_t) user_path);
	if (status < 0)
		return status;
	if (status > 0)
		goto skip;

	/* So far "result" was used as a base path, it's time to join
	 * it to the user path.  */
	assert(result[0] == '/');
	status = join_paths(2, guest_path, result, user_path);
	if (status < 0)
		return status;
	strcpy(result, "/");

	/* Canonicalize regarding the new root. */
	status = canonicalize(tracee, guest_path, deref_final, result, 0);
	if (status < 0)
		return status;

	/* Final binding substitution to convert "result" into a host
	 * path, since canonicalize() works from the guest
	 * point-of-view.  */
	status = substitute_binding(tracee, GUEST, result);
	if (status < 0)
		return status;

skip:
	VERBOSE(tracee, 2, "pid %d:          -> \"%s\"",
		tracee != NULL ? tracee->pid : 0, result);
	return 0;
}
Beispiel #3
0
/**
 * Remove/substitute the leading part of a "translated" @path.  It
 * returns 0 if no transformation is required (ie. symmetric binding),
 * otherwise it returns the size in bytes of the updated @path,
 * including the end-of-string terminator.  On error it returns
 * -errno.
 */
int detranslate_path(Tracee *tracee, char path[PATH_MAX], const char t_referrer[PATH_MAX])
{
	size_t prefix_length;
	ssize_t new_length;

	bool sanity_check;
	bool follow_binding;

	/* Sanity check.  */
	if (strnlen(path, PATH_MAX) >= PATH_MAX)
		return -ENAMETOOLONG;

	/* Don't try to detranslate relative paths (typically the
	 * target of a relative symbolic link). */
	if (path[0] != '/')
		return 0;

	/* Is it a symlink?  */
	if (t_referrer != NULL) {
		Comparison comparison;

		sanity_check = false;
		follow_binding = false;

		/* In some cases bindings have to be resolved.  */
		comparison = compare_paths("/proc", t_referrer);
		if (comparison == PATH1_IS_PREFIX) {
			/* Some links in "/proc" are generated
			 * dynamically by the kernel.  PRoot has to
			 * emulate some of them.  */
			char proc_path[PATH_MAX];
			strcpy(proc_path, path);
			new_length = readlink_proc2(tracee, proc_path, t_referrer);
			if (new_length < 0)
				return new_length;
			if (new_length != 0) {
				strcpy(path, proc_path);
				return new_length + 1;
			}

			/* Always resolve bindings for symlinks in
			 * "/proc", they always point to the emulated
			 * file-system namespace by design. */
			follow_binding = true;
		}
		else if (!belongs_to_guestfs(tracee, t_referrer)) {
			const char *binding_referree;
			const char *binding_referrer;

			binding_referree = get_path_binding(tracee, HOST, path);
			binding_referrer = get_path_binding(tracee, HOST, t_referrer);
			assert(binding_referrer != NULL);

			/* Resolve bindings for symlinks that belong
			 * to a binding and point to the same binding.
			 * For example, if "-b /lib:/foo" is specified
			 * and the symlink "/lib/a -> /lib/b" exists
			 * in the host rootfs namespace, then it
			 * should appear as "/foo/a -> /foo/b" in the
			 * guest rootfs namespace for consistency
			 * reasons.  */
			if (binding_referree != NULL) {
				comparison = compare_paths(binding_referree, binding_referrer);
				follow_binding = (comparison == PATHS_ARE_EQUAL);
			}
		}
	}
	else {
		sanity_check = true;
		follow_binding = true;
	}

	if (follow_binding) {
		switch (substitute_binding(tracee, HOST, path)) {
		case 0:
			return 0;
		case 1:
			return strlen(path) + 1;
		default:
			break;
		}
	}

	switch (compare_paths(get_root(tracee), path)) {
	case PATH1_IS_PREFIX:
		/* Remove the leading part, that is, the "root".  */
		prefix_length = strlen(get_root(tracee));

		/* Special case when path to the guest rootfs == "/". */
		if (prefix_length == 1)
			prefix_length = 0;

		new_length = strlen(path) - prefix_length;
		memmove(path, path + prefix_length, new_length);

		path[new_length] = '\0';
		break;

	case PATHS_ARE_EQUAL:
		/* Special case when path == root. */
		new_length = 1;
		strcpy(path, "/");
		break;

	default:
		/* Ensure the path is within the new root.  */
		if (sanity_check)
			return -EPERM;
		else
			return 0;
	}

	return new_length + 1;
}
Beispiel #4
0
/**
 * Copy in @host_path the equivalent of "@tracee->root +
 * canonicalize(@dir_fd + @guest_path)".  If @guest_path is not
 * absolute then it is relative to the directory referred by the
 * descriptor @dir_fd (AT_FDCWD is for the current working directory).
 * See the documentation of canonicalize() for the meaning of
 * @deref_final.  This function returns -errno if an error occured,
 * otherwise 0.
 */
int translate_path(Tracee *tracee, char host_path[PATH_MAX],
		   int dir_fd, const char *guest_path, bool deref_final)
{
	int status;

	/* Use "/" as the base if it is an absolute guest path. */
	if (guest_path[0] == '/') {
		strcpy(host_path, "/");
	}
	/* It is relative to the current working directory or to a
	 * directory referred by a descriptors, see openat(2) for
	 * details. */
	else if (dir_fd == AT_FDCWD) {
		status = getcwd2(tracee, host_path);
		if (status < 0)
			return status;
	}
	else {
		struct stat statl;

		/* /proc/@tracee->pid/fd/@dir_fd -> host_path.  */
		status = readlink_proc_pid_fd(tracee->pid, dir_fd, host_path);
		if (status < 0)
			return status;

		/* Ensure it points to a directory. */
		status = stat(host_path, &statl);
		if (status < 0)
			return -errno;
		if (!S_ISDIR(statl.st_mode))
			return -ENOTDIR;

		/* Remove the leading "root" part of the base
		 * (required!). */
		status = detranslate_path(tracee, host_path, NULL);
		if (status < 0)
			return status;
	}

	VERBOSE(tracee, 2, "pid %d: translate(\"%s\" + \"%s\")",
		tracee != NULL ? tracee->pid : 0, host_path, guest_path);

	status = notify_extensions(tracee, GUEST_PATH, (intptr_t)host_path, (intptr_t)guest_path);
	if (status < 0)
		return status;
	if (status > 0)
		goto skip;

	/* Canonicalize regarding the new root. */
	status = canonicalize(tracee, guest_path, deref_final, host_path, 0);
	if (status < 0)
		return status;

	/* Final binding substitution to convert "host_path" into a host
	 * path, since canonicalize() works from the guest
	 * point-of-view.  */
	status = substitute_binding(tracee, GUEST, host_path);
	if (status < 0)
		return status;

skip:
	VERBOSE(tracee, 2, "pid %d:          -> \"%s\"",
		tracee != NULL ? tracee->pid : 0, host_path);
	return 0;
}