822 lines
22 KiB
Diff
822 lines
22 KiB
Diff
From c008838abc34819705f1b2ad51b24b5d7bbd629c Mon Sep 17 00:00:00 2001
|
|
From: tanyifeng <tanyifeng1@huawei.com>
|
|
Date: Mon, 14 Jan 2019 20:12:06 +0800
|
|
Subject: [PATCH 024/139] isulad: support symlink in mount entry, and not
|
|
permit mount to /proc
|
|
|
|
Signed-off-by: LiFeng <lifeng68@huawei.com>
|
|
---
|
|
src/lxc/Makefile.am | 2 +
|
|
src/lxc/conf.c | 108 ++++++++++-
|
|
src/lxc/path.c | 546 ++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
src/lxc/path.h | 70 +++++++
|
|
4 files changed, 721 insertions(+), 5 deletions(-)
|
|
create mode 100644 src/lxc/path.c
|
|
create mode 100644 src/lxc/path.h
|
|
|
|
diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
|
|
index 08e2fab..f2928b7 100644
|
|
--- a/src/lxc/Makefile.am
|
|
+++ b/src/lxc/Makefile.am
|
|
@@ -12,6 +12,7 @@ noinst_HEADERS = attach.h \
|
|
confile_utils.h \
|
|
criu.h \
|
|
error.h \
|
|
+ path.h \
|
|
file_utils.h \
|
|
../include/netns_ifaddrs.h \
|
|
initutils.h \
|
|
@@ -95,6 +96,7 @@ liblxc_la_SOURCES = af_unix.c af_unix.h \
|
|
commands_utils.c commands_utils.h \
|
|
conf.c conf.h \
|
|
confile.c confile.h \
|
|
+ path.c path.h \
|
|
confile_utils.c confile_utils.h \
|
|
criu.c criu.h \
|
|
error.c error.h \
|
|
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
|
|
index 55d1e45..800573a 100644
|
|
--- a/src/lxc/conf.c
|
|
+++ b/src/lxc/conf.c
|
|
@@ -77,6 +77,7 @@
|
|
#include "storage/overlay.h"
|
|
#include "syscall_wrappers.h"
|
|
#include "terminal.h"
|
|
+#include "path.h"
|
|
#include "utils.h"
|
|
|
|
#ifdef MAJOR_IN_MKDEV
|
|
@@ -2309,6 +2310,79 @@ static void cull_mntent_opt(struct mntent *mntent)
|
|
}
|
|
}
|
|
|
|
+/* isulad: checkMountDestination checks to ensure that the mount destination is not over the top of /proc.
|
|
+ * dest is required to be an abs path and have any symlinks resolved before calling this function. */
|
|
+static int check_mount_destination(const char *rootfs, const char *dest)
|
|
+{
|
|
+ const char *invalid_destinations[] = {
|
|
+ "/proc",
|
|
+ NULL
|
|
+ };
|
|
+ // White list, it should be sub directories of invalid destinations
|
|
+ const char *valid_destinations[] = {
|
|
+ // These entries can be bind mounted by files emulated by fuse,
|
|
+ // so commands like top, free displays stats in container.
|
|
+ "/proc/cpuinfo",
|
|
+ "/proc/diskstats",
|
|
+ "/proc/meminfo",
|
|
+ "/proc/stat",
|
|
+ "/proc/swaps",
|
|
+ "/proc/uptime",
|
|
+ "/proc/net/dev",
|
|
+ NULL
|
|
+ };
|
|
+ const char **valid, **invalid;
|
|
+
|
|
+ for(valid = valid_destinations; *valid != NULL; valid++) {
|
|
+ char *fullpath, *relpath;
|
|
+ const char *parts[3] = {
|
|
+ rootfs,
|
|
+ *valid,
|
|
+ NULL
|
|
+ };
|
|
+ fullpath = lxc_string_join("/", parts, false);
|
|
+ if (!fullpath) {
|
|
+ ERROR("Out of memory");
|
|
+ return -1;
|
|
+ }
|
|
+ relpath = path_relative(fullpath, dest);
|
|
+ free(fullpath);
|
|
+ if (!relpath)
|
|
+ return -1;
|
|
+ if (!strcmp(relpath, ".")) {
|
|
+ free(relpath);
|
|
+ return 0;
|
|
+ }
|
|
+ free(relpath);
|
|
+ }
|
|
+
|
|
+ for(invalid = invalid_destinations; *invalid != NULL; invalid++) {
|
|
+ char *fullpath, *relpath;
|
|
+ const char *parts[3] = {
|
|
+ rootfs,
|
|
+ *invalid,
|
|
+ NULL
|
|
+ };
|
|
+ fullpath = lxc_string_join("/", parts, false);
|
|
+ if (!fullpath) {
|
|
+ ERROR("Out of memory");
|
|
+ return -1;
|
|
+ }
|
|
+ relpath = path_relative(fullpath, dest);
|
|
+ free(fullpath);
|
|
+ if (!relpath)
|
|
+ return -1;
|
|
+ if (!strcmp(relpath, ".") || strncmp(relpath, "..", 2)) {
|
|
+ ERROR("%s cannot be mounted because it is located inside %s", dest, *invalid);
|
|
+ free(relpath);
|
|
+ return -1;
|
|
+ }
|
|
+ free(relpath);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int mount_entry_create_dir_file(const struct mntent *mntent,
|
|
const char *path,
|
|
const struct lxc_rootfs *rootfs,
|
|
@@ -2370,7 +2444,8 @@ static inline int mount_entry_on_generic(struct mntent *mntent,
|
|
unsigned long mntflags, pflags;
|
|
char *mntdata;
|
|
bool dev, optional, relative;
|
|
- char *rootfs_path = NULL;
|
|
+ char *rootfs_path = NULL, *rpath = NULL;
|
|
+ const char *dest = path;
|
|
|
|
optional = hasmntopt(mntent, "optional") != NULL;
|
|
dev = hasmntopt(mntent, "dev") != NULL;
|
|
@@ -2379,9 +2454,29 @@ static inline int mount_entry_on_generic(struct mntent *mntent,
|
|
if (rootfs && rootfs->path)
|
|
rootfs_path = rootfs->mount;
|
|
|
|
- ret = mount_entry_create_dir_file(mntent, path, rootfs, lxc_name,
|
|
- lxc_path);
|
|
+ // isulad: ensure that the destination of the bind mount is resolved of symlinks at mount time because
|
|
+ // any previous mounts can invalidate the next mount's destination.
|
|
+ // this can happen when a user specifies mounts within other mounts to cause breakouts or other
|
|
+ // evil stuff to try to escape the container's rootfs.
|
|
+ if (rootfs_path) {
|
|
+ rpath = follow_symlink_in_scope(path, rootfs_path);
|
|
+ if (!rpath) {
|
|
+ ERROR("Failed to get real path for '%s'", path);
|
|
+ return -1;
|
|
+ }
|
|
+ dest = rpath;
|
|
+
|
|
+ ret = check_mount_destination(rootfs_path, dest);
|
|
+ if (ret) {
|
|
+ ERROR("Mount destination is invalid: '%s'", dest);
|
|
+ free(rpath);
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = mount_entry_create_dir_file(mntent, dest, rootfs, lxc_name, lxc_path);
|
|
if (ret < 0) {
|
|
+ free(rpath);
|
|
if (optional)
|
|
return 0;
|
|
|
|
@@ -2390,13 +2485,16 @@ static inline int mount_entry_on_generic(struct mntent *mntent,
|
|
cull_mntent_opt(mntent);
|
|
|
|
ret = parse_mntopts(mntent->mnt_opts, &mntflags, &pflags, &mntdata);
|
|
- if (ret < 0)
|
|
+ if (ret < 0) {
|
|
+ free(rpath);
|
|
return -1;
|
|
+ }
|
|
|
|
- ret = mount_entry(mntent->mnt_fsname, path, mntent->mnt_type, mntflags,
|
|
+ ret = mount_entry(mntent->mnt_fsname, dest, mntent->mnt_type, mntflags,
|
|
pflags, mntdata, optional, dev, relative, rootfs_path);
|
|
|
|
free(mntdata);
|
|
+ free(rpath);
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/src/lxc/path.c b/src/lxc/path.c
|
|
new file mode 100644
|
|
index 0000000..e917dcb
|
|
--- /dev/null
|
|
+++ b/src/lxc/path.c
|
|
@@ -0,0 +1,546 @@
|
|
+#include <unistd.h>
|
|
+#include <stdlib.h>
|
|
+#include <limits.h>
|
|
+#include <string.h>
|
|
+#include <stdio.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <sys/param.h>
|
|
+#include <libgen.h>
|
|
+
|
|
+#include "path.h"
|
|
+#include "log.h"
|
|
+
|
|
+lxc_log_define(lxc_path_ui, lxc);
|
|
+
|
|
+#define ISSLASH(C) ((C) == '/')
|
|
+#define IS_ABSOLUTE_FILE_NAME(F) (ISSLASH ((F)[0]))
|
|
+#define IS_RELATIVE_FILE_NAME(F) (! IS_ABSOLUTE_FILE_NAME (F))
|
|
+
|
|
+bool specify_current_dir(const char *path)
|
|
+{
|
|
+ char *basec = NULL, *bname = NULL;
|
|
+ bool res = false;
|
|
+
|
|
+ basec = strdup(path);
|
|
+ if (!basec) {
|
|
+ ERROR("Out of memory");
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ bname = basename(basec);
|
|
+ res = !strcmp(bname, ".");
|
|
+ free(basec);
|
|
+ return res;
|
|
+}
|
|
+
|
|
+bool has_traling_path_separator(const char *path)
|
|
+{
|
|
+ return path && strlen(path) && (path[strlen(path) - 1] == '/');
|
|
+}
|
|
+
|
|
+// PreserveTrailingDotOrSeparator returns the given cleaned path
|
|
+// and appends a trailing `/.` or `/` if its corresponding original
|
|
+// path ends with a trailing `/.` or `/`. If the cleaned
|
|
+// path already ends in a `.` path segment, then another is not added. If the
|
|
+// clean path already ends in a path separator, then another is not added.
|
|
+char *preserve_trailing_dot_or_separator(const char *cleanedpath,
|
|
+ const char *originalpath)
|
|
+{
|
|
+ char *respath = NULL;
|
|
+ size_t len;
|
|
+
|
|
+ len = strlen(cleanedpath) + 3;
|
|
+ respath = malloc(len);
|
|
+ if (!respath) {
|
|
+ ERROR("Out of memory");
|
|
+ return NULL;
|
|
+ }
|
|
+ memset(respath, 0x00, len);
|
|
+ strcat(respath, cleanedpath);
|
|
+
|
|
+ if (!specify_current_dir(cleanedpath) && specify_current_dir(originalpath)) {
|
|
+ if (!has_traling_path_separator(respath))
|
|
+ strcat(respath, "/");
|
|
+ strcat(respath, ".");
|
|
+ }
|
|
+
|
|
+ if (!has_traling_path_separator(respath) &&
|
|
+ has_traling_path_separator(originalpath))
|
|
+ strcat(respath, "/");
|
|
+
|
|
+ return respath;
|
|
+}
|
|
+
|
|
+
|
|
+// Split splits path immediately following the final Separator,
|
|
+// separating it into a directory and file name component.
|
|
+// If there is no Separator in path, Split returns an empty dir
|
|
+// and file set to path.
|
|
+// The returned values have the property that path = dir+file.
|
|
+bool filepath_split(const char *path, char **dir, char **base)
|
|
+{
|
|
+ ssize_t i;
|
|
+ size_t len;
|
|
+
|
|
+ len = strlen(path);
|
|
+ i = len - 1;
|
|
+ while (i >= 0 && path[i] != '/')
|
|
+ i--;
|
|
+
|
|
+ *dir = malloc(i + 2);
|
|
+ if (!*dir) {
|
|
+ ERROR("Out of memory");
|
|
+ return false;
|
|
+ }
|
|
+ memcpy(*dir, path, i + 1);
|
|
+ *(*dir + i + 1) = '\0';
|
|
+
|
|
+ *base = strdup(path + i + 1);
|
|
+ if (!*base) {
|
|
+ ERROR("Out of memory");
|
|
+ free(*dir);
|
|
+ *dir = NULL;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cleanpath is similar to realpath of glibc, but not expands symbolic links,
|
|
+ * and not check the existence of components of the path.
|
|
+ */
|
|
+char *cleanpath(const char *path, char *resolved)
|
|
+{
|
|
+ char *rpath, *dest;
|
|
+ const char *start, *end, *rpath_limit;
|
|
+
|
|
+ if (path == NULL || path[0] == '\0')
|
|
+ return NULL;
|
|
+
|
|
+ if (resolved == NULL) {
|
|
+ rpath = malloc(PATH_MAX);
|
|
+ if (rpath == NULL) {
|
|
+ ERROR("Out of memory");
|
|
+ return NULL;
|
|
+ }
|
|
+ } else {
|
|
+ rpath = resolved;
|
|
+ }
|
|
+ rpath_limit = rpath + PATH_MAX;
|
|
+
|
|
+ if (!IS_ABSOLUTE_FILE_NAME(path)) {
|
|
+ if (!getcwd(rpath, PATH_MAX)) {
|
|
+ ERROR("Failed to getcwd");
|
|
+ rpath[0] = '\0';
|
|
+ goto error;
|
|
+ }
|
|
+ dest = strchr(rpath, '\0');
|
|
+ start = path;
|
|
+ } else {
|
|
+ dest = rpath;
|
|
+ *dest++ = '/';
|
|
+ start = path;
|
|
+ }
|
|
+
|
|
+ for (end = start; *start; start = end) {
|
|
+ /* Skip sequence of multiple path-separators. */
|
|
+ while (ISSLASH(*start))
|
|
+ ++start;
|
|
+
|
|
+ /* Find end of path component. */
|
|
+ for (end = start; *end && !ISSLASH(*end); ++end)
|
|
+ /* Nothing. */;
|
|
+
|
|
+ if (end - start == 0) {
|
|
+ break;
|
|
+ } else if (end - start == 1 && start[0] == '.') {
|
|
+ /* nothing */;
|
|
+ } else if (end - start == 2 && start[0] == '.' && start[1] == '.') {
|
|
+ /* Back up to previous component, ignore if at root already. */
|
|
+ if (dest > rpath + 1)
|
|
+ for (--dest; dest > rpath && !ISSLASH(dest[-1]); --dest)
|
|
+ continue;
|
|
+ } else {
|
|
+ size_t new_size;
|
|
+
|
|
+ if (!ISSLASH(dest[-1]))
|
|
+ *dest++ = '/';
|
|
+
|
|
+ if (dest + (end - start) >= rpath_limit) {
|
|
+ long long dest_offset = dest - rpath;
|
|
+ char *new_rpath;
|
|
+
|
|
+ if (resolved) {
|
|
+ printf("Path is to long");
|
|
+ if (dest > rpath + 1)
|
|
+ dest--;
|
|
+ *dest = '\0';
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ new_size = rpath_limit - rpath;
|
|
+ if (end - start + 1 > PATH_MAX)
|
|
+ new_size += end - start + 1;
|
|
+ else
|
|
+ new_size += PATH_MAX;
|
|
+ new_rpath = (char *) realloc(rpath, new_size);
|
|
+ if (new_rpath == NULL) {
|
|
+ ERROR("Out of memory");
|
|
+ goto error;
|
|
+ }
|
|
+ rpath = new_rpath;
|
|
+ rpath_limit = rpath + new_size;
|
|
+
|
|
+ dest = rpath + dest_offset;
|
|
+ }
|
|
+
|
|
+ memcpy(dest, start, end - start);
|
|
+ dest += end - start;
|
|
+ *dest = '\0';
|
|
+ }
|
|
+ }
|
|
+ if (dest > rpath + 1 && ISSLASH(dest[-1]))
|
|
+ --dest;
|
|
+ *dest = '\0';
|
|
+
|
|
+ return rpath;
|
|
+
|
|
+error:
|
|
+ if (resolved == NULL)
|
|
+ free(rpath);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+// evalSymlinksInScope will evaluate symlinks in `path` within a scope `root` and return
|
|
+// a result guaranteed to be contained within the scope `root`, at the time of the call.
|
|
+// Symlinks in `root` are not evaluated and left as-is.
|
|
+// Errors encountered while attempting to evaluate symlinks in path will be returned.
|
|
+// Non-existing paths are valid and do not constitute an error.
|
|
+// `path` has to contain `root` as a prefix, or else an error will be returned.
|
|
+// Trying to break out from `root` does not constitute an error.
|
|
+//
|
|
+// Example:
|
|
+// If /foo/bar -> /outside,
|
|
+// FollowSymlinkInScope("/foo/bar", "/foo") == "/foo/outside" instead of "/oustide"
|
|
+char *eval_symlinks_in_scope(const char *fullpath, const char *rootpath)
|
|
+{
|
|
+ char resroot[PATH_MAX] = {0}, *root = NULL;
|
|
+ char *rpath, *dest, *prefix, *extra_buf = NULL;
|
|
+ const char *start, *end, *rpath_limit;
|
|
+ int num_links = 0;
|
|
+ size_t prefix_len;
|
|
+
|
|
+ if (!fullpath || !rootpath)
|
|
+ return NULL;
|
|
+
|
|
+ root = cleanpath(rootpath, resroot);
|
|
+ if (!root) {
|
|
+ ERROR("Failed to get cleaned path");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (!strcmp(fullpath, root))
|
|
+ return strdup(fullpath);
|
|
+
|
|
+ if (!strstr(fullpath, root)) {
|
|
+ ERROR("Path '%s' is not in '%s'", fullpath, root);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ rpath = malloc(PATH_MAX);
|
|
+ if (rpath == NULL) {
|
|
+ ERROR("Out of memory");
|
|
+ goto error;
|
|
+ return NULL;
|
|
+ }
|
|
+ rpath_limit = rpath + PATH_MAX;
|
|
+
|
|
+ prefix = root;
|
|
+ prefix_len = strlen(prefix);
|
|
+ if (!strcmp(prefix, "/"))
|
|
+ prefix_len = 0;
|
|
+
|
|
+ dest = rpath;
|
|
+ if (prefix_len) {
|
|
+ memcpy(rpath, prefix, prefix_len);
|
|
+ dest += prefix_len;
|
|
+ }
|
|
+ *dest++ = '/';
|
|
+ start = fullpath + prefix_len;
|
|
+
|
|
+ for (end = start; *start; start = end) {
|
|
+ struct stat st;
|
|
+ int n;
|
|
+
|
|
+ /* Skip sequence of multiple path-separators. */
|
|
+ while (ISSLASH(*start))
|
|
+ ++start;
|
|
+
|
|
+ /* Find end of path component. */
|
|
+ for (end = start; *end && !ISSLASH(*end); ++end)
|
|
+ /* Nothing. */;
|
|
+
|
|
+ if (end - start == 0) {
|
|
+ break;
|
|
+ } else if (end - start == 1 && start[0] == '.') {
|
|
+ /* nothing */;
|
|
+ } else if (end - start == 2 && start[0] == '.' && start[1] == '.') {
|
|
+ /* Back up to previous component, ignore if at root already. */
|
|
+ if (dest > rpath + prefix_len + 1)
|
|
+ for (--dest; dest > rpath && !ISSLASH(dest[-1]); --dest)
|
|
+ continue;
|
|
+ } else {
|
|
+ size_t new_size;
|
|
+
|
|
+ if (!ISSLASH(dest[-1]))
|
|
+ *dest++ = '/';
|
|
+
|
|
+ if (dest + (end - start) >= rpath_limit) {
|
|
+ long long dest_offset = dest - rpath;
|
|
+ char *new_rpath;
|
|
+
|
|
+ new_size = rpath_limit - rpath;
|
|
+ if (end - start + 1 > PATH_MAX)
|
|
+ new_size += end - start + 1;
|
|
+ else
|
|
+ new_size += PATH_MAX;
|
|
+ new_rpath = (char *) realloc(rpath, new_size);
|
|
+ if (new_rpath == NULL) {
|
|
+ ERROR("Out of memory");
|
|
+ goto error;
|
|
+ }
|
|
+ rpath = new_rpath;
|
|
+ rpath_limit = rpath + new_size;
|
|
+
|
|
+ dest = rpath + dest_offset;
|
|
+ }
|
|
+
|
|
+ memcpy(dest, start, end - start);
|
|
+ dest += end - start;
|
|
+ *dest = '\0';
|
|
+
|
|
+ if (lstat(rpath, &st) < 0) {
|
|
+ // if rpath does not exist, accept it
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (S_ISLNK(st.st_mode)) {
|
|
+ char *buf;
|
|
+ size_t len;
|
|
+
|
|
+ if (++num_links > MAXSYMLINKS) {
|
|
+ ERROR("Too many links in '%s'", fullpath);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ buf = malloc(PATH_MAX);
|
|
+ if (!buf) {
|
|
+ ERROR("Out of memory");
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ n = readlink(rpath, buf, PATH_MAX - 1);
|
|
+ if (n < 0) {
|
|
+ free(buf);
|
|
+ goto error;
|
|
+ }
|
|
+ buf[n] = '\0';
|
|
+
|
|
+ if (!extra_buf) {
|
|
+ extra_buf = malloc(PATH_MAX);
|
|
+ if (!extra_buf) {
|
|
+ ERROR("Out of memory");
|
|
+ free(buf);
|
|
+ goto error;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ len = strlen(end);
|
|
+ if ((long int)(n + len) >= PATH_MAX) {
|
|
+ free(buf);
|
|
+ ERROR("Path is too long");
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Careful here, end may be a pointer into extra_buf... */
|
|
+ memmove(&extra_buf[n], end, len + 1);
|
|
+ fullpath = end = memcpy(extra_buf, buf, n);
|
|
+
|
|
+ if (IS_ABSOLUTE_FILE_NAME(buf)) {
|
|
+ if (prefix_len)
|
|
+ memcpy(rpath, prefix, prefix_len);
|
|
+ dest = rpath + prefix_len;
|
|
+ *dest++ = '/'; /* It's an absolute symlink */
|
|
+ } else {
|
|
+ /* Back up to previous component, ignore if at root
|
|
+ already: */
|
|
+ if (dest > rpath + prefix_len + 1)
|
|
+ for (--dest; dest > rpath && !ISSLASH(dest[-1]); --dest)
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if (dest > rpath + prefix_len + 1 && ISSLASH(dest[-1]))
|
|
+ --dest;
|
|
+ *dest = '\0';
|
|
+
|
|
+ if (extra_buf)
|
|
+ free(extra_buf);
|
|
+
|
|
+ return rpath;
|
|
+
|
|
+error:
|
|
+ if (extra_buf)
|
|
+ free(extra_buf);
|
|
+ free(rpath);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+// FollowSymlinkInScope is a wrapper around evalSymlinksInScope that returns an
|
|
+// absolute path. This function handles paths in a platform-agnostic manner.
|
|
+char *follow_symlink_in_scope(const char *fullpath, const char *rootpath)
|
|
+{
|
|
+ char resfull[PATH_MAX] = {0}, *full = NULL;
|
|
+ char resroot[PATH_MAX] = {0}, *root = NULL;
|
|
+
|
|
+ full = cleanpath(fullpath, resfull);
|
|
+ if (!full) {
|
|
+ ERROR("Failed to get cleaned path");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ root = cleanpath(rootpath, resroot);
|
|
+ if (!root) {
|
|
+ ERROR("Failed to get cleaned path");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return eval_symlinks_in_scope(full, root);
|
|
+}
|
|
+
|
|
+// GetResourcePath evaluates `path` in the scope of the container's rootpath, with proper path
|
|
+// sanitisation. Symlinks are all scoped to the rootpath of the container, as
|
|
+// though the container's rootpath was `/`.
|
|
+//
|
|
+// The BaseFS of a container is the host-facing path which is bind-mounted as
|
|
+// `/` inside the container. This method is essentially used to access a
|
|
+// particular path inside the container as though you were a process in that
|
|
+// container.
|
|
+int get_resource_path(const char *rootpath, const char *path,
|
|
+ char **scopepath)
|
|
+{
|
|
+ char resolved[PATH_MAX] = {0}, *cleanedpath = NULL;
|
|
+ char *fullpath = NULL;
|
|
+ size_t len;
|
|
+
|
|
+ if (!rootpath || !path || !scopepath)
|
|
+ return -1;
|
|
+
|
|
+ *scopepath = NULL;
|
|
+
|
|
+ cleanedpath = cleanpath(path, resolved);
|
|
+ if (!cleanedpath) {
|
|
+ ERROR("Failed to get cleaned path");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ len = strlen(rootpath) + strlen(cleanedpath) + 1;
|
|
+ fullpath = malloc(len);
|
|
+ if (!fullpath) {
|
|
+ ERROR("Out of memory");
|
|
+ return -1;
|
|
+ }
|
|
+ snprintf(fullpath, len, "%s%s", rootpath, cleanedpath);
|
|
+
|
|
+ *scopepath = follow_symlink_in_scope(fullpath, rootpath);
|
|
+
|
|
+ free(fullpath);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+// Rel returns a relative path that is lexically equivalent to targpath when
|
|
+// joined to basepath with an intervening separator. That is,
|
|
+// Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself.
|
|
+// On success, the returned path will always be relative to basepath,
|
|
+// even if basepath and targpath share no elements.
|
|
+// An error is returned if targpath can't be made relative to basepath or if
|
|
+// knowing the current working directory would be necessary to compute it.
|
|
+// Rel calls Clean on the result.
|
|
+char *path_relative(const char *basepath, const char *targpath)
|
|
+{
|
|
+ char resbase[PATH_MAX] = {0}, *base = NULL;
|
|
+ char restarg[PATH_MAX] = {0}, *targ = NULL;
|
|
+ size_t bl = 0, tl = 0, b0 = 0, bi = 0, t0 = 0, ti = 0;
|
|
+
|
|
+ base = cleanpath(basepath, resbase);
|
|
+ if (!base) {
|
|
+ ERROR("Failed to get cleaned path");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ targ = cleanpath(targpath, restarg);
|
|
+ if (!targ) {
|
|
+ ERROR("Failed to get cleaned path");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (strcmp(base, targ) == 0)
|
|
+ return strdup(".");
|
|
+
|
|
+ bl = strlen(base);
|
|
+ tl = strlen(targ);
|
|
+ while(true) {
|
|
+ while(bi < bl && !ISSLASH(base[bi]))
|
|
+ bi++;
|
|
+ while(ti < tl && !ISSLASH(targ[ti]))
|
|
+ ti++;
|
|
+ //not the same string
|
|
+ if (((bi - b0) != (ti - t0)) || strncmp(base + b0, targ + t0, bi - b0))
|
|
+ break;
|
|
+ if (bi < bl)
|
|
+ bi++;
|
|
+ if (ti < tl)
|
|
+ ti++;
|
|
+ b0 = bi;
|
|
+ t0 = ti;
|
|
+ }
|
|
+
|
|
+ if (b0 != bl) {
|
|
+ // Base elements left. Must go up before going down.
|
|
+ int seps = 0, i;
|
|
+ size_t ncopyed = 0, seps_size;
|
|
+ char *buf;
|
|
+
|
|
+ for (bi = b0; bi < bl; bi++) {
|
|
+ if (ISSLASH(base[bi]))
|
|
+ seps++;
|
|
+ }
|
|
+ //strlen(..) + strlen(/..) + '\0'
|
|
+ seps_size = 2 + seps * 3 + 1;
|
|
+ if (t0 != tl)
|
|
+ seps_size += 1 + tl - t0;
|
|
+
|
|
+ buf = calloc(seps_size, 1);
|
|
+ if (!buf) {
|
|
+ ERROR("Out of memory");
|
|
+ return NULL;
|
|
+ }
|
|
+ buf[ncopyed++] = '.';
|
|
+ buf[ncopyed++] = '.';
|
|
+ for (i = 0; i < seps; i++) {
|
|
+ buf[ncopyed++] = '/';
|
|
+ buf[ncopyed++] = '.';
|
|
+ buf[ncopyed++] = '.';
|
|
+ }
|
|
+ if (t0 != tl) {
|
|
+ buf[ncopyed++] = '/';
|
|
+ memcpy(buf + ncopyed, targ + t0, tl - t0 + 1);
|
|
+ }
|
|
+ return buf;
|
|
+ }
|
|
+
|
|
+ return strdup(targ + t0);
|
|
+}
|
|
\ No newline at end of file
|
|
diff --git a/src/lxc/path.h b/src/lxc/path.h
|
|
new file mode 100644
|
|
index 0000000..e3a04cc
|
|
--- /dev/null
|
|
+++ b/src/lxc/path.h
|
|
@@ -0,0 +1,70 @@
|
|
+#ifndef __LCRD_PATH_H_
|
|
+#define __LCRD_PATH_H_
|
|
+
|
|
+#include <stdbool.h>
|
|
+
|
|
+bool specify_current_dir(const char *path);
|
|
+
|
|
+bool has_traling_path_separator(const char *path);
|
|
+
|
|
+// PreserveTrailingDotOrSeparator returns the given cleaned path
|
|
+// and appends a trailing `/.` or `/` if its corresponding original
|
|
+// path ends with a trailing `/.` or `/`. If the cleaned
|
|
+// path already ends in a `.` path segment, then another is not added. If the
|
|
+// clean path already ends in a path separator, then another is not added.
|
|
+char *preserve_trailing_dot_or_separator(const char *cleanedpath,
|
|
+ const char *originalpath);
|
|
+
|
|
+
|
|
+// Split splits path immediately following the final Separator,
|
|
+// separating it into a directory and file name component.
|
|
+// If there is no Separator in path, Split returns an empty dir
|
|
+// and file set to path.
|
|
+// The returned values have the property that path = dir+file.
|
|
+bool filepath_split(const char *path, char **dir, char **base);
|
|
+
|
|
+/*
|
|
+ * cleanpath is similar to realpath of glibc, but not expands symbolic links,
|
|
+ * and not check the existence of components of the path.
|
|
+ */
|
|
+char *cleanpath(const char *path, char *resolved);
|
|
+
|
|
+// evalSymlinksInScope will evaluate symlinks in `path` within a scope `root` and return
|
|
+// a result guaranteed to be contained within the scope `root`, at the time of the call.
|
|
+// Symlinks in `root` are not evaluated and left as-is.
|
|
+// Errors encountered while attempting to evaluate symlinks in path will be returned.
|
|
+// Non-existing paths are valid and do not constitute an error.
|
|
+// `path` has to contain `root` as a prefix, or else an error will be returned.
|
|
+// Trying to break out from `root` does not constitute an error.
|
|
+//
|
|
+// Example:
|
|
+// If /foo/bar -> /outside,
|
|
+// FollowSymlinkInScope("/foo/bar", "/foo") == "/foo/outside" instead of "/oustide"
|
|
+char *eval_symlinks_in_scope(const char *fullpath, const char *rootpath);
|
|
+
|
|
+// FollowSymlinkInScope is a wrapper around evalSymlinksInScope that returns an
|
|
+// absolute path. This function handles paths in a platform-agnostic manner.
|
|
+char *follow_symlink_in_scope(const char *fullpath, const char *rootpath);
|
|
+
|
|
+// GetResourcePath evaluates `path` in the scope of the container's rootpath, with proper path
|
|
+// sanitisation. Symlinks are all scoped to the rootpath of the container, as
|
|
+// though the container's rootpath was `/`.
|
|
+//
|
|
+// The BaseFS of a container is the host-facing path which is bind-mounted as
|
|
+// `/` inside the container. This method is essentially used to access a
|
|
+// particular path inside the container as though you were a process in that
|
|
+// container.
|
|
+int get_resource_path(const char *rootpath, const char *path,
|
|
+ char **scopepath);
|
|
+
|
|
+// Rel returns a relative path that is lexically equivalent to targpath when
|
|
+// joined to basepath with an intervening separator. That is,
|
|
+// Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself.
|
|
+// On success, the returned path will always be relative to basepath,
|
|
+// even if basepath and targpath share no elements.
|
|
+// An error is returned if targpath can't be made relative to basepath or if
|
|
+// knowing the current working directory would be necessary to compute it.
|
|
+// Rel calls Clean on the result.
|
|
+char *path_relative(const char *basepath, const char *targpath);
|
|
+
|
|
+#endif
|
|
--
|
|
1.8.3.1
|
|
|