diff --git a/Makefile-bwrap.am b/Makefile-bwrap.am index aa97bd17..e4d993cb 100644 --- a/Makefile-bwrap.am +++ b/Makefile-bwrap.am @@ -6,6 +6,14 @@ bwrap_SOURCES = \ $(bwrap_srcpath)/network.c \ $(bwrap_srcpath)/utils.h \ $(bwrap_srcpath)/utils.c \ + $(bwrap-srcpath)/parse-mountinfo.h \ + $(bwrap-srcpath)/parse-mountinfo.c \ + $(bwrap-srcpath)/data-structures/destinations-graph.h \ + $(bwrap-srcpath)/data-structures/destinations-graph-ops.c \ + $(bwrap-srcpath)/data-structures/destinations-graph-ops-linked-list.c \ + $(bwrap-srcpath)/data-structures/destinations-graph-ops-node.c \ + $(bwrap-srcpath)/data-structures/lazy-segment-tree.h \ + $(bwrap-srcpath)/data-structures/lazy-segment-tree.c \ $(NULL) bwrap_CFLAGS = $(AM_CFLAGS) diff --git a/bind-mount.c b/bind-mount.c index 2757caea..39a0cd48 100644 --- a/bind-mount.c +++ b/bind-mount.c @@ -17,390 +17,24 @@ * */ -#include "config.h" - #include #include "utils.h" #include "bind-mount.h" +#include "data-structures/destinations-graph.h" -static char * -skip_token (char *line, bool eat_whitespace) -{ - while (*line != ' ' && *line != '\n') - line++; - - if (eat_whitespace && *line == ' ') - line++; - - return line; -} - -static char * -unescape_inline (char *escaped) -{ - char *unescaped, *res; - const char *end; - - res = escaped; - end = escaped + strlen (escaped); - - unescaped = escaped; - while (escaped < end) - { - if (*escaped == '\\') - { - *unescaped++ = - ((escaped[1] - '0') << 6) | - ((escaped[2] - '0') << 3) | - ((escaped[3] - '0') << 0); - escaped += 4; - } - else - { - *unescaped++ = *escaped++; - } - } - *unescaped = 0; - return res; -} - -static bool -match_token (const char *token, const char *token_end, const char *str) -{ - while (token != token_end && *token == *str) - { - token++; - str++; - } - if (token == token_end) - return *str == 0; - - return FALSE; -} - -static unsigned long -decode_mountoptions (const char *options) -{ - const char *token, *end_token; - int i; - unsigned long flags = 0; - static const struct { int flag; - const char *name; - } flags_data[] = { - { 0, "rw" }, - { MS_RDONLY, "ro" }, - { MS_NOSUID, "nosuid" }, - { MS_NODEV, "nodev" }, - { MS_NOEXEC, "noexec" }, - { MS_NOATIME, "noatime" }, - { MS_NODIRATIME, "nodiratime" }, - { MS_RELATIME, "relatime" }, - { 0, NULL } - }; - - token = options; - do - { - end_token = strchr (token, ','); - if (end_token == NULL) - end_token = token + strlen (token); - - for (i = 0; flags_data[i].name != NULL; i++) - { - if (match_token (token, end_token, flags_data[i].name)) - { - flags |= flags_data[i].flag; - break; - } - } - - if (*end_token != 0) - token = end_token + 1; - else - token = NULL; - } - while (token != NULL); - - return flags; -} - -typedef struct MountInfo MountInfo; -struct MountInfo { - char *mountpoint; - unsigned long options; -}; - -typedef MountInfo *MountTab; - -static void -mount_tab_free (MountTab tab) -{ - int i; - - for (i = 0; tab[i].mountpoint != NULL; i++) - free (tab[i].mountpoint); - free (tab); -} - -static inline void -cleanup_mount_tabp (void *p) -{ - void **pp = (void **) p; - - if (*pp) - mount_tab_free ((MountTab)*pp); -} - -#define cleanup_mount_tab __attribute__((cleanup (cleanup_mount_tabp))) - -typedef struct MountInfoLine MountInfoLine; -struct MountInfoLine { - const char *mountpoint; - const char *options; - bool covered; - int id; - int parent_id; - MountInfoLine *first_child; - MountInfoLine *next_sibling; -}; - -static unsigned int -count_lines (const char *data) -{ - unsigned int count = 0; - const char *p = data; - - while (*p != 0) - { - if (*p == '\n') - count++; - p++; - } - - /* If missing final newline, add one */ - if (p > data && *(p-1) != '\n') - count++; - - return count; -} - -static int -count_mounts (MountInfoLine *line) -{ - MountInfoLine *child; - int res = 0; - - if (!line->covered) - res += 1; - - child = line->first_child; - while (child != NULL) - { - res += count_mounts (child); - child = child->next_sibling; - } - - return res; -} - -static MountInfo * -collect_mounts (MountInfo *info, MountInfoLine *line) -{ - MountInfoLine *child; - - if (!line->covered) - { - info->mountpoint = xstrdup (line->mountpoint); - info->options = decode_mountoptions (line->options); - info ++; - } - - child = line->first_child; - while (child != NULL) - { - info = collect_mounts (info, child); - child = child->next_sibling; - } - - return info; -} - -static MountTab -parse_mountinfo (int proc_fd, - const char *root_mount) +static bind_mount_result +retrieve_kernel_case (char *dest, char **result, char **failing_path) { - cleanup_free char *mountinfo = NULL; - cleanup_free MountInfoLine *lines = NULL; - cleanup_free MountInfoLine **by_id = NULL; - cleanup_mount_tab MountTab mount_tab = NULL; - MountInfo *end_tab; - int n_mounts; - char *line; - unsigned int i; - int max_id; - unsigned int n_lines; - int root; - - mountinfo = load_file_at (proc_fd, "self/mountinfo"); - if (mountinfo == NULL) - die_with_error ("Can't open /proc/self/mountinfo"); - - n_lines = count_lines (mountinfo); - lines = xcalloc (n_lines, sizeof (MountInfoLine)); - - max_id = 0; - line = mountinfo; - i = 0; - root = -1; - while (*line != 0) - { - int rc, consumed = 0; - unsigned int maj, min; - char *end; - char *rest; - char *mountpoint; - char *mountpoint_end; - char *options; - char *options_end; - char *next_line; - - assert (i < n_lines); - - end = strchr (line, '\n'); - if (end != NULL) - { - *end = 0; - next_line = end + 1; - } - else - next_line = line + strlen (line); - - rc = sscanf (line, "%d %d %u:%u %n", &lines[i].id, &lines[i].parent_id, &maj, &min, &consumed); - if (rc != 4) - die ("Can't parse mountinfo line"); - rest = line + consumed; - - rest = skip_token (rest, TRUE); /* mountroot */ - mountpoint = rest; - rest = skip_token (rest, FALSE); /* mountpoint */ - mountpoint_end = rest++; - options = rest; - rest = skip_token (rest, FALSE); /* vfs options */ - options_end = rest; - - *mountpoint_end = 0; - lines[i].mountpoint = unescape_inline (mountpoint); - - *options_end = 0; - lines[i].options = options; - - if (lines[i].id > max_id) - max_id = lines[i].id; - if (lines[i].parent_id > max_id) - max_id = lines[i].parent_id; - - if (path_equal (lines[i].mountpoint, root_mount)) - root = i; - - i++; - line = next_line; - } - assert (i == n_lines); - - if (root == -1) - { - mount_tab = xcalloc (1, sizeof (MountInfo)); - return steal_pointer (&mount_tab); - } - - by_id = xcalloc (max_id + 1, sizeof (MountInfoLine*)); - for (i = 0; i < n_lines; i++) - by_id[lines[i].id] = &lines[i]; - - for (i = 0; i < n_lines; i++) - { - MountInfoLine *this = &lines[i]; - MountInfoLine *parent = by_id[this->parent_id]; - MountInfoLine **to_sibling; - MountInfoLine *sibling; - bool covered = FALSE; - - if (!has_path_prefix (this->mountpoint, root_mount)) - continue; - - if (parent == NULL) - continue; - - if (strcmp (parent->mountpoint, this->mountpoint) == 0) - parent->covered = TRUE; - - to_sibling = &parent->first_child; - sibling = parent->first_child; - while (sibling != NULL) - { - /* If this mountpoint is a path prefix of the sibling, - * say this->mp=/foo/bar and sibling->mp=/foo, then it is - * covered by the sibling, and we drop it. */ - if (has_path_prefix (this->mountpoint, sibling->mountpoint)) - { - covered = TRUE; - break; - } - - /* If the sibling is a path prefix of this mount point, - * say this->mp=/foo and sibling->mp=/foo/bar, then the sibling - * is covered, and we drop it. - */ - if (has_path_prefix (sibling->mountpoint, this->mountpoint)) - *to_sibling = sibling->next_sibling; - else - to_sibling = &sibling->next_sibling; - sibling = sibling->next_sibling; - } - - if (covered) - continue; - - *to_sibling = this; - } - - n_mounts = count_mounts (&lines[root]); - mount_tab = xcalloc (n_mounts + 1, sizeof (MountInfo)); - - end_tab = collect_mounts (&mount_tab[0], &lines[root]); - assert (end_tab == &mount_tab[n_mounts]); - - return steal_pointer (&mount_tab); -} - -bind_mount_result -bind_mount (int proc_fd, - const char *src, - const char *dest, - bind_option_t options, - char **failing_path) -{ - bool readonly = (options & BIND_READONLY) != 0; - bool devices = (options & BIND_DEVICES) != 0; - bool recursive = (options & BIND_RECURSIVE) != 0; - unsigned long current_flags, new_flags; - cleanup_mount_tab MountTab mount_tab = NULL; cleanup_free char *resolved_dest = NULL; cleanup_free char *dest_proc = NULL; cleanup_free char *oldroot_dest_proc = NULL; cleanup_free char *kernel_case_combination = NULL; cleanup_fd int dest_fd = -1; - int i; - if (src) - { - if (mount (src, dest, NULL, MS_SILENT | MS_BIND | (recursive ? MS_REC : 0), NULL) != 0) - return BIND_MOUNT_ERROR_MOUNT; - } + // The mount operation will resolve any symlinks in the destination + // path, so to find it in the mount table we need to do that too. - /* The mount operation will resolve any symlinks in the destination - path, so to find it in the mount table we need to do that too. */ resolved_dest = realpath (dest, NULL); if (resolved_dest == NULL) return BIND_MOUNT_ERROR_REALPATH_DEST; @@ -424,67 +58,156 @@ bind_mount (int proc_fd, * instead of the provided @root_mount, so that we can compare the mountinfo * entries with the same case combination that the kernel is expected to * use. */ + dest_proc = xasprintf ("/proc/self/fd/%d", dest_fd); oldroot_dest_proc = get_oldroot_path (dest_proc); + kernel_case_combination = readlink_malloc (oldroot_dest_proc); + if (kernel_case_combination == NULL) { if (failing_path != NULL) *failing_path = steal_pointer (&resolved_dest); + free(kernel_case_combination); return BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD; } - mount_tab = parse_mountinfo (proc_fd, kernel_case_combination); - if (mount_tab[0].mountpoint == NULL) - { - if (failing_path != NULL) - *failing_path = steal_pointer (&kernel_case_combination); + *result = steal_pointer(&kernel_case_combination); + return BIND_MOUNT_SUCCESS; +} - errno = EINVAL; - return BIND_MOUNT_ERROR_FIND_DEST_MOUNT; +bind_mount_result +bind_mount_fixup (int proc_fd, BindOp *bind_ops, size_t bind_ops_quantity, char **failing_path) +{ + // The old bind_mount() contains note while recursive remount binds: + // + // | We need to work around the fact that a bind mount does not apply the flags, so we need to manually + // | apply the flags to all submounts in the recursive case. + // | Note: This does not apply the flags to mounts which are later propagated into this namespace. + // + // With current fixup function this behaviour approaches with virtual propagation of mount flags + // "Virtual" here means that we store all the information inside graph data structure + // Here this data structure is an object of optimisation + // + // In this way first we just bind mount all the mount points and in the end + // just remount everything with correct flags. That is, we perform fix up of mounts + + cleanup_destinations_graph DestinationsGraph *graph; + cleanup_mount_tab MountTab mount_tab; +// Here clang-tidy says allocated memory is leaked. It's not true, because of cleanup attribute usage +// At least it seems so. + cleanup_free DestinationsGraph_Node **mount_points = xcalloc (bind_ops_quantity, sizeof (DestinationsGraph_Node *)); + BindOp *bop; + size_t current; + + __debug__("Performing bind mounting fix up:\n\n"); + + // (0) Prepare + graph = DestinationsGraph_create (); + mount_tab = parse_mountinfo (proc_fd, "/newroot"); + + // (1) Collect all information about actual mounts to the graph + for (int i = 0; mount_tab[i].mountpoint != NULL; i++) + { + __debug__("(Initial) Mountinfo: %s (flags %lu)\n", mount_tab[i].mountpoint, mount_tab[i].options); + DestinationsGraph_ensure_mount_point (graph, &mount_tab[i].mountpoint, NULL); } - assert (path_equal (mount_tab[0].mountpoint, kernel_case_combination)); - current_flags = mount_tab[0].options; - new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0); - if (new_flags != current_flags && - mount ("none", resolved_dest, - NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0) + // (2) Initialize flags system. + // 1. Performs Euler tour by graph to map each node to the index in segment tree + // 2. And initializes segment trees for each of the flag + DestinationsGraph_Flags_init (graph); + + // (3) Collect all nodes that correspond bind mount operations + // (4) Lazy propagate desired flags in the graph + for (bop = bind_ops, current = 0; bop != NULL; bop = bop->next, current++) { - if (failing_path != NULL) - *failing_path = steal_pointer (&resolved_dest); + __debug__("BindOp: %s (readonly %d, nodev %d)\n", + bop->dest, + (bop->options & BIND_READONLY) != 0, + (bop->options & BIND_DEVICES) != 0 + ); + + // Retrieve real path after mounting + cleanup_free char *kernel_case = NULL; + bind_mount_result result = retrieve_kernel_case (bop->dest, &kernel_case, failing_path); + + if (result != BIND_MOUNT_SUCCESS) + return result; + + // Get corresponding destinations graph node + // Also check if node was already added on (1); if not - we're screwed up + bool added = FALSE; + mount_points[current] = DestinationsGraph_ensure_mount_point (graph, &kernel_case, &added); + if(added) + { + if(failing_path != NULL) + *failing_path = steal_pointer(&kernel_case); + return BIND_MOUNT_ERROR_FIND_DEST_MOUNT; + } + assert(added == FALSE); + + // Lazy propagate flags specified in bind operation + bool readonly = (bop->options & BIND_READONLY) != 0; + bool devices = (bop->options & BIND_DEVICES) != 0; - return BIND_MOUNT_ERROR_REMOUNT_DEST; + DestinationsGraph_Flags_set_readonly (graph, mount_points[current], readonly); + DestinationsGraph_Flags_set_nodev (graph, mount_points[current], !devices); } - /* We need to work around the fact that a bind mount does not apply the flags, so we need to manually - * apply the flags to all submounts in the recursive case. - * Note: This does not apply the flags to mounts which are later propagated into this namespace. - */ - if (recursive) +#ifdef DEBUG + __debug__("Destinations graph: \n"); + DestinationsGraph_debug_pretty_print (graph, stderr); +#endif + + // (5) Go through all actual mountpoints and remount all of them with correct desired flags + for (int i = 0; mount_tab[i].mountpoint != NULL; i++) { - for (i = 1; mount_tab[i].mountpoint != NULL; i++) + char *mount_point = mount_tab[i].mountpoint; + + bool added = FALSE; + DestinationsGraph_Node *node = DestinationsGraph_ensure_mount_point (graph, &mount_point, &added); + // Here we shouldn't even return any error. Because it shouldn't happen ever. + // In case it did I'm screwed up. + assert(added == FALSE); + + bool readonly = DestinationsGraph_Flags_check_readonly (graph, node); + bool devices = !DestinationsGraph_Flags_check_nodev (graph, node); + + unsigned long current_flags, new_flags; + current_flags = mount_tab[i].options; + new_flags = current_flags | MS_NOSUID | (devices ? 0 : MS_NODEV) | (readonly ? MS_RDONLY : 0); + + if (new_flags != current_flags) { - current_flags = mount_tab[i].options; - new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0); - if (new_flags != current_flags && - mount ("none", mount_tab[i].mountpoint, - NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0) + __debug__("Remount: %s (readonly %d, nodev %d) (old flags %lu | new flags %lu)\n", + mount_point, + readonly, + !devices, + current_flags, + new_flags + ); + + int mount_result = mount ("none", mount_point, NULL, + MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL); + + /* If we can't read the mountpoint we can't remount it, but that should + be safe to ignore because it's not something the user can access. */ + if (mount_result != 0 && errno != EACCES) { - /* If we can't read the mountpoint we can't remount it, but that should - be safe to ignore because its not something the user can access. */ - if (errno != EACCES) - { - if (failing_path != NULL) - *failing_path = xstrdup (mount_tab[i].mountpoint); - - return BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT; - } + if (failing_path != NULL) *failing_path = steal_pointer (&mount_point); + return BIND_MOUNT_ERROR_REMOUNT; } } } +#ifdef DEBUG + mount_tab = parse_mountinfo (proc_fd, "/newroot"); + for (int i = 0; mount_tab[i].mountpoint != NULL; i++) + __debug__("(Final) Mountinfo: %s (flags %lu)\n", mount_tab[i].mountpoint, mount_tab[i].options); +#endif + return BIND_MOUNT_SUCCESS; } @@ -505,42 +228,37 @@ bind_mount_result_to_string (bind_mount_result res, { case BIND_MOUNT_ERROR_MOUNT: string = xstrdup ("Unable to mount source on destination"); - break; + break; case BIND_MOUNT_ERROR_REALPATH_DEST: string = xstrdup ("realpath(destination)"); - break; + break; case BIND_MOUNT_ERROR_REOPEN_DEST: string = xasprintf ("open(\"%s\", O_PATH)", failing_path); - break; + break; case BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD: string = xasprintf ("readlink(/proc/self/fd/N) for \"%s\"", failing_path); - break; + break; case BIND_MOUNT_ERROR_FIND_DEST_MOUNT: string = xasprintf ("Unable to find \"%s\" in mount table", failing_path); - want_errno = FALSE; - break; + want_errno = FALSE; + break; - case BIND_MOUNT_ERROR_REMOUNT_DEST: - string = xasprintf ("Unable to remount destination \"%s\" with correct flags", - failing_path); - break; - - case BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT: + case BIND_MOUNT_ERROR_REMOUNT: string = xasprintf ("Unable to apply mount flags: remount \"%s\"", failing_path); - break; + break; case BIND_MOUNT_SUCCESS: string = xstrdup ("Success"); - break; + break; default: string = xstrdup ("(unknown/invalid bind_mount_result)"); - break; + break; } if (want_errno_p != NULL) @@ -551,9 +269,9 @@ bind_mount_result_to_string (bind_mount_result res, void die_with_bind_result (bind_mount_result res, - int saved_errno, - const char *failing_path, - const char *format, + int saved_errno, + const char *failing_path, + const char *format, ...) { va_list args; @@ -575,15 +293,14 @@ die_with_bind_result (bind_mount_result res, switch (res) { case BIND_MOUNT_ERROR_MOUNT: - case BIND_MOUNT_ERROR_REMOUNT_DEST: - case BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT: + case BIND_MOUNT_ERROR_REMOUNT: + case BIND_MOUNT_ERROR_FIND_DEST_MOUNT: fprintf (stderr, ": %s", mount_strerror (saved_errno)); - break; + break; case BIND_MOUNT_ERROR_REALPATH_DEST: case BIND_MOUNT_ERROR_REOPEN_DEST: case BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD: - case BIND_MOUNT_ERROR_FIND_DEST_MOUNT: case BIND_MOUNT_SUCCESS: default: fprintf (stderr, ": %s", strerror (saved_errno)); diff --git a/bind-mount.h b/bind-mount.h index 8a361fbd..b0373632 100644 --- a/bind-mount.h +++ b/bind-mount.h @@ -21,34 +21,44 @@ #include "utils.h" +#include "parse-mountinfo.h" + +/// -------------------------------------------------------------------------------------------------------------------- +/// Type definitions + typedef enum { - BIND_READONLY = (1 << 0), - BIND_DEVICES = (1 << 2), - BIND_RECURSIVE = (1 << 3), + BIND_READONLY = (1 << 0), + BIND_DEVICES = (1 << 2), } bind_option_t; -typedef enum -{ - BIND_MOUNT_SUCCESS = 0, - BIND_MOUNT_ERROR_MOUNT, - BIND_MOUNT_ERROR_REALPATH_DEST, - BIND_MOUNT_ERROR_REOPEN_DEST, - BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD, - BIND_MOUNT_ERROR_FIND_DEST_MOUNT, - BIND_MOUNT_ERROR_REMOUNT_DEST, - BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT, +typedef enum { + BIND_MOUNT_SUCCESS = 0, + BIND_MOUNT_ERROR_MOUNT, + BIND_MOUNT_ERROR_REALPATH_DEST, + BIND_MOUNT_ERROR_REOPEN_DEST, + BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD, + BIND_MOUNT_ERROR_FIND_DEST_MOUNT, + BIND_MOUNT_ERROR_REMOUNT, } bind_mount_result; -bind_mount_result bind_mount (int proc_fd, - const char *src, - const char *dest, - bind_option_t options, - char **failing_path); +typedef struct _BindOp BindOp; + +struct _BindOp { + char *dest; + bind_option_t options; + BindOp *next; +}; + +/// -------------------------------------------------------------------------------------------------------------------- +/// Functions + +bind_mount_result +bind_mount_fixup (int proc_fd, BindOp *bind_ops, size_t bind_ops_quantity, char **failing_path); void die_with_bind_result (bind_mount_result res, - int saved_errno, - const char *failing_path, - const char *format, + int saved_errno, + const char *failing_path, + const char *format, ...) - __attribute__((__noreturn__)) - __attribute__((format (printf, 4, 5))); +__attribute__((__noreturn__)) +__attribute__((format (printf, 4, 5))); diff --git a/bubblewrap.c b/bubblewrap.c index 9b78a9ae..842762d3 100644 --- a/bubblewrap.c +++ b/bubblewrap.c @@ -109,89 +109,96 @@ static size_t next_size_arg = 0; typedef struct _NsInfo NsInfo; struct _NsInfo { - const char *name; - bool *do_unshare; - ino_t id; + const char *name; + bool *do_unshare; + ino_t id; }; static NsInfo ns_infos[] = { - {"cgroup", &opt_unshare_cgroup, 0}, - {"ipc", &opt_unshare_ipc, 0}, - {"mnt", NULL, 0}, - {"net", &opt_unshare_net, 0}, - {"pid", &opt_unshare_pid, 0}, - /* user namespace info omitted because it - * is not (yet) valid when we obtain the - * namespace info (get un-shared later) */ - {"uts", &opt_unshare_uts, 0}, - {NULL, NULL, 0} + {"cgroup", &opt_unshare_cgroup, 0}, + {"ipc", &opt_unshare_ipc, 0}, + {"mnt", NULL, 0}, + {"net", &opt_unshare_net, 0}, + {"pid", &opt_unshare_pid, 0}, + /* user namespace info omitted because it + * is not (yet) valid when we obtain the + * namespace info (get un-shared later) */ + {"uts", &opt_unshare_uts, 0}, + {NULL, NULL, 0} }; typedef enum { - SETUP_BIND_MOUNT, - SETUP_RO_BIND_MOUNT, - SETUP_DEV_BIND_MOUNT, - SETUP_MOUNT_PROC, - SETUP_MOUNT_DEV, - SETUP_MOUNT_TMPFS, - SETUP_MOUNT_MQUEUE, - SETUP_MAKE_DIR, - SETUP_MAKE_FILE, - SETUP_MAKE_BIND_FILE, - SETUP_MAKE_RO_BIND_FILE, - SETUP_MAKE_SYMLINK, - SETUP_REMOUNT_RO_NO_RECURSIVE, - SETUP_SET_HOSTNAME, - SETUP_CHMOD, + SETUP_BIND_MOUNT, + SETUP_RO_BIND_MOUNT, + SETUP_DEV_BIND_MOUNT, + SETUP_MOUNT_PROC, + SETUP_MOUNT_DEV, + SETUP_MOUNT_TMPFS, + SETUP_MOUNT_MQUEUE, + SETUP_MAKE_DIR, + SETUP_MAKE_FILE, + SETUP_MAKE_BIND_FILE, + SETUP_MAKE_RO_BIND_FILE, + SETUP_MAKE_SYMLINK, + SETUP_REMOUNT_RO_NO_RECURSIVE, + SETUP_SET_HOSTNAME, + SETUP_CHMOD, } SetupOpType; typedef enum { - NO_CREATE_DEST = (1 << 0), - ALLOW_NOTEXIST = (2 << 0), + NO_CREATE_DEST = (1 << 0), + ALLOW_NOTEXIST = (2 << 0), } SetupOpFlag; typedef struct _SetupOp SetupOp; struct _SetupOp { - SetupOpType type; - const char *source; - const char *dest; - int fd; - SetupOpFlag flags; - int perms; - size_t size; /* number of bytes, zero means unset/default */ - SetupOp *next; + SetupOpType type; + const char *source; + const char *dest; + int fd; + SetupOpFlag flags; + int perms; + size_t size; /* number of bytes, zero means unset/default */ + SetupOp *next; }; typedef struct _LockFile LockFile; struct _LockFile { - const char *path; - int fd; - LockFile *next; + const char *path; + int fd; + LockFile *next; +}; + +typedef struct _TempFile TempFile; + +struct _TempFile { + const char *dest; + TempFile *next; }; enum { - PRIV_SEP_OP_DONE, - PRIV_SEP_OP_BIND_MOUNT, - PRIV_SEP_OP_PROC_MOUNT, - PRIV_SEP_OP_TMPFS_MOUNT, - PRIV_SEP_OP_DEVPTS_MOUNT, - PRIV_SEP_OP_MQUEUE_MOUNT, - PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE, - PRIV_SEP_OP_SET_HOSTNAME, + PRIV_SEP_OP_DONE, + PRIV_SEP_OP_BIND_MOUNT, + PRIV_SEP_OP_PROC_MOUNT, + PRIV_SEP_OP_TMPFS_MOUNT, + PRIV_SEP_OP_DEVPTS_MOUNT, + PRIV_SEP_OP_MQUEUE_MOUNT, + PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE, + PRIV_SEP_OP_SET_HOSTNAME, }; typedef struct { - uint32_t op; - uint32_t flags; - uint32_t perms; - size_t size_arg; - uint32_t arg1_offset; - uint32_t arg2_offset; + uint32_t op; + uint32_t flags; + uint32_t perms; + size_t size_arg; + uint32_t arg1_offset; + uint32_t arg2_offset; } PrivSepOp; /* @@ -224,6 +231,23 @@ _ ## name ## _append_new (void) \ return self; \ } +size_t bind_ops_quantity = 0; +DEFINE_LINKED_LIST (BindOp, bind_op) + +static BindOp * +bind_op_new (char *dest, bind_option_t options) +{ + BindOp *bop = _bind_op_append_new (); + bind_ops_quantity++; + + bop->dest = xstrdup (dest); + bop->options = options; + + return bop; +} + +DEFINE_LINKED_LIST (TempFile, temp_file) + DEFINE_LINKED_LIST (SetupOp, op) static SetupOp * @@ -252,8 +276,8 @@ typedef struct _SeccompProgram SeccompProgram; struct _SeccompProgram { - struct sock_fprog program; - SeccompProgram *next; + struct sock_fprog program; + SeccompProgram *next; }; DEFINE_LINKED_LIST (SeccompProgram, seccomp_program) @@ -367,7 +391,7 @@ usage (int ecode, FILE *out) " --perms OCTAL Set permissions of next argument (--bind-data, --file, etc.)\n" " --size BYTES Set size of next argument (only for --tmpfs)\n" " --chmod OCTAL PATH Change permissions of PATH (must already exist)\n" - ); + ); exit (ecode); } @@ -601,10 +625,10 @@ do_init (int event_fd, pid_t initial_pid) die_with_error ("Unable to open lock file %s", lock->path); struct flock l = { - .l_type = F_RDLCK, - .l_whence = SEEK_SET, - .l_start = 0, - .l_len = 0 + .l_type = F_RDLCK, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 0 }; if (fcntl (fd, F_SETLK, &l) < 0) @@ -1086,78 +1110,102 @@ privileged_op (int privileged_op_socket, */ switch (op) { - case PRIV_SEP_OP_DONE: - break; - - case PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE: - bind_result = bind_mount (proc_fd, NULL, arg2, BIND_READONLY, &failing_path); + case PRIV_SEP_OP_DONE: + bind_result = bind_mount_fixup (proc_fd, bind_ops, bind_ops_quantity, &failing_path); if (bind_result != BIND_MOUNT_SUCCESS) die_with_bind_result (bind_result, errno, failing_path, - "Can't remount readonly on %s", arg2); + "Setting the correct mount flags up (fixup) failed" + ); + + assert(failing_path == NULL); + + /* Remove files so we're sure the app can't get to it in any other way. + It's outside the container chroot, so it shouldn't be possible, but lets + make it really sure. */ + + for (TempFile *temp_file = temp_files; temp_file != NULL; temp_file = temp_file->next) + unlink (temp_file->dest); + + break; - assert (failing_path == NULL); /* otherwise we would have died */ + case PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE: + bind_op_new ((char *) arg2, BIND_READONLY); break; - case PRIV_SEP_OP_BIND_MOUNT: - /* We always bind directories recursively, otherwise this would let us - access files that are otherwise covered on the host */ - bind_result = bind_mount (proc_fd, arg1, arg2, BIND_RECURSIVE | flags, &failing_path); + case PRIV_SEP_OP_BIND_MOUNT: - if (bind_result != BIND_MOUNT_SUCCESS) - die_with_bind_result (bind_result, errno, failing_path, - "Can't bind mount %s on %s", arg1, arg2); + if (mount (arg1, arg2, NULL, MS_SILENT | MS_BIND | MS_REC, NULL) != 0) + die_with_bind_result ( + BIND_MOUNT_ERROR_MOUNT, errno, arg2, + "Can't bind mount %s on %s", arg1, arg2 + ); + else + bind_op_new ((char *) arg2, flags); - assert (failing_path == NULL); /* otherwise we would have died */ break; - case PRIV_SEP_OP_PROC_MOUNT: - if (mount ("proc", arg1, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL) != 0) - die_with_mount_error ("Can't mount proc on %s", arg1); + case PRIV_SEP_OP_PROC_MOUNT: + + if (mount ("proc", arg1, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL) != 0) + die_with_mount_error ("Can't mount proc on %s", arg1); + else + bind_op_new ((char *) arg1, 0); + break; - case PRIV_SEP_OP_TMPFS_MOUNT: - { - cleanup_free char *mode = NULL; + case PRIV_SEP_OP_TMPFS_MOUNT: + { + cleanup_free char *mode = NULL; - /* This check should be unnecessary since we checked this when parsing - * the --size option as well. However, better be safe than sorry. */ - if (size_arg > MAX_TMPFS_BYTES) - die_with_error ("Specified tmpfs size too large (%zu > %zu)", size_arg, MAX_TMPFS_BYTES); + /* This check should be unnecessary since we checked this when parsing + * the --size option as well. However, better be safe than sorry. */ + if (size_arg > MAX_TMPFS_BYTES) + die_with_error ("Specified tmpfs size too large (%zu > %zu)", size_arg, MAX_TMPFS_BYTES); - if (size_arg != 0) - mode = xasprintf ("mode=%#o,size=%zu", perms, size_arg); + if (size_arg != 0) + mode = xasprintf ("mode=%#o,size=%zu", perms, size_arg); + else + mode = xasprintf ("mode=%#o", perms); + + cleanup_free char *opt = label_mount (mode, opt_file_label); + if (mount ("tmpfs", arg1, "tmpfs", MS_NOSUID | MS_NODEV, opt) != 0) + die_with_mount_error ("Can't mount tmpfs on %s", arg1); + else + bind_op_new ((char *) arg1, 0); + + break; + } + + case PRIV_SEP_OP_DEVPTS_MOUNT: + if (mount ("devpts", arg1, "devpts", MS_NOSUID | MS_NOEXEC, + "newinstance,ptmxmode=0666,mode=620") != 0) + die_with_mount_error ("Can't mount devpts on %s", arg1); else - mode = xasprintf ("mode=%#o", perms); - - cleanup_free char *opt = label_mount (mode, opt_file_label); - if (mount ("tmpfs", arg1, "tmpfs", MS_NOSUID | MS_NODEV, opt) != 0) - die_with_mount_error ("Can't mount tmpfs on %s", arg1); - break; - } - - case PRIV_SEP_OP_DEVPTS_MOUNT: - if (mount ("devpts", arg1, "devpts", MS_NOSUID | MS_NOEXEC, - "newinstance,ptmxmode=0666,mode=620") != 0) - die_with_mount_error ("Can't mount devpts on %s", arg1); + bind_op_new ((char *) arg1, BIND_DEVICES); + break; - case PRIV_SEP_OP_MQUEUE_MOUNT: - if (mount ("mqueue", arg1, "mqueue", 0, NULL) != 0) - die_with_mount_error ("Can't mount mqueue on %s", arg1); + case PRIV_SEP_OP_MQUEUE_MOUNT: + + if (mount ("mqueue", arg1, "mqueue", 0, NULL) != 0) + die_with_mount_error ("Can't mount mqueue on %s", arg1); + else + bind_op_new ((char *) arg1, BIND_DEVICES); + break; - case PRIV_SEP_OP_SET_HOSTNAME: - /* This is checked at the start, but lets verify it here in case - something manages to send hacked priv-sep operation requests. */ - if (!opt_unshare_uts) - die ("Refusing to set hostname in original namespace"); + case PRIV_SEP_OP_SET_HOSTNAME: + /* This is checked at the start, but lets verify it here in case + something manages to send hacked priv-sep operation requests. */ + if (!opt_unshare_uts) + die ("Refusing to set hostname in original namespace"); if (sethostname (arg1, strlen(arg1)) != 0) die_with_error ("Can't set hostname to %s", arg1); break; - default: - die ("Unexpected privileged op %d", op); + default: + die ("Unexpected privileged op %d", op); } } @@ -1215,16 +1263,16 @@ setup_newroot (bool unshare_pid, switch (op->type) { - case SETUP_RO_BIND_MOUNT: - case SETUP_DEV_BIND_MOUNT: - case SETUP_BIND_MOUNT: - if (source_mode == S_IFDIR) - { - if (ensure_dir (dest, 0755) != 0) - die_with_error ("Can't mkdir %s", op->dest); - } - else if (ensure_file (dest, 0444) != 0) - die_with_error ("Can't create file at %s", op->dest); + case SETUP_RO_BIND_MOUNT: + case SETUP_DEV_BIND_MOUNT: + case SETUP_BIND_MOUNT: + if (source_mode == S_IFDIR) + { + if (ensure_dir (dest, 0755) != 0) + die_with_error ("Can't mkdir %s", op->dest); + } + else if (ensure_file (dest, 0444) != 0) + die_with_error ("Can't create file at %s", op->dest); privileged_op (privileged_op_socket, PRIV_SEP_OP_BIND_MOUNT, @@ -1233,14 +1281,14 @@ setup_newroot (bool unshare_pid, 0, 0, source, dest); break; - case SETUP_REMOUNT_RO_NO_RECURSIVE: - privileged_op (privileged_op_socket, - PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE, 0, 0, 0, NULL, dest); + case SETUP_REMOUNT_RO_NO_RECURSIVE: + privileged_op (privileged_op_socket, + PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE, 0, 0, 0, NULL, dest); break; - case SETUP_MOUNT_PROC: - if (ensure_dir (dest, 0755) != 0) - die_with_error ("Can't mkdir %s", op->dest); + case SETUP_MOUNT_PROC: + if (ensure_dir (dest, 0755) != 0) + die_with_error ("Can't mkdir %s", op->dest); if (unshare_pid || opt_pidns_fd != -1) { @@ -1281,9 +1329,9 @@ setup_newroot (bool unshare_pid, break; - case SETUP_MOUNT_DEV: - if (ensure_dir (dest, 0755) != 0) - die_with_error ("Can't mkdir %s", op->dest); + case SETUP_MOUNT_DEV: + if (ensure_dir (dest, 0755) != 0) + die_with_error ("Can't mkdir %s", op->dest); privileged_op (privileged_op_socket, PRIV_SEP_OP_TMPFS_MOUNT, 0, 0755, 0, @@ -1357,8 +1405,8 @@ setup_newroot (bool unshare_pid, break; - case SETUP_MOUNT_TMPFS: - assert (dest != NULL); + case SETUP_MOUNT_TMPFS: + assert (dest != NULL); assert (op->perms >= 0); assert (op->perms <= 07777); @@ -1370,17 +1418,17 @@ setup_newroot (bool unshare_pid, dest, NULL); break; - case SETUP_MOUNT_MQUEUE: - if (ensure_dir (dest, 0755) != 0) - die_with_error ("Can't mkdir %s", op->dest); + case SETUP_MOUNT_MQUEUE: + if (ensure_dir (dest, 0755) != 0) + die_with_error ("Can't mkdir %s", op->dest); privileged_op (privileged_op_socket, PRIV_SEP_OP_MQUEUE_MOUNT, 0, 0, 0, dest, NULL); break; - case SETUP_MAKE_DIR: - assert (dest != NULL); + case SETUP_MAKE_DIR: + assert (dest != NULL); assert (op->perms >= 0); assert (op->perms <= 07777); @@ -1389,8 +1437,8 @@ setup_newroot (bool unshare_pid, break; - case SETUP_CHMOD: - assert (op->dest != NULL); + case SETUP_CHMOD: + assert (op->dest != NULL); /* We used NO_CREATE_DEST so we have to use get_newroot_path() * explicitly */ assert (dest == NULL); @@ -1404,69 +1452,70 @@ setup_newroot (bool unshare_pid, break; - case SETUP_MAKE_FILE: - { - cleanup_fd int dest_fd = -1; + case SETUP_MAKE_FILE: + { + cleanup_fd int dest_fd = -1; - assert (dest != NULL); - assert (op->perms >= 0); - assert (op->perms <= 07777); + assert (dest != NULL); + assert (op->perms >= 0); + assert (op->perms <= 07777); - dest_fd = creat (dest, op->perms); - if (dest_fd == -1) - die_with_error ("Can't create file %s", op->dest); + dest_fd = creat (dest, op->perms); + if (dest_fd == -1) + die_with_error ("Can't create file %s", op->dest); - if (copy_file_data (op->fd, dest_fd) != 0) - die_with_error ("Can't write data to file %s", op->dest); + if (copy_file_data (op->fd, dest_fd) != 0) + die_with_error ("Can't write data to file %s", op->dest); - close (op->fd); - op->fd = -1; - } + close (op->fd); + op->fd = -1; + } break; - case SETUP_MAKE_BIND_FILE: - case SETUP_MAKE_RO_BIND_FILE: - { - cleanup_fd int dest_fd = -1; - char tempfile[] = "/bindfileXXXXXX"; + case SETUP_MAKE_BIND_FILE: + case SETUP_MAKE_RO_BIND_FILE: + { + cleanup_fd int dest_fd = -1; + char tempfile[] = "/bindfileXXXXXX"; - assert (dest != NULL); - assert (op->perms >= 0); - assert (op->perms <= 07777); + assert (dest != NULL); + assert (op->perms >= 0); + assert (op->perms <= 07777); - dest_fd = mkstemp (tempfile); - if (dest_fd == -1) - die_with_error ("Can't create tmpfile for %s", op->dest); + dest_fd = mkstemp (tempfile); + if (dest_fd == -1) + die_with_error ("Can't create tmpfile for %s", op->dest); - if (fchmod (dest_fd, op->perms) != 0) - die_with_error ("Can't set mode %#o on file to be used for %s", - op->perms, op->dest); + if (fchmod (dest_fd, op->perms) != 0) + die_with_error ("Can't set mode %#o on file to be used for %s", + op->perms, op->dest); - if (copy_file_data (op->fd, dest_fd) != 0) - die_with_error ("Can't write data to file %s", op->dest); + if (copy_file_data (op->fd, dest_fd) != 0) + die_with_error ("Can't write data to file %s", op->dest); - close (op->fd); - op->fd = -1; + close (op->fd); + op->fd = -1; - assert (dest != NULL); + assert (dest != NULL); - if (ensure_file (dest, 0444) != 0) - die_with_error ("Can't create file at %s", op->dest); + if (ensure_file (dest, 0444) != 0) + die_with_error ("Can't create file at %s", op->dest); - privileged_op (privileged_op_socket, - PRIV_SEP_OP_BIND_MOUNT, - (op->type == SETUP_MAKE_RO_BIND_FILE ? BIND_READONLY : 0), - 0, 0, tempfile, dest); - - /* Remove the file so we're sure the app can't get to it in any other way. - Its outside the container chroot, so it shouldn't be possible, but lets - make it really sure. */ - unlink (tempfile); - } + privileged_op (privileged_op_socket, + PRIV_SEP_OP_BIND_MOUNT, + (op->type == SETUP_MAKE_RO_BIND_FILE ? BIND_READONLY : 0), + 0, 0, tempfile, dest); + + /* We don't need tempfile anymore. But because we read kernel_case in bind_mount_fixup, + * we really still need links to those temp files being correct. + * That's why we add all temp files to list and unlink them later. */ + TempFile *temp = _temp_file_append_new (); + temp->dest = tempfile; + } break; - case SETUP_MAKE_SYMLINK: - assert (op->source != NULL); /* guaranteed by the constructor */ + case SETUP_MAKE_SYMLINK: + assert (op->source != NULL); /* guaranteed by the constructor */ if (symlink (op->source, dest) != 0) { if (errno == EEXIST) @@ -1489,15 +1538,15 @@ setup_newroot (bool unshare_pid, } break; - case SETUP_SET_HOSTNAME: - assert (op->dest != NULL); /* guaranteed by the constructor */ + case SETUP_SET_HOSTNAME: + assert (op->dest != NULL); /* guaranteed by the constructor */ privileged_op (privileged_op_socket, PRIV_SEP_OP_SET_HOSTNAME, 0, 0, 0, op->dest, NULL); break; - default: - die ("Unexpected type %d", op->type); + default: + die ("Unexpected type %d", op->type); } } privileged_op (privileged_op_socket, @@ -1535,10 +1584,10 @@ resolve_symlinks_in_ops (void) switch (op->type) { - case SETUP_RO_BIND_MOUNT: - case SETUP_DEV_BIND_MOUNT: - case SETUP_BIND_MOUNT: - old_source = op->source; + case SETUP_RO_BIND_MOUNT: + case SETUP_DEV_BIND_MOUNT: + case SETUP_BIND_MOUNT: + old_source = op->source; op->source = realpath (old_source, NULL); if (op->source == NULL) { @@ -1549,20 +1598,20 @@ resolve_symlinks_in_ops (void) } break; - case SETUP_MOUNT_PROC: - case SETUP_MOUNT_DEV: - case SETUP_MOUNT_TMPFS: - case SETUP_MOUNT_MQUEUE: - case SETUP_MAKE_DIR: - case SETUP_MAKE_FILE: - case SETUP_MAKE_BIND_FILE: - case SETUP_MAKE_RO_BIND_FILE: - case SETUP_MAKE_SYMLINK: - case SETUP_REMOUNT_RO_NO_RECURSIVE: - case SETUP_SET_HOSTNAME: - case SETUP_CHMOD: - default: - break; + case SETUP_MOUNT_PROC: + case SETUP_MOUNT_DEV: + case SETUP_MOUNT_TMPFS: + case SETUP_MOUNT_MQUEUE: + case SETUP_MAKE_DIR: + case SETUP_MAKE_FILE: + case SETUP_MAKE_BIND_FILE: + case SETUP_MAKE_RO_BIND_FILE: + case SETUP_MAKE_SYMLINK: + case SETUP_REMOUNT_RO_NO_RECURSIVE: + case SETUP_SET_HOSTNAME: + case SETUP_CHMOD: + default: + break; } } } @@ -1759,10 +1808,10 @@ parse_args_recurse (int *argcp, * to support systems/kernels without support for those. */ opt_unshare_user_try = opt_unshare_ipc = opt_unshare_pid = - opt_unshare_uts = opt_unshare_cgroup_try = - opt_unshare_net = TRUE; + opt_unshare_uts = opt_unshare_cgroup_try = + opt_unshare_net = TRUE; } - /* Begin here the older individual --unshare variants */ + /* Begin here the older individual --unshare variants */ else if (strcmp (arg, "--unshare-user") == 0) { opt_unshare_user = TRUE; @@ -1795,12 +1844,12 @@ parse_args_recurse (int *argcp, { opt_unshare_cgroup_try = TRUE; } - /* Begin here the newer --share variants */ + /* Begin here the newer --share variants */ else if (strcmp (arg, "--share-net") == 0) { opt_unshare_net = FALSE; } - /* End --share variants, other arguments begin */ + /* End --share variants, other arguments begin */ else if (strcmp (arg, "--chdir") == 0) { if (argc < 2) diff --git a/data-structures/destinations-graph-ops-linked-list.c b/data-structures/destinations-graph-ops-linked-list.c new file mode 100644 index 00000000..a3ec1fed --- /dev/null +++ b/data-structures/destinations-graph-ops-linked-list.c @@ -0,0 +1,64 @@ +#include "destinations-graph.h" + +DestinationsLinkedList * +DestinationsLinkedList_create (void) +{ + DestinationsLinkedList *list = xcalloc(1, sizeof (DestinationsLinkedList)); + list->head = NULL; + list->tail = NULL; + list->count = 0; + return list; +} + +void +DestinationsLinkedList_free (DestinationsLinkedList *self) +{ + DestinationsLinkedList_Node *current = self->head; + + while (current != NULL) + { + DestinationsLinkedList_Node *next = current->next; + free (current); + current = next; + } + + free (self); +} + +void +DestinationsLinkedList_push_back (DestinationsLinkedList *self, + DestinationsGraph_Node *node) +{ + + DestinationsLinkedList_Node *pushed = xcalloc(1, sizeof (DestinationsLinkedList_Node)); + pushed->value = node; + pushed->next = NULL; + + // If they NULL, then only both + if (self->head == NULL || self->tail == NULL) + { + self->head = pushed; + self->tail = pushed; + self->count = 1; + } + else + { + self->tail->next = pushed; + self->tail = self->tail->next; + } +} + +DestinationsGraph_Node* +DestinationsLinkedList_find_by_path_part (DestinationsLinkedList *self, + char *path_part) +{ + DestinationsLinkedList_Node *current = self->head; + + while (current != NULL) + { + if(strcmp (current->value->path_part, path_part) == 0) + return current->value; + current = current->next; + } + return NULL; +} diff --git a/data-structures/destinations-graph-ops-node.c b/data-structures/destinations-graph-ops-node.c new file mode 100644 index 00000000..9f379ed1 --- /dev/null +++ b/data-structures/destinations-graph-ops-node.c @@ -0,0 +1,84 @@ +#include "destinations-graph.h" + +DestinationsGraph_Node * +DestinationsGraph_Node_create (void) +{ + DestinationsGraph_Node *self = xcalloc(1, sizeof (DestinationsGraph_Node)); + + self->path_part = NULL; + self->is_mount_point = FALSE; + self->source = NULL; + self->dest = NULL; + self->children = DestinationsLinkedList_create (); + self->euler_tour_start = 0; + self->euler_tour_end = 0; + + return self; +} + +void +DestinationsGraph_Node_free (DestinationsGraph_Node *self) +{ + self->is_mount_point = 0; + free (self->path_part = NULL); + free (self->source); + free (self->dest); + DestinationsLinkedList_free (self->children); + free(self); +} + +void +DestinationsGraph_Node_free_recursive (DestinationsGraph_Node *self) +{ + DestinationsLinkedList_Node *currentChild = self->children->head; + + while (currentChild != NULL) + { + DestinationsGraph_Node_free_recursive (currentChild->value); + currentChild = currentChild->next; + } + + DestinationsGraph_Node_free (self); +} + +void +DestinationsGraph_Node__euler_tour__ (DestinationsGraph_Node *self, size_t *euler_tour_timer) +{ + self->euler_tour_start = (*euler_tour_timer)++; + + DestinationsLinkedList_Node *currentChild = self->children->head; + + while (currentChild != NULL) + { + DestinationsGraph_Node__euler_tour__ (currentChild->value, euler_tour_timer); + currentChild = currentChild->next; + } + + self->euler_tour_end = (*euler_tour_timer)++; +} + +void +DestinationsGraph_Node_debug_pretty_print (FILE *fd, DestinationsGraph_Node *current, int depth) +{ + for (int i = 0; i < depth; i++) + fputs (" |", fd); + + if (depth == 0) + fputs ("/ ", fd); + else + fprintf (fd, "- %s ", current->path_part); + + if (current->is_mount_point) + fputs("(*)", fd); + + fputs ("\n", fd); + + + DestinationsLinkedList_Node *currentChild = current->children->head; + + while (currentChild != NULL) + { + DestinationsGraph_Node_debug_pretty_print (fd, currentChild->value, depth + 1); + currentChild = currentChild->next; + } +} \ No newline at end of file diff --git a/data-structures/destinations-graph-ops.c b/data-structures/destinations-graph-ops.c new file mode 100644 index 00000000..97bcd5df --- /dev/null +++ b/data-structures/destinations-graph-ops.c @@ -0,0 +1,178 @@ +#include "destinations-graph.h" + +/// -------------------------------------------------------------------------------------------------------------------- +/// Safety + +void +cleanup_destinations_graphp (void *p) +{ + void **pp = (void **) p; + if (*pp) + DestinationsGraph_free ((DestinationsGraph *) *pp); +} + +void +cleanup_destinations_graph_nodep (void *p) +{ + void **pp = (void **) p; + if (*pp) + DestinationsGraph_Node_free((DestinationsGraph_Node *) *pp); +} + +void +cleanup_destinations_graph_node_recursivep (void *p) +{ + void **pp = (void **) p; + if (*pp) + DestinationsGraph_Node_free_recursive ((DestinationsGraph_Node *) *pp); +} + +// -------------------------------------------------------------------------------------------------------------------- +// Regular methods of DestinationsGraph + +DestinationsGraph * +DestinationsGraph_create (void) +{ + DestinationsGraph *self = xcalloc(1, sizeof (DestinationsGraph)); + + // Create initial root node. + // It's a little special because it has no path_part. + // Also, by default it's not mount point. + // But things can change later if we bind mount something to the root. + DestinationsGraph_Node *root = DestinationsGraph_Node_create (); + + self->root = root; + self->count_mount_points = 0; + self->count_nodes = 1; + + return self; +} + +void +DestinationsGraph_free (DestinationsGraph *self) +{ + if (self->nodev != NULL) + SumSegmentTree_free (self->nodev); + + if (self->readonly != NULL) + SumSegmentTree_free (self->readonly); + + if(self->root != NULL) + DestinationsGraph_Node_free_recursive (self->root); + + free(self); +} + +DestinationsGraph_Node * +DestinationsGraph_ensure_mount_point (DestinationsGraph *self, char **destination_path, bool *added) +{ + // We duplicate given destination_path because next + // we will split it by "/" with strtok function. + // We don'value want to corrupt original data + char *destination_path_dup = xstrdup (*destination_path); + + // Start with root + DestinationsGraph_Node *current = self->root; + + // Destination_path_dup + 1 means we ignore first "/" in path + char *next_path_part = strtok (destination_path_dup + 1, "/"); + + // We take path_parts one by one + while (next_path_part != NULL) + { + + // Look for next node corresponding this next_path_part + DestinationsGraph_Node *next = DestinationsLinkedList_find_by_path_part (current->children, next_path_part); + + // If we found none, need to create it + if (next == NULL) + { + DestinationsGraph_Node *new_node = DestinationsGraph_Node_create (); + new_node->path_part = xstrdup (next_path_part); + + DestinationsLinkedList_push_back (current->children, new_node); + self->count_nodes++; + + if(added != NULL) *added = TRUE; + next = new_node; + } + + current = next; + next_path_part = strtok (NULL, "/"); + } + + // For now current node is a mount point. Label it + if (!current->is_mount_point) + { + if(added != NULL) *added = TRUE; + current->is_mount_point = TRUE; + self->count_mount_points++; + } + + free (destination_path_dup); + return current; +} + +void +DestinationsGraph__euler_tour__ (DestinationsGraph *self) +{ + size_t euler_tour_timer = 0; + DestinationsGraph_Node__euler_tour__ (self->root, &euler_tour_timer); +} + +void +DestinationsGraph_debug_pretty_print (DestinationsGraph *self, FILE *fd) +{ + DestinationsGraph_Node_debug_pretty_print (fd, self->root, 0); +} + +// --------------------------------------------------------------------------------------------------------------------- +// Flags-related methods of DestinationsGraph + +void +DestinationsGraph_Flags_init (DestinationsGraph *self) +{ + // Perform euler tour to reflect nodes on segment tree + DestinationsGraph__euler_tour__ (self); + + self->nodev = SumSegmentTree_create (self->count_nodes); + self->readonly = SumSegmentTree_create (self->count_nodes); +} + +void +DestinationsGraph_Flags__set_flag__ (SumSegmentTree *segment_tree, DestinationsGraph_Node *node, bool value) +{ + SumSegmentTree_modify (segment_tree, node->euler_tour_start, node->euler_tour_end, value ? 1 : 0); +} + +bool +DestinationsGraph_Flags__check_flag__ (SumSegmentTree *segment_tree, DestinationsGraph_Node *node) +{ + // For real we CAN convert euler_tour_start to the int. + // Although it's size_t, it will never be THAT big. + return SumSegmentTree_query (segment_tree, (int)node->euler_tour_start); +} + +void +DestinationsGraph_Flags_set_readonly (DestinationsGraph *self, DestinationsGraph_Node *node, bool value) +{ + DestinationsGraph_Flags__set_flag__ (self->readonly, node, value); +} + +bool +DestinationsGraph_Flags_check_readonly (DestinationsGraph *self, DestinationsGraph_Node *node) +{ + return DestinationsGraph_Flags__check_flag__ (self->readonly, node); +} + +void +DestinationsGraph_Flags_set_nodev (DestinationsGraph *self, DestinationsGraph_Node *node, bool value) +{ + DestinationsGraph_Flags__set_flag__ (self->nodev, node, value); +} + +bool +DestinationsGraph_Flags_check_nodev (DestinationsGraph *self, DestinationsGraph_Node *node) +{ + return DestinationsGraph_Flags__check_flag__ (self->nodev, node); +} \ No newline at end of file diff --git a/data-structures/destinations-graph.h b/data-structures/destinations-graph.h new file mode 100644 index 00000000..e2fa6768 --- /dev/null +++ b/data-structures/destinations-graph.h @@ -0,0 +1,146 @@ +#pragma once + +#include "../utils.h" +#include "lazy-segment-tree.h" + +/// -------------------------------------------------------------------------------------------------------------------- +/// Type declarations + +typedef struct _DestinationsGraph DestinationsGraph; + +typedef struct _DestinationsGraph_Node DestinationsGraph_Node; + +typedef struct _DestinationsLinkedList_Node DestinationsLinkedList_Node; + +typedef struct _DestinationsLinkedList DestinationsLinkedList; + +struct _DestinationsGraph_Node { + + char *path_part; /* Parts of slash-separated path. Could be directory or file name */ + + bool is_mount_point; /* Whether it is a mount point we want to bind or simple path of path. */ + char *source; /* == NULL when is_mount_point == FALSE */ + char *dest; /* == NULL when is_mount_point == FALSE */ + + DestinationsLinkedList *children; + + size_t euler_tour_start; + size_t euler_tour_end; + +}; + +struct _DestinationsGraph { + + DestinationsGraph_Node *root; + + size_t count_nodes; + size_t count_mount_points; + + SumSegmentTree *nodev; + SumSegmentTree *readonly; + +}; + +struct _DestinationsLinkedList_Node { + DestinationsGraph_Node *value; + DestinationsLinkedList_Node *next; +}; + +struct _DestinationsLinkedList { + DestinationsLinkedList_Node *head; + DestinationsLinkedList_Node *tail; + int count; +}; + +/// --------------------------------------------------------------------------------------------------------------------- +/// Default methods of DestinationsGraph + +DestinationsGraph * +DestinationsGraph_create (void); + +void +DestinationsGraph_free (DestinationsGraph *self); + +DestinationsGraph_Node * +DestinationsGraph_ensure_mount_point (DestinationsGraph *self, char **destination_path, bool *added); + +void +DestinationsGraph__euler_tour__ (DestinationsGraph *self); + +void +DestinationsGraph_debug_pretty_print (DestinationsGraph *self, FILE *fd); + +/// -------------------------------------------------------------------------------------------------------------------- +/// Nodes methods of DestinationsGraph + +DestinationsGraph_Node * +DestinationsGraph_Node_create (void); + +void +DestinationsGraph_Node_free (DestinationsGraph_Node *self); + +void +DestinationsGraph_Node_free_recursive (DestinationsGraph_Node *self); + +void +DestinationsGraph_Node__euler_tour__ (DestinationsGraph_Node *self, size_t *euler_tour_timer); + +void +DestinationsGraph_Node_debug_pretty_print (FILE *fd, DestinationsGraph_Node *current, int depth); + +/// -------------------------------------------------------------------------------------------------------------------- +/// Linked list methods of DestinationsGraph + +DestinationsLinkedList * +DestinationsLinkedList_create (void); + +void +DestinationsLinkedList_free (DestinationsLinkedList *self); + +void +DestinationsLinkedList_push_back (DestinationsLinkedList *self, + DestinationsGraph_Node *node); + +DestinationsGraph_Node * +DestinationsLinkedList_find_by_path_part (DestinationsLinkedList *self, + char *path_part); + +/// --------------------------------------------------------------------------------------------------------------------- +/// Flags-related methods of DestinationsGraph + +void +DestinationsGraph_Flags_init (DestinationsGraph *self); + +void +DestinationsGraph_Flags__set_flag__ (SumSegmentTree *segment_tree, DestinationsGraph_Node *node, bool value); + +bool +DestinationsGraph_Flags__check_flag__ (SumSegmentTree *segment_tree, DestinationsGraph_Node *node); + +void +DestinationsGraph_Flags_set_readonly (DestinationsGraph *self, DestinationsGraph_Node *node, bool value); + +bool +DestinationsGraph_Flags_check_readonly (DestinationsGraph *self, DestinationsGraph_Node *node); + +void +DestinationsGraph_Flags_set_nodev (DestinationsGraph *self, DestinationsGraph_Node *node, bool value); + +bool +DestinationsGraph_Flags_check_nodev (DestinationsGraph *self, DestinationsGraph_Node *node); + +/// -------------------------------------------------------------------------------------------------------------------- +/// Safety + +void +cleanup_destinations_graphp (void *p); + +void +cleanup_destinations_graph_nodep (void *p); + +void +cleanup_destinations_graph_node_recursivep (void *p); + +#define cleanup_destinations_graph __attribute__((cleanup (cleanup_destinations_graphp))) +#define cleanup_destinations_graph_node __attribute__((cleanup (cleanup_destinations_graph_nodep))) +#define cleanup_destinations_graph_node_subtree __attribute__((cleanup (cleanup_destinations_graph_node_recursivep))) \ No newline at end of file diff --git a/data-structures/lazy-segment-tree.c b/data-structures/lazy-segment-tree.c new file mode 100644 index 00000000..0ab449b7 --- /dev/null +++ b/data-structures/lazy-segment-tree.c @@ -0,0 +1,140 @@ +#ifndef LAZY_SEGMENT_TREE_OPS +#define LAZY_SEGMENT_TREE_OPS + +#include "lazy-segment-tree.h" +#include +#include +#include + +/// -------------------------------------------------------------------------------------------------------------------- +/// Constructing and destructing object + +SumSegmentTree +*SumSegmentTree_create (size_t quantity) +{ + SumSegmentTree *self = xcalloc(1, sizeof (SumSegmentTree)); + assert(quantity > 0); + + // levels = log2(quantity - 1) + size_t under_logarithm = quantity - 1; + size_t levels = 1; + while (under_logarithm >>= 1) levels++; + + // self->n = (levels + 1) ^ 2 + self->n = (1 << (levels + 1)); + + size_t elements = self->n * 2; + + self->value = xcalloc (elements, sizeof (int)); + self->lazy = xcalloc (elements, sizeof (int)); + + return self; +} + +void +SumSegmentTree_free (SumSegmentTree *self) +{ + self->n = 0; + free (self->value); + free (self->lazy); + free (self); +} + +/// -------------------------------------------------------------------------------------------------------------------- +/// Private methods + +static void +SumSegmentTree__push__ (SumSegmentTree *self, size_t v, size_t l, size_t r); + +static void +SumSegmentTree__modify__ (SumSegmentTree *self, size_t x, size_t y, int val, size_t root, size_t l, size_t r); + +static int +SumSegmentTree__query__ (SumSegmentTree *self, size_t x, size_t y, size_t root, size_t l, size_t r); + +static void +SumSegmentTree__push__ (SumSegmentTree *self, size_t v, size_t l, size_t r) +{ + if (self->lazy[v] != -1 && v < self->n) + { + size_t mid = (l + r) / 2; + + // Left child = lazy[current] * left segment length + self->value[v << 1] = (int) (self->lazy[v] * (mid - l)); + + // Right child = lazy[current] * right segment length + self->value[v << 1 | 1] = (int) (self->lazy[v] * (r - mid)); + + // Lazy propagate value to subtrees + self->lazy[v << 1] = self->lazy[v]; + self->lazy[v << 1 | 1] = self->lazy[v]; + self->lazy[v] = -1; + } +} + +static void +SumSegmentTree__modify__ (SumSegmentTree *self, size_t x, size_t y, int val, size_t root, size_t l, size_t r) +{ + // Return if we are out of desired segment + if (x >= r || y <= l) + return; + + // If we are fully in desired segment + // Then assign value in lazy way + if (x <= l && r <= y) + { + self->lazy[root] = val; + self->value[root] = val * (int) (r - l); + return; + } + + // Lazy propagate existing changes + SumSegmentTree__push__ (self, root, l, r); + + size_t mid = (l + r) / 2; + + // Perform the same operation for left and right subtrees + SumSegmentTree__modify__ (self, x, y, val, root << 1, l, mid); + SumSegmentTree__modify__ (self, x, y, val, root << 1 | 1, mid, r); + + self->value[root] = self->value[root << 1] + self->value[root << 1 | 1]; +} + +static int +SumSegmentTree__query__ (SumSegmentTree *self, size_t x, size_t y, size_t root, size_t l, size_t r) +{ + // If we are outside of desired segment, return 0 + // Because current implementation of segment tree calculates sums on segments, + // 0 is okay for us and won't change query result in unexpected way + if (x >= r || y <= l) + return 0; + + // If we are fully in requested segment, we can just return value + if (x <= l && r <= y) + return self->value[root]; + + // Otherwise, we propagate lazy value and recurse into subtrees + SumSegmentTree__push__ (self, root, l, r); + + size_t mid = (l + r) >> 1; + + return SumSegmentTree__query__ (self, x, y, root << 1, l, mid) + \ + SumSegmentTree__query__ (self, x, y, root << 1 | 1, mid, r); +} + +/// -------------------------------------------------------------------------------------------------------------------- +/// Public methods + +void +SumSegmentTree_modify (SumSegmentTree *self, size_t x, size_t y, int val) +{ + SumSegmentTree__modify__ (self, x, y, val, 1, 0, self->n); +} + +int +SumSegmentTree_query (SumSegmentTree *self, int x) +{ + return SumSegmentTree__query__ (self, x, x + 1, 1, 0, self->n); +} + +#endif diff --git a/data-structures/lazy-segment-tree.h b/data-structures/lazy-segment-tree.h new file mode 100644 index 00000000..ab102697 --- /dev/null +++ b/data-structures/lazy-segment-tree.h @@ -0,0 +1,34 @@ +#ifndef LAZY_SEGMENT_TREE +#define LAZY_SEGMENT_TREE + +#include "stddef.h" +#include "../utils.h" + +/// -------------------------------------------------------------------------------------------------------------------- +/// Type declarations + +typedef struct _SumSegmentTree SumSegmentTree; + +struct _SumSegmentTree { + size_t n; + int *value; + int *lazy; +}; + + +/// -------------------------------------------------------------------------------------------------------------------- +/// Methods + +SumSegmentTree +*SumSegmentTree_create (size_t quantity); + +void +SumSegmentTree_free (SumSegmentTree *self); + +void +SumSegmentTree_modify (SumSegmentTree *self, size_t x, size_t y, int val); + +int +SumSegmentTree_query (SumSegmentTree *self, int x); + +#endif diff --git a/meson.build b/meson.build index 72257afd..16e6f805 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'bubblewrap', 'c', - version : '0.9.0', + version : '0.10.0', meson_version : '>=0.49.0', default_options : [ 'warning_level=2', @@ -115,6 +115,11 @@ bwrap = executable( 'bind-mount.c', 'network.c', 'utils.c', + 'parse-mountinfo.c', + 'data-structures/destinations-graph-ops.c', + 'data-structures/destinations-graph-ops-linked-list.c', + 'data-structures/destinations-graph-ops-node.c', + 'data-structures/lazy-segment-tree.c', ], build_rpath : get_option('build_rpath'), install : true, diff --git a/parse-mountinfo.c b/parse-mountinfo.c new file mode 100644 index 00000000..d2f506dd --- /dev/null +++ b/parse-mountinfo.c @@ -0,0 +1,374 @@ +/* bubblewrap + * Copyright (C) 2016 Alexander Larsson + * SPDX-License-Identifier: LGPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#include "parse-mountinfo.h" + +/// -------------------------------------------------------------------------------------------------------------------- +/// Safety + +void +mount_tab_free (MountTab tab) +{ + int i; + + for (i = 0; tab[i].mountpoint != NULL; i++) + free (tab[i].mountpoint); + free (tab); +} + +inline void +cleanup_mount_tabp (void *p) +{ + void **pp = (void **) p; + + if (*pp) + mount_tab_free ((MountTab) *pp); +} + +/// -------------------------------------------------------------------------------------------------------------------- +/// Local type definitions + +typedef struct MountInfoLine MountInfoLine; +struct MountInfoLine { + const char *mountpoint; + const char *options; + bool covered; + int id; + int parent_id; + MountInfoLine *first_child; + MountInfoLine *next_sibling; +}; + +/// -------------------------------------------------------------------------------------------------------------------- +/// Static functions + +static char * +skip_token (char *line, bool eat_whitespace) +{ + while (*line != ' ' && *line != '\n') + line++; + + if (eat_whitespace && *line == ' ') + line++; + + return line; +} + +static char * +unescape_inline (char *escaped) +{ + char *unescaped, *res; + const char *end; + + res = escaped; + end = escaped + strlen (escaped); + + unescaped = escaped; + while (escaped < end) + { + if (*escaped == '\\') + { + *unescaped++ = + ((escaped[1] - '0') << 6) | + ((escaped[2] - '0') << 3) | + ((escaped[3] - '0') << 0); + escaped += 4; + } + else + { + *unescaped++ = *escaped++; + } + } + *unescaped = 0; + return res; +} + +static bool +match_token (const char *token, const char *token_end, const char *str) +{ + while (token != token_end && *token == *str) + { + token++; + str++; + } + if (token == token_end) + return *str == 0; + + return FALSE; +} + +static unsigned long +decode_mountoptions (const char *options) +{ + const char *token, *end_token; + int i; + unsigned long flags = 0; + static const struct { + int flag; + const char *name; + } flags_data[] = { + {0, "rw"}, + {MS_RDONLY, "ro"}, + {MS_NOSUID, "nosuid"}, + {MS_NODEV, "nodev"}, + {MS_NOEXEC, "noexec"}, + {MS_NOATIME, "noatime"}, + {MS_NODIRATIME, "nodiratime"}, + {MS_RELATIME, "relatime"}, + {0, NULL} + }; + + token = options; + do + { + end_token = strchr (token, ','); + if (end_token == NULL) + end_token = token + strlen (token); + + for (i = 0; flags_data[i].name != NULL; i++) + { + if (match_token (token, end_token, flags_data[i].name)) + { + flags |= flags_data[i].flag; + break; + } + } + + if (*end_token != 0) + token = end_token + 1; + else + token = NULL; + } + while (token != NULL); + + return flags; +} + +static unsigned int +count_lines (const char *data) +{ + unsigned int count = 0; + const char *p = data; + + while (*p != 0) + { + if (*p == '\n') + count++; + p++; + } + + /* If missing final newline, add one */ + if (p > data && *(p - 1) != '\n') + count++; + + return count; +} + +static int +count_mounts (MountInfoLine *line) +{ + MountInfoLine *child; + int res = 0; + + if (!line->covered) + res += 1; + + child = line->first_child; + while (child != NULL) + { + res += count_mounts (child); + child = child->next_sibling; + } + + return res; +} + +static MountInfo * +collect_mounts (MountInfo *info, MountInfoLine *line) +{ + MountInfoLine *child; + + if (!line->covered) + { + info->mountpoint = xstrdup (line->mountpoint); + info->options = decode_mountoptions (line->options); + info++; + } + + child = line->first_child; + while (child != NULL) + { + info = collect_mounts (info, child); + child = child->next_sibling; + } + + return info; +} + +/// -------------------------------------------------------------------------------------------------------------------- +/// Public functions + +MountTab +parse_mountinfo (int proc_fd, + const char *root_mount) +{ + cleanup_free char *mountinfo = NULL; + cleanup_free MountInfoLine *lines = NULL; + cleanup_free MountInfoLine **by_id = NULL; + cleanup_mount_tab MountTab mount_tab = NULL; + MountInfo *end_tab; + int n_mounts; + char *line; + unsigned int i; + int max_id; + unsigned int n_lines; + int root; + + mountinfo = load_file_at (proc_fd, "self/mountinfo"); + if (mountinfo == NULL) + die_with_error ("Can't open /proc/self/mountinfo"); + + n_lines = count_lines (mountinfo); + lines = xcalloc (n_lines, sizeof (MountInfoLine)); + + max_id = 0; + line = mountinfo; + i = 0; + root = -1; + while (*line != 0) + { + int rc, consumed = 0; + unsigned int maj, min; + char *end; + char *rest; + char *mountpoint; + char *mountpoint_end; + char *options; + char *options_end; + char *next_line; + + assert (i < n_lines); + + end = strchr (line, '\n'); + if (end != NULL) + { + *end = 0; + next_line = end + 1; + } + else + next_line = line + strlen (line); + + rc = sscanf (line, "%d %d %u:%u %n", &lines[i].id, &lines[i].parent_id, &maj, &min, &consumed); + + if (rc != 4) + die ("Can't parse mountinfo line"); + rest = line + consumed; + + rest = skip_token (rest, TRUE); /* mountroot */ + mountpoint = rest; + rest = skip_token (rest, FALSE); /* mountpoint */ + mountpoint_end = rest++; + options = rest; + rest = skip_token (rest, FALSE); /* vfs options */ + options_end = rest; + + *mountpoint_end = 0; + lines[i].mountpoint = unescape_inline (mountpoint); + + *options_end = 0; + lines[i].options = options; + + if (lines[i].id > max_id) + max_id = lines[i].id; + if (lines[i].parent_id > max_id) + max_id = lines[i].parent_id; + + if (path_equal (lines[i].mountpoint, root_mount)) + root = i; + + i++; + line = next_line; + } + assert (i == n_lines); + + if (root == -1) + { + mount_tab = xcalloc (1, sizeof (MountInfo)); + return steal_pointer (&mount_tab); + } + + by_id = xcalloc (max_id + 1, sizeof (MountInfoLine *)); + for (i = 0; i < n_lines; i++) + by_id[lines[i].id] = &lines[i]; + + for (i = 0; i < n_lines; i++) + { + MountInfoLine *this = &lines[i]; + MountInfoLine *parent = by_id[this->parent_id]; + MountInfoLine **to_sibling; + MountInfoLine *sibling; + bool covered = FALSE; + + if (!has_path_prefix (this->mountpoint, root_mount)) + continue; + + if (parent == NULL) + continue; + + if (strcmp (parent->mountpoint, this->mountpoint) == 0) + parent->covered = TRUE; + + to_sibling = &parent->first_child; + sibling = parent->first_child; + while (sibling != NULL) + { + /* If this mountpoint is a path prefix of the sibling, + * say this->mp=/foo/bar and sibling->mp=/foo, then it is + * covered by the sibling, and we drop it. */ + if (has_path_prefix (this->mountpoint, sibling->mountpoint)) + { + covered = TRUE; + break; + } + + /* If the sibling is a path prefix of this mount point, + * say this->mp=/foo and sibling->mp=/foo/bar, then the sibling + * is covered, and we drop it. + */ + if (has_path_prefix (sibling->mountpoint, this->mountpoint)) + *to_sibling = sibling->next_sibling; + else + to_sibling = &sibling->next_sibling; + sibling = sibling->next_sibling; + } + + if (covered) + continue; + + *to_sibling = this; + } + + n_mounts = count_mounts (&lines[root]); + mount_tab = xcalloc (n_mounts + 1, sizeof (MountInfo)); + + end_tab = collect_mounts (&mount_tab[0], &lines[root]); + assert (end_tab == &mount_tab[n_mounts]); + + return steal_pointer (&mount_tab); +} \ No newline at end of file diff --git a/parse-mountinfo.h b/parse-mountinfo.h new file mode 100644 index 00000000..a78f1709 --- /dev/null +++ b/parse-mountinfo.h @@ -0,0 +1,52 @@ +/* bubblewrap + * Copyright (C) 2016 Alexander Larsson + * SPDX-License-Identifier: LGPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#pragma once + +#include "utils.h" +#include "sys/mount.h" + +/// --------------------------------------------------------------------------------------------------------------------- +/// Types declarations + +typedef struct MountInfo MountInfo; +struct MountInfo { + char *mountpoint; + unsigned long options; +}; + +typedef MountInfo *MountTab; + +/// --------------------------------------------------------------------------------------------------------------------- +/// Safety + +#define cleanup_mount_tab __attribute__((cleanup (cleanup_mount_tabp))) + +void +cleanup_mount_tabp (void *p); + +void +mount_tab_free (MountTab tab); + +/// --------------------------------------------------------------------------------------------------------------------- +/// Public functions + +MountTab +parse_mountinfo (int proc_fd, + const char *root_mount); \ No newline at end of file diff --git a/utils.c b/utils.c index 43c8d798..fd332068 100644 --- a/utils.c +++ b/utils.c @@ -829,7 +829,6 @@ readlink_malloc (const char *pathname) size_t size = 50; ssize_t n; cleanup_free char *value = NULL; - do { if (size > SIZE_MAX / 2) diff --git a/utils.h b/utils.h index 9f17297d..ccc38be1 100644 --- a/utils.h +++ b/utils.h @@ -31,10 +31,10 @@ #include #include -#if 0 -#define __debug__(x) printf x +#ifdef DEBUG +#define __debug__(...) do { fprintf(stderr, __VA_ARGS__); } while (0) #else -#define __debug__(x) +#define __debug__(...) #endif #define UNUSED __attribute__((__unused__))