From f82c465f24d61e8d1ed9d77341b308efb5eabca1 Mon Sep 17 00:00:00 2001 From: Yahor Levanenka Date: Sun, 28 Apr 2024 07:22:44 +0300 Subject: [PATCH 1/4] Changed the bind mount algorithm to gain O(NlogN) asymptotics. More advanced explanation of algorithm placed it bind_mount_fixup(comments --- bind-mount.c | 271 ++++++-- bind-mount.h | 33 + bubblewrap.c | 625 ++++++++++-------- .../destinations-graph-ops-linked-list.c | 65 ++ data-structures/destinations-graph-ops-node.c | 85 +++ data-structures/destinations-graph-ops.c | 170 +++++ data-structures/destinations-graph.h | 141 ++++ data-structures/lazy-segment-tree.c | 146 ++++ data-structures/lazy-segment-tree.h | 33 + meson.build | 4 + utils.c | 1 - 11 files changed, 1233 insertions(+), 341 deletions(-) create mode 100644 data-structures/destinations-graph-ops-linked-list.c create mode 100644 data-structures/destinations-graph-ops-node.c create mode 100644 data-structures/destinations-graph-ops.c create mode 100644 data-structures/destinations-graph.h create mode 100644 data-structures/lazy-segment-tree.c create mode 100644 data-structures/lazy-segment-tree.h diff --git a/bind-mount.c b/bind-mount.c index 2757caea..73ef1d43 100644 --- a/bind-mount.c +++ b/bind-mount.c @@ -23,6 +23,7 @@ #include "utils.h" #include "bind-mount.h" +#include "data-structures/destinations-graph.h" static char * skip_token (char *line, bool eat_whitespace) @@ -51,9 +52,9 @@ unescape_inline (char *escaped) if (*escaped == '\\') { *unescaped++ = - ((escaped[1] - '0') << 6) | - ((escaped[2] - '0') << 3) | - ((escaped[3] - '0') << 0); + ((escaped[1] - '0') << 6) | + ((escaped[2] - '0') << 3) | + ((escaped[3] - '0') << 0); escaped += 4; } else @@ -85,18 +86,19 @@ decode_mountoptions (const char *options) const char *token, *end_token; int i; unsigned long flags = 0; - static const struct { int flag; - const char *name; + 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 } + {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; @@ -125,14 +127,6 @@ decode_mountoptions (const char *options) return flags; } -typedef struct MountInfo MountInfo; -struct MountInfo { - char *mountpoint; - unsigned long options; -}; - -typedef MountInfo *MountTab; - static void mount_tab_free (MountTab tab) { @@ -149,20 +143,20 @@ cleanup_mount_tabp (void *p) void **pp = (void **) p; if (*pp) - mount_tab_free ((MountTab)*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; + const char *mountpoint; + const char *options; + bool covered; + int id; + int parent_id; + MountInfoLine *first_child; + MountInfoLine *next_sibling; }; static unsigned int @@ -179,7 +173,7 @@ count_lines (const char *data) } /* If missing final newline, add one */ - if (p > data && *(p-1) != '\n') + if (p > data && *(p - 1) != '\n') count++; return count; @@ -213,7 +207,7 @@ collect_mounts (MountInfo *info, MountInfoLine *line) { info->mountpoint = xstrdup (line->mountpoint); info->options = decode_mountoptions (line->options); - info ++; + info++; } child = line->first_child; @@ -227,7 +221,7 @@ collect_mounts (MountInfo *info, MountInfoLine *line) } static MountTab -parse_mountinfo (int proc_fd, +parse_mountinfo (int proc_fd, const char *root_mount) { cleanup_free char *mountinfo = NULL; @@ -314,7 +308,7 @@ parse_mountinfo (int proc_fd, return steal_pointer (&mount_tab); } - by_id = xcalloc (max_id + 1, sizeof (MountInfoLine*)); + by_id = xcalloc (max_id + 1, sizeof (MountInfoLine *)); for (i = 0; i < n_lines; i++) by_id[lines[i].id] = &lines[i]; @@ -360,7 +354,7 @@ parse_mountinfo (int proc_fd, } if (covered) - continue; + continue; *to_sibling = this; } @@ -375,11 +369,182 @@ parse_mountinfo (int proc_fd, } bind_mount_result -bind_mount (int proc_fd, - const char *src, - const char *dest, +retrieve_kernel_case (int proc_fd, char *dest, char **result, char **failing_path); + +bind_mount_result +retrieve_kernel_case (int proc_fd, char *dest, char **result, char **failing_path) +{ + 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; + + // 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; + + dest_fd = open (resolved_dest, O_PATH | O_CLOEXEC); + if (dest_fd < 0) + { + if (failing_path != NULL) + *failing_path = steal_pointer (&resolved_dest); + + return BIND_MOUNT_ERROR_REOPEN_DEST; + } + + /* If we are in a case-insensitive filesystem, mountinfo might contain a + * different case combination of the path we requested to mount. + * This is due to the fact that the kernel, as of the beginning of 2021, + * populates mountinfo with whatever case combination first appeared in the + * dcache; kernel developers plan to change this in future so that it + * reflects the on-disk encoding instead. + * To avoid throwing an error when this happens, we use readlink() result + * 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); + + return BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD; + } + + *result = steal_pointer(&kernel_case_combination); + return BIND_MOUNT_SUCCESS; +} + +bind_mount_result +bind_mount_fixup (int proc_fd, BindOp *bind_ops, size_t bind_ops_quantity, char **failing_path) +{ + DestinationsGraph *graph; + cleanup_mount_tab MountTab mount_tab; + DestinationsGraph_Node **mount_points = malloc (bind_ops_quantity * sizeof (DestinationsGraph_Node *)); + BindOp *bop; + size_t current; + + // Bind_mount_fixup calls after all bind mount operations executed + // Now we only collect all information about actual mounts to the graph + + graph = DestinationsGraph_create (); + mount_tab = parse_mountinfo (proc_fd, "/newroot"); + + for (int i = 0; mount_tab[i].mountpoint != NULL; i++) + { + //printf ("Mountinfo: %s\n", mount_tab[i].mountpoint); + DestinationsGraph_ensure_mount_point (graph, &mount_tab[i].mountpoint, NULL); + } + + // Here we go through all bind operations and collect list of actual graph nodes + // that correspond these bind operations. + // And the same time we lazy propagate flags. + + DestinationsGraph_Flags_init (graph); + + for (bop = bind_ops, current = 0; bop != NULL; bop = bop->next, current++) + { + // Retrieve real path after mounting + + char *kernel_case; + bind_mount_result result = retrieve_kernel_case (bop->fd, bop->dest, &kernel_case, failing_path); + + if (result != BIND_MOUNT_SUCCESS) + return result; + + // Get corresponding destinations graph node + // It's important that here we really shouldn't retrieve any points we haven't mounted before + // In the case we have some problems :) + + bool added = FALSE; + mount_points[current] = DestinationsGraph_ensure_mount_point (graph, &kernel_case, &added); + + //printf("BindOp: %s [%d]", kernel_case, bop->options); + //if(added) + // printf (" (ADDED)"); + //printf("\n"); + //printf ("%s\n", kernel_case); + + assert(added == FALSE); + + // Lazy propagate flags specified in bind operation + + bool readonly = (bop->options & BIND_READONLY) != 0; + bool devices = (bop->options & BIND_DEVICES) != 0; + + if (readonly) + DestinationsGraph_Flags_set_readonly (graph, mount_points[current]); + + if (!readonly) + DestinationsGraph_Flags_unset_readonly (graph, mount_points[current]); + + if (!devices) + DestinationsGraph_Flags_set_nodev (graph, mount_points[current]); + + if (devices) + DestinationsGraph_Flags_unset_nodev (graph, mount_points[current]); + + +// printf ("Kernel case: %s\n", kernel_case); + } + + //DestinationsGraph_debug_pretty_print (graph, stdout); + + for (int i = 0; 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); + 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 | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0); + + if (new_flags != current_flags) + { + //printf(">>> %s (readonly %d, nodev %d) (%d | %d)\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 (mount_result != 0 && errno != EACCES) + { + if (failing_path != NULL) *failing_path = steal_pointer (&mount_point); + return BIND_MOUNT_ERROR_REMOUNT_DEST; + } + } + } + + mount_tab = parse_mountinfo (proc_fd, "/newroot"); + + for (int i = 0; mount_tab[i].mountpoint != NULL; i++) + printf ("@ Mountinfo: %s (%d)\n", mount_tab[i].mountpoint, mount_tab[i].options); + + return BIND_MOUNT_SUCCESS; +} + +bind_mount_result +bind_mount (int proc_fd, + const char *src, + const char *dest, bind_option_t options, - char **failing_path) + char **failing_path) { bool readonly = (options & BIND_READONLY) != 0; bool devices = (options & BIND_DEVICES) != 0; @@ -505,42 +670,42 @@ 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; + break; case BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT: 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 +716,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; @@ -578,7 +743,7 @@ die_with_bind_result (bind_mount_result res, case BIND_MOUNT_ERROR_REMOUNT_DEST: case BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT: fprintf (stderr, ": %s", mount_strerror (saved_errno)); - break; + break; case BIND_MOUNT_ERROR_REALPATH_DEST: case BIND_MOUNT_ERROR_REOPEN_DEST: diff --git a/bind-mount.h b/bind-mount.h index 8a361fbd..e57a99ce 100644 --- a/bind-mount.h +++ b/bind-mount.h @@ -39,6 +39,39 @@ typedef enum BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT, } bind_mount_result; +typedef struct _BindOp BindOp; + +struct _BindOp { + int fd; + char *source; + char *dest; + bind_option_t options; + BindOp* next; +}; + +typedef struct MountInfo MountInfo; +struct MountInfo { + char *mountpoint; + unsigned long options; +}; + +typedef MountInfo *MountTab; + +static void +mount_tab_free (MountTab tab); + +#define cleanup_mount_tab __attribute__((cleanup (cleanup_mount_tabp))) + +static inline void +cleanup_mount_tabp (void *p); + +static MountTab +parse_mountinfo (int proc_fd, + const char *root_mount); + +bind_mount_result +bind_mount_fixup (int proc_fd, BindOp *bind_ops, size_t bind_ops_quantity, char** failing_path); + bind_mount_result bind_mount (int proc_fd, const char *src, const char *dest, diff --git a/bubblewrap.c b/bubblewrap.c index 9b78a9ae..5e2ed831 100644 --- a/bubblewrap.c +++ b/bubblewrap.c @@ -35,11 +35,24 @@ #include #include #include +#include +#include #include "utils.h" #include "network.h" +#include "data-structures/destinations-graph.h" #include "bind-mount.h" +double get_time(void); + +double get_time(void) +{ + struct timeval t; + struct timezone tzp; + gettimeofday(&t, &tzp); + return t.tv_sec + t.tv_usec*1e-6; +} + #ifndef CLONE_NEWCGROUP #define CLONE_NEWCGROUP 0x02000000 /* New cgroup namespace */ #endif @@ -109,89 +122,93 @@ 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; +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; }; typedef struct _LockFile LockFile; -struct _LockFile -{ - const char *path; - int fd; - LockFile *next; +struct _LockFile { + 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; +typedef struct { + uint32_t op; + uint32_t flags; + uint32_t perms; + size_t size_arg; + uint32_t arg1_offset; + uint32_t arg2_offset; } PrivSepOp; /* @@ -224,6 +241,11 @@ _ ## name ## _append_new (void) \ return self; \ } +size_t bind_ops_quantity = 0; +DEFINE_LINKED_LIST (BindOp, bind_op) + +DEFINE_LINKED_LIST (TempFile, temp_file) + DEFINE_LINKED_LIST (SetupOp, op) static SetupOp * @@ -250,10 +272,9 @@ lock_file_new (const char *path) typedef struct _SeccompProgram SeccompProgram; -struct _SeccompProgram -{ - struct sock_fprog program; - SeccompProgram *next; +struct _SeccompProgram { + struct sock_fprog program; + SeccompProgram *next; }; DEFINE_LINKED_LIST (SeccompProgram, seccomp_program) @@ -367,7 +388,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); } @@ -394,8 +415,7 @@ block_sigchild (void) die_with_error ("sigprocmask"); /* Reap any outstanding zombies that we may have inherited */ - while (waitpid (-1, &status, WNOHANG) > 0) - ; + while (waitpid (-1, &status, WNOHANG) > 0); } static void @@ -511,7 +531,7 @@ monitor_child (int event_fd, pid_t child_pid, int setup_finished_fd) dont_close[j++] = opt_json_status_fd; if (setup_finished_fd != -1) dont_close[j++] = setup_finished_fd; - assert (j < sizeof(dont_close)/sizeof(*dont_close)); + assert (j < sizeof (dont_close) / sizeof (*dont_close)); fdwalk (proc_fd, close_extra_fds, dont_close); sigemptyset (&mask); @@ -601,10 +621,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) @@ -629,7 +649,7 @@ do_init (int event_fd, pid_t initial_pid) { initial_exit_status = propagate_exit_status (status); - if(event_fd != -1) + if (event_fd != -1) { uint64_t val; int res UNUSED; @@ -679,8 +699,8 @@ static uint32_t requested_caps[2] = {0, 0}; static void set_required_caps (void) { - struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 }; - struct __user_cap_data_struct data[2] = { { 0 } }; + struct __user_cap_header_struct hdr = {_LINUX_CAPABILITY_VERSION_3, 0}; + struct __user_cap_data_struct data[2] = {{0}}; /* Drop all non-require capabilities */ data[0].effective = REQUIRED_CAPS_0; @@ -696,8 +716,8 @@ set_required_caps (void) static void drop_all_caps (bool keep_requested_caps) { - struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 }; - struct __user_cap_data_struct data[2] = { { 0 } }; + struct __user_cap_header_struct hdr = {_LINUX_CAPABILITY_VERSION_3, 0}; + struct __user_cap_data_struct data[2] = {{0}}; if (keep_requested_caps) { @@ -736,10 +756,10 @@ drop_all_caps (bool keep_requested_caps) static bool has_caps (void) { - struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 }; - struct __user_cap_data_struct data[2] = { { 0 } }; + struct __user_cap_header_struct hdr = {_LINUX_CAPABILITY_VERSION_3, 0}; + struct __user_cap_data_struct data[2] = {{0}}; - if (capget (&hdr, data) < 0) + if (capget (&hdr, data) < 0) die_with_error ("capget failed"); return data[0].permitted != 0 || data[1].permitted != 0; @@ -862,7 +882,7 @@ acquire_privs (void) /* setfsuid can't properly report errors, check that it worked (as per manpage) */ new_fsuid = setfsuid (-1); if (new_fsuid != real_uid) - die ("Unable to set fsuid (was %d)", (int)new_fsuid); + die ("Unable to set fsuid (was %d)", (int) new_fsuid); /* We never need capabilities after execve(), so lets drop everything from the bounding set */ drop_cap_bounding_set (TRUE); @@ -883,8 +903,8 @@ acquire_privs (void) * can drop them via --cap-drop. This is used by at least rpm-ostree. * Note this needs to happen before the argument parsing of --cap-drop. */ - struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 }; - struct __user_cap_data_struct data[2] = { { 0 } }; + struct __user_cap_header_struct hdr = {_LINUX_CAPABILITY_VERSION_3, 0}; + struct __user_cap_data_struct data[2] = {{0}}; if (capget (&hdr, data) < 0) die_with_error ("capget (for uid == 0) failed"); @@ -952,14 +972,14 @@ write_uid_gid_map (uid_t sandbox_uid, uid_t sandbox_gid, uid_t parent_gid, pid_t pid, - bool deny_groups, - bool map_root) + bool deny_groups, + bool map_root) { cleanup_free char *uid_map = NULL; cleanup_free char *gid_map = NULL; cleanup_free char *dir = NULL; cleanup_fd int dir_fd = -1; - uid_t old_fsuid = (uid_t)-1; + uid_t old_fsuid = (uid_t) -1; if (pid == -1) dir = xstrdup ("self"); @@ -1014,14 +1034,15 @@ write_uid_gid_map (uid_t sandbox_uid, } static void -privileged_op (int privileged_op_socket, - uint32_t op, - uint32_t flags, - uint32_t perms, - size_t size_arg, +privileged_op (int privileged_op_socket, + uint32_t op, + uint32_t flags, + uint32_t perms, + size_t size_arg, const char *arg1, const char *arg2) { + BindOp *bop; bind_mount_result bind_result; char *failing_path = NULL; @@ -1059,7 +1080,7 @@ privileged_op (int privileged_op_socket, if (arg2 != NULL) strcpy ((char *) buffer + arg2_offset, arg2); - if (write (privileged_op_socket, buffer, buffer_size) != (ssize_t)buffer_size) + if (write (privileged_op_socket, buffer, buffer_size) != (ssize_t) buffer_size) die ("Can't write to privileged_op_socket"); if (read (privileged_op_socket, buffer, 1) != 1) @@ -1086,78 +1107,84 @@ privileged_op (int privileged_op_socket, */ switch (op) { - case PRIV_SEP_OP_DONE: - break; + 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_REMOUNT_RO_NO_RECURSIVE: - if (bind_result != BIND_MOUNT_SUCCESS) - die_with_bind_result (bind_result, errno, failing_path, - "Can't remount readonly on %s", arg2); +// if (mount (NULL, arg2, NULL, MS_SILENT | MS_REMOUNT | MS_NOSUID | MS_RDONLY, NULL) != 0) +// die_with_mount_error ("Can't remount readonly on %s", arg2); + + bind_ops_quantity++; + bop = _bind_op_append_new (); + bop->source = NULL; + bop->dest = xstrdup (arg2); + bop->options = BIND_READONLY; + bop->fd = proc_fd; - assert (failing_path == NULL); /* otherwise we would have died */ 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 (mount (arg1, arg2, NULL, MS_SILENT | MS_BIND | MS_REC, NULL) != 0) + die_with_mount_error ("Can't bind mount %s on %s", arg1, arg2); - if (bind_result != BIND_MOUNT_SUCCESS) - die_with_bind_result (bind_result, errno, failing_path, - "Can't bind mount %s on %s", arg1, arg2); + bind_ops_quantity++; + bop = _bind_op_append_new (); + bop->source = xstrdup (arg1); + bop->dest = xstrdup (arg2); + bop->options = 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); + 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); - else - mode = xasprintf ("mode=%#o", perms); + 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); - break; - } + 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); + 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); 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); 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"); - if (sethostname (arg1, strlen(arg1)) != 0) + 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); } } @@ -1167,8 +1194,9 @@ privileged_op (int privileged_op_socket, */ static void setup_newroot (bool unshare_pid, - int privileged_op_socket) + int privileged_op_socket) { + double startTime = get_time(); SetupOp *op; for (op = ops; op != NULL; op = op->next) @@ -1187,7 +1215,7 @@ setup_newroot (bool unshare_pid, { if (op->flags & ALLOW_NOTEXIST && errno == ENOENT) continue; /* Ignore and move on */ - die_with_error("Can't get type of source %s", op->source); + die_with_error ("Can't get type of source %s", op->source); } } @@ -1215,16 +1243,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 +1261,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) { @@ -1261,7 +1289,7 @@ setup_newroot (bool unshare_pid, problematic (for instance /proc/sysrq-trigger lets you shut down the machine if you have write access). We should not have access to these as a non-privileged user, but lets cover them anyway just to make sure */ - static const char * const cover_proc_dirs[] = { "sys", "sysrq-trigger", "irq", "bus" }; + static const char *const cover_proc_dirs[] = {"sys", "sysrq-trigger", "irq", "bus"}; for (i = 0; i < N_ELEMENTS (cover_proc_dirs); i++) { cleanup_free char *subdir = strconcat3 (dest, "/", cover_proc_dirs[i]); @@ -1281,15 +1309,15 @@ 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, dest, NULL); - static const char *const devnodes[] = { "null", "zero", "full", "random", "urandom", "tty" }; + static const char *const devnodes[] = {"null", "zero", "full", "random", "urandom", "tty"}; for (i = 0; i < N_ELEMENTS (devnodes); i++) { cleanup_free char *node_dest = strconcat3 (dest, "/", devnodes[i]); @@ -1301,7 +1329,7 @@ setup_newroot (bool unshare_pid, node_src, node_dest); } - static const char *const stdionodes[] = { "stdin", "stdout", "stderr" }; + static const char *const stdionodes[] = {"stdin", "stdout", "stderr"}; for (i = 0; i < N_ELEMENTS (stdionodes); i++) { cleanup_free char *target = xasprintf ("/proc/self/fd/%d", i); @@ -1311,11 +1339,13 @@ setup_newroot (bool unshare_pid, } /* /dev/fd and /dev/core - legacy, but both nspawn and docker do these */ - { cleanup_free char *dev_fd = strconcat (dest, "/fd"); + { + cleanup_free char *dev_fd = strconcat (dest, "/fd"); if (symlink ("/proc/self/fd", dev_fd) < 0) die_with_error ("Can't create symlink %s", dev_fd); } - { cleanup_free char *dev_core = strconcat (dest, "/core"); + { + cleanup_free char *dev_core = strconcat (dest, "/core"); if (symlink ("/proc/kcore", dev_core) < 0) die_with_error ("Can't create symlink %s", dev_core); } @@ -1357,8 +1387,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 +1400,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 +1419,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 +1434,69 @@ 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); + + /// Todo + 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,19 +1519,40 @@ 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); } } + + char *failing_path = NULL; + bind_mount_result result = bind_mount_fixup (proc_fd, bind_ops, bind_ops_quantity, &failing_path); + + if (result != BIND_MOUNT_SUCCESS) + die_with_bind_result (result, errno, failing_path, + "Can't bind mount %s on %s", NULL, NULL); + + assert(failing_path == NULL); + + /* Remove files 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. */ + + for(TempFile* temp_file = temp_files; temp_file != NULL; temp_file = temp_file->next) + unlink (temp_file->dest); + privileged_op (privileged_op_socket, PRIV_SEP_OP_DONE, 0, 0, 0, NULL, NULL); + + double endTime = get_time(); + double timeElapsed = endTime - startTime; + printf("Initialized on %f\n\n\n", timeElapsed); } /* Do not leak file descriptors already used by setup_newroot () */ @@ -1535,42 +1586,41 @@ 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) { if (op->flags & ALLOW_NOTEXIST && errno == ENOENT) op->source = old_source; else - die_with_error("Can't find source path %s", old_source); + die_with_error ("Can't find source path %s", old_source); } 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; } } } - static const char * -resolve_string_offset (void *buffer, - size_t buffer_size, +resolve_string_offset (void *buffer, + size_t buffer_size, uint32_t offset) { if (offset == 0) @@ -1583,12 +1633,12 @@ resolve_string_offset (void *buffer, } static uint32_t -read_priv_sec_op (int read_socket, - void *buffer, - size_t buffer_size, - uint32_t *flags, - uint32_t *perms, - size_t *size_arg, +read_priv_sec_op (int read_socket, + void *buffer, + size_t buffer_size, + uint32_t *flags, + uint32_t *perms, + size_t *size_arg, const char **arg1, const char **arg2) { @@ -1605,7 +1655,7 @@ read_priv_sec_op (int read_socket, if (rec_len == 0) exit (1); /* Privileged helper died and printed error, so exit silently */ - if ((size_t)rec_len < sizeof (PrivSepOp)) + if ((size_t) rec_len < sizeof (PrivSepOp)) die ("Invalid size %zd from unprivileged helper", rec_len); /* Guarantee zero termination of any strings */ @@ -1631,7 +1681,7 @@ static int is_modifier_option (const char *option) { return strcmp (option, "--perms") == 0 - || strcmp(option, "--size") == 0; + || strcmp (option, "--size") == 0; } static void @@ -1641,10 +1691,10 @@ warn_only_last_option (const char *name) } static void -parse_args_recurse (int *argcp, +parse_args_recurse (int *argcp, const char ***argvp, - bool in_file, - int *total_parsed_argc_p) + bool in_file, + int *total_parsed_argc_p) { SetupOp *op; int argc = *argcp; @@ -1759,10 +1809,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 +1845,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) @@ -1832,8 +1882,8 @@ parse_args_recurse (int *argcp, argv++; argc--; } - else if (strcmp(arg, "--bind") == 0 || - strcmp(arg, "--bind-try") == 0) + else if (strcmp (arg, "--bind") == 0 || + strcmp (arg, "--bind-try") == 0) { if (argc < 3) die ("%s takes two arguments", arg); @@ -1841,14 +1891,14 @@ parse_args_recurse (int *argcp, op = setup_op_new (SETUP_BIND_MOUNT); op->source = argv[1]; op->dest = argv[2]; - if (strcmp(arg, "--bind-try") == 0) + if (strcmp (arg, "--bind-try") == 0) op->flags = ALLOW_NOTEXIST; argv += 2; argc -= 2; } - else if (strcmp(arg, "--ro-bind") == 0 || - strcmp(arg, "--ro-bind-try") == 0) + else if (strcmp (arg, "--ro-bind") == 0 || + strcmp (arg, "--ro-bind-try") == 0) { if (argc < 3) die ("%s takes two arguments", arg); @@ -1856,7 +1906,7 @@ parse_args_recurse (int *argcp, op = setup_op_new (SETUP_RO_BIND_MOUNT); op->source = argv[1]; op->dest = argv[2]; - if (strcmp(arg, "--ro-bind-try") == 0) + if (strcmp (arg, "--ro-bind-try") == 0) op->flags = ALLOW_NOTEXIST; argv += 2; @@ -1871,7 +1921,7 @@ parse_args_recurse (int *argcp, op = setup_op_new (SETUP_DEV_BIND_MOUNT); op->source = argv[1]; op->dest = argv[2]; - if (strcmp(arg, "--dev-bind-try") == 0) + if (strcmp (arg, "--dev-bind-try") == 0) op->flags = ALLOW_NOTEXIST; argv += 2; @@ -2324,7 +2374,7 @@ parse_args_recurse (int *argcp, if (argc < 2) die ("--uid takes an argument"); - if (opt_sandbox_uid != (uid_t)-1) + if (opt_sandbox_uid != (uid_t) -1) warn_only_last_option ("--uid"); the_uid = strtol (argv[1], &endptr, 10); @@ -2344,7 +2394,7 @@ parse_args_recurse (int *argcp, if (argc < 2) die ("--gid takes an argument"); - if (opt_sandbox_gid != (gid_t)-1) + if (opt_sandbox_gid != (gid_t) -1) warn_only_last_option ("--gid"); the_gid = strtol (argv[1], &endptr, 10); @@ -2539,11 +2589,11 @@ parse_args_recurse (int *argcp, /* If --perms was set for the current action but the current action * didn't consume the setting, apparently --perms wasn't suitable for * this action. */ - if (!is_modifier_option(arg) && next_perms >= 0) + if (!is_modifier_option (arg) && next_perms >= 0) die ("--perms must be followed by an option that creates a file"); /* Similarly for --size. */ - if (!is_modifier_option(arg) && next_size_arg != 0) + if (!is_modifier_option (arg) && next_size_arg != 0) die ("--size must be followed by --tmpfs"); argv++; @@ -2555,7 +2605,7 @@ parse_args_recurse (int *argcp, } static void -parse_args (int *argcp, +parse_args (int *argcp, const char ***argvp) { int total_parsed_argc = *argcp; @@ -2587,7 +2637,7 @@ read_overflowids (void) } static void -namespace_ids_read (pid_t pid) +namespace_ids_read (pid_t pid) { cleanup_free char *dir = NULL; cleanup_fd int ns_fd = -1; @@ -2620,8 +2670,8 @@ namespace_ids_read (pid_t pid) } static void -namespace_ids_write (int fd, - bool in_json) +namespace_ids_write (int fd, + bool in_json) { NsInfo *info; @@ -2646,7 +2696,7 @@ namespace_ids_write (int fd, } int -main (int argc, +main (int argc, char **argv) { mode_t old_umask; @@ -2776,7 +2826,7 @@ main (int argc, { cleanup_free char *max_user_ns = NULL; max_user_ns = load_file_at (AT_FDCWD, "/proc/sys/user/max_user_namespaces"); - if (max_user_ns != NULL && strcmp(max_user_ns, "0\n") == 0) + if (max_user_ns != NULL && strcmp (max_user_ns, "0\n") == 0) disabled = TRUE; } @@ -2793,9 +2843,9 @@ main (int argc, __debug__ (("Creating root mount point\n")); - if (opt_sandbox_uid == (uid_t)-1) + if (opt_sandbox_uid == (uid_t) -1) opt_sandbox_uid = real_uid; - if (opt_sandbox_gid == (gid_t)-1) + if (opt_sandbox_gid == (gid_t) -1) opt_sandbox_gid = real_gid; if (!opt_unshare_user && opt_userns_fd == -1 && opt_sandbox_uid != real_uid) @@ -3178,7 +3228,8 @@ main (int argc, * We're aiming to make /newroot the real root, and get rid of /oldroot. To do * that we need a temporary place to store it before we can unmount it. */ - { cleanup_fd int oldrootfd = open ("/", O_DIRECTORY | O_RDONLY); + { + cleanup_fd int oldrootfd = open ("/", O_DIRECTORY | O_RDONLY); if (oldrootfd < 0) die_with_error ("can't open /"); if (chdir ("/newroot") != 0) 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..00537d88 --- /dev/null +++ b/data-structures/destinations-graph-ops-linked-list.c @@ -0,0 +1,65 @@ +#include "destinations-graph.h" +#include "../utils.h" + +DestinationsLinkedList * +DestinationsLinkedList_create (void) +{ + DestinationsLinkedList *list = malloc (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 = malloc (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..35cf9174 --- /dev/null +++ b/data-structures/destinations-graph-ops-node.c @@ -0,0 +1,85 @@ +#include "destinations-graph.h" + +DestinationsGraph_Node * +DestinationsGraph_Node_create (void) +{ + DestinationsGraph_Node *self = malloc (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 *graph, DestinationsGraph_Node *self) +{ + self->euler_tour_start = graph->_euler_tour_timer++; + + DestinationsLinkedList_Node *currentChild = self->children->head; + + while (currentChild != NULL) + { + DestinationsGraph_Node__euler_tour__ (graph, currentChild->value); + currentChild = currentChild->next; + } + + self->euler_tour_end = graph->_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) + fprintf (fd, "/ "); + else + fprintf (fd, "- %s ", current->path_part); + + if (current->is_mount_point) + fprintf (fd, "(*) "); + + fprintf (fd, "<==> (%d; %d)", current->euler_tour_start, current->euler_tour_end); + + fprintf (fd, "\n"); + + 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..f08db43a --- /dev/null +++ b/data-structures/destinations-graph-ops.c @@ -0,0 +1,170 @@ +#pragma once + +#include "destinations-graph.h" + +// -------------------------------------------------------------------------------------------------------------------- +// Regular methods of DestinationsGraph + +DestinationsGraph * +DestinationsGraph_create (void) +{ + DestinationsGraph *self = malloc (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; + self->_euler_tour_timer = 0; + + return self; +} + +void +DestinationsGraph_free (DestinationsGraph *self) +{ + if (self->nodev != NULL) + SumSegmentTree_free (self->nodev); + + if (self->readonly != NULL) + SumSegmentTree_free (self->readonly); + + DestinationsGraph_Node__free_recursive__ (self->root); + self->_euler_tour_timer = 0; + + 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 = strdup (*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 = strdup (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) +{ + self->_euler_tour_timer = 0; + DestinationsGraph_Node__euler_tour__ (self, self->root); +} + +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) +{ + SumSegmentTree_modify (segment_tree, node->euler_tour_start, node->euler_tour_end, 1); +} + +void +DestinationsGraph_Flags__unset_flag__ (SumSegmentTree *segment_tree, DestinationsGraph_Node *node) +{ + SumSegmentTree_modify (segment_tree, node->euler_tour_start, node->euler_tour_end, 0); +} + +bool +DestinationsGraph_Flags__check_flag__ (SumSegmentTree *segment_tree, DestinationsGraph_Node *node) +{ + return SumSegmentTree_query (segment_tree, node->euler_tour_start); +} + +void +DestinationsGraph_Flags_set_readonly (DestinationsGraph *self, DestinationsGraph_Node *node) +{ + DestinationsGraph_Flags__set_flag__ (self->readonly, node); +} + +void +DestinationsGraph_Flags_unset_readonly (DestinationsGraph *self, DestinationsGraph_Node *node) +{ + DestinationsGraph_Flags__unset_flag__ (self->readonly, node); +} + +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) +{ + DestinationsGraph_Flags__set_flag__ (self->nodev, node); +} + +void +DestinationsGraph_Flags_unset_nodev (DestinationsGraph *self, DestinationsGraph_Node *node) +{ + DestinationsGraph_Flags__unset_flag__ (self->nodev, node); +} + +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..7c77fa5c --- /dev/null +++ b/data-structures/destinations-graph.h @@ -0,0 +1,141 @@ +#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; + + unsigned int euler_tour_start; + unsigned int euler_tour_end; + +}; + +struct _DestinationsGraph { + + DestinationsGraph_Node *root; + + unsigned int count_nodes; + unsigned int count_mount_points; + + unsigned int _euler_tour_timer; + + 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 *graph, DestinationsGraph_Node *self); + +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); + +void +DestinationsGraph_Flags__unset_flag__ (SumSegmentTree *segment_tree, DestinationsGraph_Node *node); + +bool +DestinationsGraph_Flags__check_flag__ (SumSegmentTree *segment_tree, DestinationsGraph_Node *node); + +void +DestinationsGraph_Flags_set_readonly (DestinationsGraph *self, DestinationsGraph_Node *node); + +void +DestinationsGraph_Flags_unset_readonly (DestinationsGraph *self, DestinationsGraph_Node *node); + +bool +DestinationsGraph_Flags_check_readonly (DestinationsGraph *self, DestinationsGraph_Node *node); + +void +DestinationsGraph_Flags_set_nodev (DestinationsGraph *self, DestinationsGraph_Node *node); + +void +DestinationsGraph_Flags_unset_nodev (DestinationsGraph *self, DestinationsGraph_Node *node); + +bool +DestinationsGraph_Flags_check_nodev (DestinationsGraph *self, DestinationsGraph_Node *node); diff --git a/data-structures/lazy-segment-tree.c b/data-structures/lazy-segment-tree.c new file mode 100644 index 00000000..e96bc557 --- /dev/null +++ b/data-structures/lazy-segment-tree.c @@ -0,0 +1,146 @@ +#ifndef LAZY_SEGMENT_TREE_OPS +#define LAZY_SEGMENT_TREE_OPS + +#include "lazy-segment-tree.h" +#include +#include +#include +#include +#include + +/// -------------------------------------------------------------------------------------------------------------------- +/// Constructing and destructing object + +SumSegmentTree +*SumSegmentTree_create (size_t quantity) +{ + SumSegmentTree *self = malloc (sizeof (SumSegmentTree)); + assert(quantity > 0); + + // That's how we replace log2 to not link math library + // Anyway we'll not perform many operations + size_t under_logarithm = quantity - 1; + size_t levels = 1; + while (under_logarithm >>= 1) levels++; + + self->n = (1 << (levels + 1)); + + size_t elements = self->n * 2; + size_t bytes = sizeof (int) * elements; + + self->value = malloc (bytes); + memset (self->value, 0, bytes); + + self->lazy = malloc (bytes); + memset (self->lazy, 0, bytes); + + return self; +} + +void +SumSegmentTree_free (SumSegmentTree *self) +{ + self->n = 0; + free (self->value); + free (self->lazy); + free (self); +} + +/// -------------------------------------------------------------------------------------------------------------------- +/// Private methods + +void +SumSegmentTree__push__ (SumSegmentTree *self, size_t v, size_t l, size_t r); + +void +SumSegmentTree__modify__ (SumSegmentTree *self, size_t x, size_t y, int val, size_t root, size_t l, size_t r); + +int +SumSegmentTree__query__ (SumSegmentTree *self, size_t x, size_t y, size_t root, size_t l, size_t r); + +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; + } +} + +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]; +} + +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); + + int 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..a1a88c9e --- /dev/null +++ b/data-structures/lazy-segment-tree.h @@ -0,0 +1,33 @@ +#ifndef LAZY_SEGMENT_TREE +#define LAZY_SEGMENT_TREE + +#include "stddef.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..204f5a57 100644 --- a/meson.build +++ b/meson.build @@ -115,6 +115,10 @@ bwrap = executable( 'bind-mount.c', 'network.c', 'utils.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/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) From 546a380e4931338ce609888ba8287a54d20ec632 Mon Sep 17 00:00:00 2001 From: Yahor Levanenka Date: Mon, 29 Apr 2024 21:45:00 +0300 Subject: [PATCH 2/4] Little simple refactoring: 1. Enabled the usage of more safe alternatives to malloc and strdup; 2. Improved debug messages; 3. Fixed some memory leaks with usage of cleanup 4. Make comments more clear and simple to understand 5. Removed elapsed time output (added it before for comparing with old realization) 6. Improved code-style a little --- bind-mount.c | 592 +++--------------- bind-mount.h | 71 +-- bubblewrap.c | 117 ++-- .../destinations-graph-ops-linked-list.c | 5 +- data-structures/destinations-graph-ops-node.c | 21 +- data-structures/destinations-graph-ops.c | 78 +-- data-structures/destinations-graph.h | 49 +- data-structures/lazy-segment-tree.c | 30 +- data-structures/lazy-segment-tree.h | 1 + meson.build | 1 + parse-mountinfo.c | 355 +++++++++++ parse-mountinfo.h | 33 + utils.h | 8 +- 13 files changed, 642 insertions(+), 719 deletions(-) create mode 100644 parse-mountinfo.c create mode 100644 parse-mountinfo.h diff --git a/bind-mount.c b/bind-mount.c index 73ef1d43..39a0cd48 100644 --- a/bind-mount.c +++ b/bind-mount.c @@ -17,371 +17,20 @@ * */ -#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; -} - -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) +static bind_mount_result +retrieve_kernel_case (char *dest, char **result, char **failing_path) { - 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) -{ - 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 -retrieve_kernel_case (int proc_fd, char *dest, char **result, char **failing_path); - -bind_mount_result -retrieve_kernel_case (int proc_fd, char *dest, char **result, char **failing_path) -{ - 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; // 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. @@ -420,6 +69,7 @@ retrieve_kernel_case (int proc_fd, char *dest, char **result, char **failing_pat if (failing_path != NULL) *failing_path = steal_pointer (&resolved_dest); + free(kernel_case_combination); return BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD; } @@ -430,84 +80,96 @@ retrieve_kernel_case (int proc_fd, char *dest, char **result, char **failing_pat bind_mount_result bind_mount_fixup (int proc_fd, BindOp *bind_ops, size_t bind_ops_quantity, char **failing_path) { - DestinationsGraph *graph; + // 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; - DestinationsGraph_Node **mount_points = malloc (bind_ops_quantity * sizeof (DestinationsGraph_Node *)); +// 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; - // Bind_mount_fixup calls after all bind mount operations executed - // Now we only collect all information about actual mounts to the graph + __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++) { - //printf ("Mountinfo: %s\n", mount_tab[i].mountpoint); + __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); } - // Here we go through all bind operations and collect list of actual graph nodes - // that correspond these bind operations. - // And the same time we lazy propagate flags. - + // (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++) { - // Retrieve real path after mounting + __debug__("BindOp: %s (readonly %d, nodev %d)\n", + bop->dest, + (bop->options & BIND_READONLY) != 0, + (bop->options & BIND_DEVICES) != 0 + ); - char *kernel_case; - bind_mount_result result = retrieve_kernel_case (bop->fd, bop->dest, &kernel_case, failing_path); + // 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; + return result; // Get corresponding destinations graph node - // It's important that here we really shouldn't retrieve any points we haven't mounted before - // In the case we have some problems :) - + // 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); - - //printf("BindOp: %s [%d]", kernel_case, bop->options); - //if(added) - // printf (" (ADDED)"); - //printf("\n"); - //printf ("%s\n", kernel_case); - + 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; - if (readonly) - DestinationsGraph_Flags_set_readonly (graph, mount_points[current]); - - if (!readonly) - DestinationsGraph_Flags_unset_readonly (graph, mount_points[current]); - - if (!devices) - DestinationsGraph_Flags_set_nodev (graph, mount_points[current]); - - if (devices) - DestinationsGraph_Flags_unset_nodev (graph, mount_points[current]); - - -// printf ("Kernel case: %s\n", kernel_case); + DestinationsGraph_Flags_set_readonly (graph, mount_points[current], readonly); + DestinationsGraph_Flags_set_nodev (graph, mount_points[current], !devices); } - //DestinationsGraph_debug_pretty_print (graph, stdout); +#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++) { 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); @@ -515,140 +177,36 @@ bind_mount_fixup (int proc_fd, BindOp *bind_ops, size_t bind_ops_quantity, char unsigned long current_flags, new_flags; current_flags = mount_tab[i].options; - new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0); + new_flags = current_flags | MS_NOSUID | (devices ? 0 : MS_NODEV) | (readonly ? MS_RDONLY : 0); if (new_flags != current_flags) { - //printf(">>> %s (readonly %d, nodev %d) (%d | %d)\n", mount_point, readonly, !devices, current_flags, new_flags); + __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 (failing_path != NULL) *failing_path = steal_pointer (&mount_point); - return BIND_MOUNT_ERROR_REMOUNT_DEST; + return BIND_MOUNT_ERROR_REMOUNT; } } } +#ifdef DEBUG mount_tab = parse_mountinfo (proc_fd, "/newroot"); - for (int i = 0; mount_tab[i].mountpoint != NULL; i++) - printf ("@ Mountinfo: %s (%d)\n", mount_tab[i].mountpoint, mount_tab[i].options); - - return BIND_MOUNT_SUCCESS; -} - -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. */ - resolved_dest = realpath (dest, NULL); - if (resolved_dest == NULL) - return BIND_MOUNT_ERROR_REALPATH_DEST; - - dest_fd = open (resolved_dest, O_PATH | O_CLOEXEC); - if (dest_fd < 0) - { - if (failing_path != NULL) - *failing_path = steal_pointer (&resolved_dest); - - return BIND_MOUNT_ERROR_REOPEN_DEST; - } - - /* If we are in a case-insensitive filesystem, mountinfo might contain a - * different case combination of the path we requested to mount. - * This is due to the fact that the kernel, as of the beginning of 2021, - * populates mountinfo with whatever case combination first appeared in the - * dcache; kernel developers plan to change this in future so that it - * reflects the on-disk encoding instead. - * To avoid throwing an error when this happens, we use readlink() result - * 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); - - 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); - - errno = EINVAL; - return BIND_MOUNT_ERROR_FIND_DEST_MOUNT; - } - - 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) - { - if (failing_path != NULL) - *failing_path = steal_pointer (&resolved_dest); - - return BIND_MOUNT_ERROR_REMOUNT_DEST; - } - - /* 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) - { - for (i = 1; mount_tab[i].mountpoint != NULL; i++) - { - 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) - { - /* 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; - } - } - } - } + __debug__("(Final) Mountinfo: %s (flags %lu)\n", mount_tab[i].mountpoint, mount_tab[i].options); +#endif return BIND_MOUNT_SUCCESS; } @@ -689,12 +247,7 @@ bind_mount_result_to_string (bind_mount_result res, 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; @@ -740,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; 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 e57a99ce..b0373632 100644 --- a/bind-mount.h +++ b/bind-mount.h @@ -21,67 +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; typedef struct _BindOp BindOp; struct _BindOp { - int fd; - char *source; char *dest; bind_option_t options; - BindOp* next; -}; - -typedef struct MountInfo MountInfo; -struct MountInfo { - char *mountpoint; - unsigned long options; + BindOp *next; }; -typedef MountInfo *MountTab; - -static void -mount_tab_free (MountTab tab); - -#define cleanup_mount_tab __attribute__((cleanup (cleanup_mount_tabp))) - -static inline void -cleanup_mount_tabp (void *p); - -static MountTab -parse_mountinfo (int proc_fd, - const char *root_mount); +/// -------------------------------------------------------------------------------------------------------------------- +/// Functions bind_mount_result -bind_mount_fixup (int proc_fd, BindOp *bind_ops, size_t bind_ops_quantity, char** failing_path); - -bind_mount_result bind_mount (int proc_fd, - const char *src, - const char *dest, - bind_option_t options, - char **failing_path); +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 5e2ed831..989cd7e7 100644 --- a/bubblewrap.c +++ b/bubblewrap.c @@ -35,24 +35,12 @@ #include #include #include -#include -#include #include "utils.h" #include "network.h" #include "data-structures/destinations-graph.h" #include "bind-mount.h" -double get_time(void); - -double get_time(void) -{ - struct timeval t; - struct timezone tzp; - gettimeofday(&t, &tzp); - return t.tv_sec + t.tv_usec*1e-6; -} - #ifndef CLONE_NEWCGROUP #define CLONE_NEWCGROUP 0x02000000 /* New cgroup namespace */ #endif @@ -187,7 +175,7 @@ struct _LockFile { typedef struct _TempFile TempFile; struct _TempFile { - const char* dest; + const char *dest; TempFile *next; }; @@ -244,6 +232,18 @@ _ ## name ## _append_new (void) \ 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) @@ -559,7 +559,7 @@ monitor_child (int event_fd, pid_t child_pid, int setup_finished_fd) die_with_error ("poll"); /* Always read from the eventfd first, if pid 2 died then pid 1 often - * dies too, and we could race, reporting that first and we'd lose + * dies too, and we could race, reporting that first, and we'd lose * the real exit status. */ if (event_fd != -1) { @@ -1042,7 +1042,6 @@ privileged_op (int privileged_op_socket, const char *arg1, const char *arg2) { - BindOp *bop; bind_mount_result bind_result; char *failing_path = NULL; @@ -1108,38 +1107,46 @@ privileged_op (int privileged_op_socket, switch (op) { case PRIV_SEP_OP_DONE: - break; + bind_result = bind_mount_fixup (proc_fd, bind_ops, bind_ops_quantity, &failing_path); - case PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE: + if (bind_result != BIND_MOUNT_SUCCESS) + die_with_bind_result (bind_result, errno, failing_path, + "Setting the correct mount flags up (fixup) failed" + ); + + assert(failing_path == NULL); -// if (mount (NULL, arg2, NULL, MS_SILENT | MS_REMOUNT | MS_NOSUID | MS_RDONLY, NULL) != 0) -// die_with_mount_error ("Can't remount readonly on %s", arg2); + /* 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. */ - bind_ops_quantity++; - bop = _bind_op_append_new (); - bop->source = NULL; - bop->dest = xstrdup (arg2); - bop->options = BIND_READONLY; - bop->fd = proc_fd; + for (TempFile *temp_file = temp_files; temp_file != NULL; temp_file = temp_file->next) + unlink (temp_file->dest); break; + case PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE: + bind_op_new ((char *) arg2, BIND_READONLY); + break; + case PRIV_SEP_OP_BIND_MOUNT: if (mount (arg1, arg2, NULL, MS_SILENT | MS_BIND | MS_REC, NULL) != 0) - die_with_mount_error ("Can't bind mount %s on %s", arg1, arg2); - - bind_ops_quantity++; - bop = _bind_op_append_new (); - bop->source = xstrdup (arg1); - bop->dest = xstrdup (arg2); - bop->options = flags; + 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); 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); + else + bind_op_new ((char *) arg1, 0); break; @@ -1160,6 +1167,9 @@ privileged_op (int privileged_op_socket, 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; } @@ -1167,11 +1177,18 @@ privileged_op (int privileged_op_socket, 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 + 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); + else + bind_op_new ((char *) arg1, BIND_DEVICES); + break; case PRIV_SEP_OP_SET_HOSTNAME: @@ -1196,7 +1213,6 @@ static void setup_newroot (bool unshare_pid, int privileged_op_socket) { - double startTime = get_time(); SetupOp *op; for (op = ops; op != NULL; op = op->next) @@ -1488,10 +1504,11 @@ setup_newroot (bool unshare_pid, (op->type == SETUP_MAKE_RO_BIND_FILE ? BIND_READONLY : 0), 0, 0, tempfile, dest); - /// Todo - TempFile* temp = _temp_file_append_new(); + /* 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; @@ -1531,28 +1548,8 @@ setup_newroot (bool unshare_pid, } } - char *failing_path = NULL; - bind_mount_result result = bind_mount_fixup (proc_fd, bind_ops, bind_ops_quantity, &failing_path); - - if (result != BIND_MOUNT_SUCCESS) - die_with_bind_result (result, errno, failing_path, - "Can't bind mount %s on %s", NULL, NULL); - - assert(failing_path == NULL); - - /* Remove files 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. */ - - for(TempFile* temp_file = temp_files; temp_file != NULL; temp_file = temp_file->next) - unlink (temp_file->dest); - privileged_op (privileged_op_socket, PRIV_SEP_OP_DONE, 0, 0, 0, NULL, NULL); - - double endTime = get_time(); - double timeElapsed = endTime - startTime; - printf("Initialized on %f\n\n\n", timeElapsed); } /* Do not leak file descriptors already used by setup_newroot () */ @@ -2841,7 +2838,7 @@ main (int argc, if (argc <= 0) usage (EXIT_FAILURE, stderr); - __debug__ (("Creating root mount point\n")); + __debug__ ("Creating root mount point\n"); if (opt_sandbox_uid == (uid_t) -1) opt_sandbox_uid = real_uid; @@ -2877,7 +2874,7 @@ main (int argc, * access ourselves. */ base_path = "/tmp"; - __debug__ (("creating new namespace\n")); + __debug__ ("creating new namespace\n"); if (opt_unshare_pid && !opt_as_pid_1) { @@ -3355,7 +3352,7 @@ main (int argc, if (label_exec (opt_exec_label) == -1) die_with_error ("label_exec %s", argv[0]); - __debug__ (("forking for child\n")); + __debug__ ("forking for child\n"); if (!opt_as_pid_1 && (opt_unshare_pid || lock_files != NULL || opt_sync_fd != -1)) { @@ -3393,7 +3390,7 @@ main (int argc, } } - __debug__ (("launch executable %s\n", argv[0])); + __debug__ ("launch executable %s\n", argv[0]); if (proc_fd != -1) close (proc_fd); diff --git a/data-structures/destinations-graph-ops-linked-list.c b/data-structures/destinations-graph-ops-linked-list.c index 00537d88..a3ec1fed 100644 --- a/data-structures/destinations-graph-ops-linked-list.c +++ b/data-structures/destinations-graph-ops-linked-list.c @@ -1,10 +1,9 @@ #include "destinations-graph.h" -#include "../utils.h" DestinationsLinkedList * DestinationsLinkedList_create (void) { - DestinationsLinkedList *list = malloc (sizeof (DestinationsLinkedList)); + DestinationsLinkedList *list = xcalloc(1, sizeof (DestinationsLinkedList)); list->head = NULL; list->tail = NULL; list->count = 0; @@ -31,7 +30,7 @@ DestinationsLinkedList_push_back (DestinationsLinkedList *self, DestinationsGraph_Node *node) { - DestinationsLinkedList_Node *pushed = malloc (sizeof (DestinationsLinkedList_Node)); + DestinationsLinkedList_Node *pushed = xcalloc(1, sizeof (DestinationsLinkedList_Node)); pushed->value = node; pushed->next = NULL; diff --git a/data-structures/destinations-graph-ops-node.c b/data-structures/destinations-graph-ops-node.c index 35cf9174..9f379ed1 100644 --- a/data-structures/destinations-graph-ops-node.c +++ b/data-structures/destinations-graph-ops-node.c @@ -3,7 +3,7 @@ DestinationsGraph_Node * DestinationsGraph_Node_create (void) { - DestinationsGraph_Node *self = malloc (sizeof (DestinationsGraph_Node)); + DestinationsGraph_Node *self = xcalloc(1, sizeof (DestinationsGraph_Node)); self->path_part = NULL; self->is_mount_point = FALSE; @@ -28,13 +28,13 @@ DestinationsGraph_Node_free (DestinationsGraph_Node *self) } void -DestinationsGraph_Node__free_recursive__ (DestinationsGraph_Node *self) +DestinationsGraph_Node_free_recursive (DestinationsGraph_Node *self) { DestinationsLinkedList_Node *currentChild = self->children->head; while (currentChild != NULL) { - DestinationsGraph_Node__free_recursive__ (currentChild->value); + DestinationsGraph_Node_free_recursive (currentChild->value); currentChild = currentChild->next; } @@ -42,19 +42,19 @@ DestinationsGraph_Node__free_recursive__ (DestinationsGraph_Node *self) } void -DestinationsGraph_Node__euler_tour__ (DestinationsGraph *graph, DestinationsGraph_Node *self) +DestinationsGraph_Node__euler_tour__ (DestinationsGraph_Node *self, size_t *euler_tour_timer) { - self->euler_tour_start = graph->_euler_tour_timer++; + self->euler_tour_start = (*euler_tour_timer)++; DestinationsLinkedList_Node *currentChild = self->children->head; while (currentChild != NULL) { - DestinationsGraph_Node__euler_tour__ (graph, currentChild->value); + DestinationsGraph_Node__euler_tour__ (currentChild->value, euler_tour_timer); currentChild = currentChild->next; } - self->euler_tour_end = graph->_euler_tour_timer++; + self->euler_tour_end = (*euler_tour_timer)++; } void @@ -64,16 +64,15 @@ DestinationsGraph_Node_debug_pretty_print (FILE *fd, DestinationsGraph_Node *cur fputs (" |", fd); if (depth == 0) - fprintf (fd, "/ "); + fputs ("/ ", fd); else fprintf (fd, "- %s ", current->path_part); if (current->is_mount_point) - fprintf (fd, "(*) "); + fputs("(*)", fd); - fprintf (fd, "<==> (%d; %d)", current->euler_tour_start, current->euler_tour_end); + fputs ("\n", fd); - fprintf (fd, "\n"); DestinationsLinkedList_Node *currentChild = current->children->head; diff --git a/data-structures/destinations-graph-ops.c b/data-structures/destinations-graph-ops.c index f08db43a..97bcd5df 100644 --- a/data-structures/destinations-graph-ops.c +++ b/data-structures/destinations-graph-ops.c @@ -1,14 +1,39 @@ -#pragma once - #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 = malloc (sizeof (DestinationsGraph)); + DestinationsGraph *self = xcalloc(1, sizeof (DestinationsGraph)); // Create initial root node. // It's a little special because it has no path_part. @@ -19,7 +44,6 @@ DestinationsGraph_create (void) self->root = root; self->count_mount_points = 0; self->count_nodes = 1; - self->_euler_tour_timer = 0; return self; } @@ -33,8 +57,8 @@ DestinationsGraph_free (DestinationsGraph *self) if (self->readonly != NULL) SumSegmentTree_free (self->readonly); - DestinationsGraph_Node__free_recursive__ (self->root); - self->_euler_tour_timer = 0; + if(self->root != NULL) + DestinationsGraph_Node_free_recursive (self->root); free(self); } @@ -45,7 +69,7 @@ DestinationsGraph_ensure_mount_point (DestinationsGraph *self, char **destinatio // 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 = strdup (*destination_path); + char *destination_path_dup = xstrdup (*destination_path); // Start with root DestinationsGraph_Node *current = self->root; @@ -64,7 +88,7 @@ DestinationsGraph_ensure_mount_point (DestinationsGraph *self, char **destinatio if (next == NULL) { DestinationsGraph_Node *new_node = DestinationsGraph_Node_create (); - new_node->path_part = strdup (next_path_part); + new_node->path_part = xstrdup (next_path_part); DestinationsLinkedList_push_back (current->children, new_node); self->count_nodes++; @@ -92,8 +116,8 @@ DestinationsGraph_ensure_mount_point (DestinationsGraph *self, char **destinatio void DestinationsGraph__euler_tour__ (DestinationsGraph *self) { - self->_euler_tour_timer = 0; - DestinationsGraph_Node__euler_tour__ (self, self->root); + size_t euler_tour_timer = 0; + DestinationsGraph_Node__euler_tour__ (self->root, &euler_tour_timer); } void @@ -116,33 +140,23 @@ DestinationsGraph_Flags_init (DestinationsGraph *self) } void -DestinationsGraph_Flags__set_flag__ (SumSegmentTree *segment_tree, DestinationsGraph_Node *node) +DestinationsGraph_Flags__set_flag__ (SumSegmentTree *segment_tree, DestinationsGraph_Node *node, bool value) { - SumSegmentTree_modify (segment_tree, node->euler_tour_start, node->euler_tour_end, 1); -} - -void -DestinationsGraph_Flags__unset_flag__ (SumSegmentTree *segment_tree, DestinationsGraph_Node *node) -{ - SumSegmentTree_modify (segment_tree, node->euler_tour_start, node->euler_tour_end, 0); + 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) { - return SumSegmentTree_query (segment_tree, node->euler_tour_start); + // 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) +DestinationsGraph_Flags_set_readonly (DestinationsGraph *self, DestinationsGraph_Node *node, bool value) { - DestinationsGraph_Flags__set_flag__ (self->readonly, node); -} - -void -DestinationsGraph_Flags_unset_readonly (DestinationsGraph *self, DestinationsGraph_Node *node) -{ - DestinationsGraph_Flags__unset_flag__ (self->readonly, node); + DestinationsGraph_Flags__set_flag__ (self->readonly, node, value); } bool @@ -152,15 +166,9 @@ DestinationsGraph_Flags_check_readonly (DestinationsGraph *self, DestinationsGra } void -DestinationsGraph_Flags_set_nodev (DestinationsGraph *self, DestinationsGraph_Node *node) -{ - DestinationsGraph_Flags__set_flag__ (self->nodev, node); -} - -void -DestinationsGraph_Flags_unset_nodev (DestinationsGraph *self, DestinationsGraph_Node *node) +DestinationsGraph_Flags_set_nodev (DestinationsGraph *self, DestinationsGraph_Node *node, bool value) { - DestinationsGraph_Flags__unset_flag__ (self->nodev, node); + DestinationsGraph_Flags__set_flag__ (self->nodev, node, value); } bool diff --git a/data-structures/destinations-graph.h b/data-structures/destinations-graph.h index 7c77fa5c..e2fa6768 100644 --- a/data-structures/destinations-graph.h +++ b/data-structures/destinations-graph.h @@ -24,8 +24,8 @@ struct _DestinationsGraph_Node { DestinationsLinkedList *children; - unsigned int euler_tour_start; - unsigned int euler_tour_end; + size_t euler_tour_start; + size_t euler_tour_end; }; @@ -33,13 +33,11 @@ struct _DestinationsGraph { DestinationsGraph_Node *root; - unsigned int count_nodes; - unsigned int count_mount_points; + size_t count_nodes; + size_t count_mount_points; - unsigned int _euler_tour_timer; - - SumSegmentTree* nodev; - SumSegmentTree* readonly; + SumSegmentTree *nodev; + SumSegmentTree *readonly; }; @@ -82,10 +80,10 @@ void DestinationsGraph_Node_free (DestinationsGraph_Node *self); void -DestinationsGraph_Node__free_recursive__ (DestinationsGraph_Node *self); +DestinationsGraph_Node_free_recursive (DestinationsGraph_Node *self); void -DestinationsGraph_Node__euler_tour__ (DestinationsGraph *graph, DestinationsGraph_Node *self); +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); @@ -103,7 +101,7 @@ void DestinationsLinkedList_push_back (DestinationsLinkedList *self, DestinationsGraph_Node *node); -DestinationsGraph_Node* +DestinationsGraph_Node * DestinationsLinkedList_find_by_path_part (DestinationsLinkedList *self, char *path_part); @@ -114,28 +112,35 @@ void DestinationsGraph_Flags_init (DestinationsGraph *self); void -DestinationsGraph_Flags__set_flag__ (SumSegmentTree *segment_tree, DestinationsGraph_Node *node); - -void -DestinationsGraph_Flags__unset_flag__ (SumSegmentTree *segment_tree, DestinationsGraph_Node *node); +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); +DestinationsGraph_Flags_set_readonly (DestinationsGraph *self, DestinationsGraph_Node *node, bool value); + +bool +DestinationsGraph_Flags_check_readonly (DestinationsGraph *self, DestinationsGraph_Node *node); void -DestinationsGraph_Flags_unset_readonly (DestinationsGraph *self, DestinationsGraph_Node *node); +DestinationsGraph_Flags_set_nodev (DestinationsGraph *self, DestinationsGraph_Node *node, bool value); bool -DestinationsGraph_Flags_check_readonly (DestinationsGraph *self, DestinationsGraph_Node *node); +DestinationsGraph_Flags_check_nodev (DestinationsGraph *self, DestinationsGraph_Node *node); + +/// -------------------------------------------------------------------------------------------------------------------- +/// Safety void -DestinationsGraph_Flags_set_nodev (DestinationsGraph *self, DestinationsGraph_Node *node); +cleanup_destinations_graphp (void *p); void -DestinationsGraph_Flags_unset_nodev (DestinationsGraph *self, DestinationsGraph_Node *node); +cleanup_destinations_graph_nodep (void *p); -bool -DestinationsGraph_Flags_check_nodev (DestinationsGraph *self, DestinationsGraph_Node *node); +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 index e96bc557..0ab449b7 100644 --- a/data-structures/lazy-segment-tree.c +++ b/data-structures/lazy-segment-tree.c @@ -3,9 +3,7 @@ #include "lazy-segment-tree.h" #include -#include #include -#include #include /// -------------------------------------------------------------------------------------------------------------------- @@ -14,25 +12,21 @@ SumSegmentTree *SumSegmentTree_create (size_t quantity) { - SumSegmentTree *self = malloc (sizeof (SumSegmentTree)); + SumSegmentTree *self = xcalloc(1, sizeof (SumSegmentTree)); assert(quantity > 0); - // That's how we replace log2 to not link math library - // Anyway we'll not perform many operations + // 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; - size_t bytes = sizeof (int) * elements; - self->value = malloc (bytes); - memset (self->value, 0, bytes); - - self->lazy = malloc (bytes); - memset (self->lazy, 0, bytes); + self->value = xcalloc (elements, sizeof (int)); + self->lazy = xcalloc (elements, sizeof (int)); return self; } @@ -49,16 +43,16 @@ SumSegmentTree_free (SumSegmentTree *self) /// -------------------------------------------------------------------------------------------------------------------- /// Private methods -void +static void SumSegmentTree__push__ (SumSegmentTree *self, size_t v, size_t l, size_t r); -void +static void SumSegmentTree__modify__ (SumSegmentTree *self, size_t x, size_t y, int val, size_t root, size_t l, size_t r); -int +static int SumSegmentTree__query__ (SumSegmentTree *self, size_t x, size_t y, size_t root, size_t l, size_t r); -void +static void SumSegmentTree__push__ (SumSegmentTree *self, size_t v, size_t l, size_t r) { if (self->lazy[v] != -1 && v < self->n) @@ -78,7 +72,7 @@ SumSegmentTree__push__ (SumSegmentTree *self, size_t v, size_t l, size_t r) } } -void +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 @@ -106,7 +100,7 @@ SumSegmentTree__modify__ (SumSegmentTree *self, size_t x, size_t y, int val, siz self->value[root] = self->value[root << 1] + self->value[root << 1 | 1]; } -int +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 @@ -122,7 +116,7 @@ SumSegmentTree__query__ (SumSegmentTree *self, size_t x, size_t y, size_t root, // Otherwise, we propagate lazy value and recurse into subtrees SumSegmentTree__push__ (self, root, l, r); - int mid = (l + r) >> 1; + 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); diff --git a/data-structures/lazy-segment-tree.h b/data-structures/lazy-segment-tree.h index a1a88c9e..ab102697 100644 --- a/data-structures/lazy-segment-tree.h +++ b/data-structures/lazy-segment-tree.h @@ -2,6 +2,7 @@ #define LAZY_SEGMENT_TREE #include "stddef.h" +#include "../utils.h" /// -------------------------------------------------------------------------------------------------------------------- /// Type declarations diff --git a/meson.build b/meson.build index 204f5a57..0e9c2f66 100644 --- a/meson.build +++ b/meson.build @@ -115,6 +115,7 @@ 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', diff --git a/parse-mountinfo.c b/parse-mountinfo.c new file mode 100644 index 00000000..e429e429 --- /dev/null +++ b/parse-mountinfo.c @@ -0,0 +1,355 @@ +#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..b85c0d76 --- /dev/null +++ b/parse-mountinfo.h @@ -0,0 +1,33 @@ +#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.h b/utils.h index 9f17297d..f6a003b9 100644 --- a/utils.h +++ b/utils.h @@ -31,10 +31,12 @@ #include #include -#if 0 -#define __debug__(x) printf x +#define DEBUG + +#ifdef DEBUG +#define __debug__(...) do { fprintf(stderr, __VA_ARGS__); } while (0) #else -#define __debug__(x) +#define __debug__(...) #endif #define UNUSED __attribute__((__unused__)) From 5616bebe72514df2eac9d16d2727ade0b2fd8ace Mon Sep 17 00:00:00 2001 From: Yahor Levanenka Date: Tue, 30 Apr 2024 00:45:09 +0300 Subject: [PATCH 3/4] Added source files to build system; May be a breaking change, so I changed the version. Disabled DEBUG mode --- Makefile-bwrap.am | 8 ++++++++ meson.build | 2 +- parse-mountinfo.c | 19 +++++++++++++++++++ parse-mountinfo.h | 19 +++++++++++++++++++ utils.h | 2 -- 5 files changed, 47 insertions(+), 3 deletions(-) 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/meson.build b/meson.build index 0e9c2f66..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', diff --git a/parse-mountinfo.c b/parse-mountinfo.c index e429e429..d2f506dd 100644 --- a/parse-mountinfo.c +++ b/parse-mountinfo.c @@ -1,3 +1,22 @@ +/* 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" /// -------------------------------------------------------------------------------------------------------------------- diff --git a/parse-mountinfo.h b/parse-mountinfo.h index b85c0d76..a78f1709 100644 --- a/parse-mountinfo.h +++ b/parse-mountinfo.h @@ -1,3 +1,22 @@ +/* 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" diff --git a/utils.h b/utils.h index f6a003b9..ccc38be1 100644 --- a/utils.h +++ b/utils.h @@ -31,8 +31,6 @@ #include #include -#define DEBUG - #ifdef DEBUG #define __debug__(...) do { fprintf(stderr, __VA_ARGS__); } while (0) #else From e28d79f3a90cc7d430e619431787ed4e7da5b86f Mon Sep 17 00:00:00 2001 From: Yahor Levanenka Date: Tue, 30 Apr 2024 11:24:40 +0300 Subject: [PATCH 4/4] Fixed formatting back --- bubblewrap.c | 183 ++++++++++++++++++++++++++------------------------- 1 file changed, 92 insertions(+), 91 deletions(-) diff --git a/bubblewrap.c b/bubblewrap.c index 989cd7e7..842762d3 100644 --- a/bubblewrap.c +++ b/bubblewrap.c @@ -38,7 +38,6 @@ #include "utils.h" #include "network.h" -#include "data-structures/destinations-graph.h" #include "bind-mount.h" #ifndef CLONE_NEWCGROUP @@ -111,21 +110,21 @@ typedef struct _NsInfo NsInfo; struct _NsInfo { const char *name; - bool *do_unshare; - ino_t id; + bool *do_unshare; + ino_t id; }; static NsInfo ns_infos[] = { {"cgroup", &opt_unshare_cgroup, 0}, {"ipc", &opt_unshare_ipc, 0}, - {"mnt", NULL, 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} + {NULL, NULL, 0} }; typedef enum { @@ -153,23 +152,25 @@ typedef enum { typedef struct _SetupOp SetupOp; -struct _SetupOp { +struct _SetupOp +{ SetupOpType type; const char *source; const char *dest; - int fd; + int fd; SetupOpFlag flags; - int perms; - size_t size; /* number of bytes, zero means unset/default */ - SetupOp *next; + int perms; + size_t size; /* number of bytes, zero means unset/default */ + SetupOp *next; }; typedef struct _LockFile LockFile; -struct _LockFile { +struct _LockFile +{ const char *path; - int fd; - LockFile *next; + int fd; + LockFile *next; }; typedef struct _TempFile TempFile; @@ -190,11 +191,12 @@ enum { PRIV_SEP_OP_SET_HOSTNAME, }; -typedef struct { +typedef struct +{ uint32_t op; uint32_t flags; uint32_t perms; - size_t size_arg; + size_t size_arg; uint32_t arg1_offset; uint32_t arg2_offset; } PrivSepOp; @@ -272,9 +274,10 @@ lock_file_new (const char *path) typedef struct _SeccompProgram SeccompProgram; -struct _SeccompProgram { - struct sock_fprog program; - SeccompProgram *next; +struct _SeccompProgram +{ + struct sock_fprog program; + SeccompProgram *next; }; DEFINE_LINKED_LIST (SeccompProgram, seccomp_program) @@ -415,7 +418,8 @@ block_sigchild (void) die_with_error ("sigprocmask"); /* Reap any outstanding zombies that we may have inherited */ - while (waitpid (-1, &status, WNOHANG) > 0); + while (waitpid (-1, &status, WNOHANG) > 0) + ; } static void @@ -531,7 +535,7 @@ monitor_child (int event_fd, pid_t child_pid, int setup_finished_fd) dont_close[j++] = opt_json_status_fd; if (setup_finished_fd != -1) dont_close[j++] = setup_finished_fd; - assert (j < sizeof (dont_close) / sizeof (*dont_close)); + assert (j < sizeof(dont_close)/sizeof(*dont_close)); fdwalk (proc_fd, close_extra_fds, dont_close); sigemptyset (&mask); @@ -559,7 +563,7 @@ monitor_child (int event_fd, pid_t child_pid, int setup_finished_fd) die_with_error ("poll"); /* Always read from the eventfd first, if pid 2 died then pid 1 often - * dies too, and we could race, reporting that first, and we'd lose + * dies too, and we could race, reporting that first and we'd lose * the real exit status. */ if (event_fd != -1) { @@ -649,7 +653,7 @@ do_init (int event_fd, pid_t initial_pid) { initial_exit_status = propagate_exit_status (status); - if (event_fd != -1) + if(event_fd != -1) { uint64_t val; int res UNUSED; @@ -699,8 +703,8 @@ static uint32_t requested_caps[2] = {0, 0}; static void set_required_caps (void) { - struct __user_cap_header_struct hdr = {_LINUX_CAPABILITY_VERSION_3, 0}; - struct __user_cap_data_struct data[2] = {{0}}; + struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 }; + struct __user_cap_data_struct data[2] = { { 0 } }; /* Drop all non-require capabilities */ data[0].effective = REQUIRED_CAPS_0; @@ -716,8 +720,8 @@ set_required_caps (void) static void drop_all_caps (bool keep_requested_caps) { - struct __user_cap_header_struct hdr = {_LINUX_CAPABILITY_VERSION_3, 0}; - struct __user_cap_data_struct data[2] = {{0}}; + struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 }; + struct __user_cap_data_struct data[2] = { { 0 } }; if (keep_requested_caps) { @@ -756,10 +760,10 @@ drop_all_caps (bool keep_requested_caps) static bool has_caps (void) { - struct __user_cap_header_struct hdr = {_LINUX_CAPABILITY_VERSION_3, 0}; - struct __user_cap_data_struct data[2] = {{0}}; + struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 }; + struct __user_cap_data_struct data[2] = { { 0 } }; - if (capget (&hdr, data) < 0) + if (capget (&hdr, data) < 0) die_with_error ("capget failed"); return data[0].permitted != 0 || data[1].permitted != 0; @@ -882,7 +886,7 @@ acquire_privs (void) /* setfsuid can't properly report errors, check that it worked (as per manpage) */ new_fsuid = setfsuid (-1); if (new_fsuid != real_uid) - die ("Unable to set fsuid (was %d)", (int) new_fsuid); + die ("Unable to set fsuid (was %d)", (int)new_fsuid); /* We never need capabilities after execve(), so lets drop everything from the bounding set */ drop_cap_bounding_set (TRUE); @@ -903,8 +907,8 @@ acquire_privs (void) * can drop them via --cap-drop. This is used by at least rpm-ostree. * Note this needs to happen before the argument parsing of --cap-drop. */ - struct __user_cap_header_struct hdr = {_LINUX_CAPABILITY_VERSION_3, 0}; - struct __user_cap_data_struct data[2] = {{0}}; + struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 }; + struct __user_cap_data_struct data[2] = { { 0 } }; if (capget (&hdr, data) < 0) die_with_error ("capget (for uid == 0) failed"); @@ -972,14 +976,14 @@ write_uid_gid_map (uid_t sandbox_uid, uid_t sandbox_gid, uid_t parent_gid, pid_t pid, - bool deny_groups, - bool map_root) + bool deny_groups, + bool map_root) { cleanup_free char *uid_map = NULL; cleanup_free char *gid_map = NULL; cleanup_free char *dir = NULL; cleanup_fd int dir_fd = -1; - uid_t old_fsuid = (uid_t) -1; + uid_t old_fsuid = (uid_t)-1; if (pid == -1) dir = xstrdup ("self"); @@ -1034,11 +1038,11 @@ write_uid_gid_map (uid_t sandbox_uid, } static void -privileged_op (int privileged_op_socket, - uint32_t op, - uint32_t flags, - uint32_t perms, - size_t size_arg, +privileged_op (int privileged_op_socket, + uint32_t op, + uint32_t flags, + uint32_t perms, + size_t size_arg, const char *arg1, const char *arg2) { @@ -1079,7 +1083,7 @@ privileged_op (int privileged_op_socket, if (arg2 != NULL) strcpy ((char *) buffer + arg2_offset, arg2); - if (write (privileged_op_socket, buffer, buffer_size) != (ssize_t) buffer_size) + if (write (privileged_op_socket, buffer, buffer_size) != (ssize_t)buffer_size) die ("Can't write to privileged_op_socket"); if (read (privileged_op_socket, buffer, 1) != 1) @@ -1196,7 +1200,7 @@ privileged_op (int privileged_op_socket, 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) + if (sethostname (arg1, strlen(arg1)) != 0) die_with_error ("Can't set hostname to %s", arg1); break; @@ -1211,7 +1215,7 @@ privileged_op (int privileged_op_socket, */ static void setup_newroot (bool unshare_pid, - int privileged_op_socket) + int privileged_op_socket) { SetupOp *op; @@ -1231,7 +1235,7 @@ setup_newroot (bool unshare_pid, { if (op->flags & ALLOW_NOTEXIST && errno == ENOENT) continue; /* Ignore and move on */ - die_with_error ("Can't get type of source %s", op->source); + die_with_error("Can't get type of source %s", op->source); } } @@ -1305,7 +1309,7 @@ setup_newroot (bool unshare_pid, problematic (for instance /proc/sysrq-trigger lets you shut down the machine if you have write access). We should not have access to these as a non-privileged user, but lets cover them anyway just to make sure */ - static const char *const cover_proc_dirs[] = {"sys", "sysrq-trigger", "irq", "bus"}; + static const char * const cover_proc_dirs[] = { "sys", "sysrq-trigger", "irq", "bus" }; for (i = 0; i < N_ELEMENTS (cover_proc_dirs); i++) { cleanup_free char *subdir = strconcat3 (dest, "/", cover_proc_dirs[i]); @@ -1333,7 +1337,7 @@ setup_newroot (bool unshare_pid, PRIV_SEP_OP_TMPFS_MOUNT, 0, 0755, 0, dest, NULL); - static const char *const devnodes[] = {"null", "zero", "full", "random", "urandom", "tty"}; + static const char *const devnodes[] = { "null", "zero", "full", "random", "urandom", "tty" }; for (i = 0; i < N_ELEMENTS (devnodes); i++) { cleanup_free char *node_dest = strconcat3 (dest, "/", devnodes[i]); @@ -1345,7 +1349,7 @@ setup_newroot (bool unshare_pid, node_src, node_dest); } - static const char *const stdionodes[] = {"stdin", "stdout", "stderr"}; + static const char *const stdionodes[] = { "stdin", "stdout", "stderr" }; for (i = 0; i < N_ELEMENTS (stdionodes); i++) { cleanup_free char *target = xasprintf ("/proc/self/fd/%d", i); @@ -1355,13 +1359,11 @@ setup_newroot (bool unshare_pid, } /* /dev/fd and /dev/core - legacy, but both nspawn and docker do these */ - { - cleanup_free char *dev_fd = strconcat (dest, "/fd"); + { cleanup_free char *dev_fd = strconcat (dest, "/fd"); if (symlink ("/proc/self/fd", dev_fd) < 0) die_with_error ("Can't create symlink %s", dev_fd); } - { - cleanup_free char *dev_core = strconcat (dest, "/core"); + { cleanup_free char *dev_core = strconcat (dest, "/core"); if (symlink ("/proc/kcore", dev_core) < 0) die_with_error ("Can't create symlink %s", dev_core); } @@ -1547,7 +1549,6 @@ setup_newroot (bool unshare_pid, die ("Unexpected type %d", op->type); } } - privileged_op (privileged_op_socket, PRIV_SEP_OP_DONE, 0, 0, 0, NULL, NULL); } @@ -1593,7 +1594,7 @@ resolve_symlinks_in_ops (void) if (op->flags & ALLOW_NOTEXIST && errno == ENOENT) op->source = old_source; else - die_with_error ("Can't find source path %s", old_source); + die_with_error("Can't find source path %s", old_source); } break; @@ -1615,9 +1616,10 @@ resolve_symlinks_in_ops (void) } } + static const char * -resolve_string_offset (void *buffer, - size_t buffer_size, +resolve_string_offset (void *buffer, + size_t buffer_size, uint32_t offset) { if (offset == 0) @@ -1630,12 +1632,12 @@ resolve_string_offset (void *buffer, } static uint32_t -read_priv_sec_op (int read_socket, - void *buffer, - size_t buffer_size, - uint32_t *flags, - uint32_t *perms, - size_t *size_arg, +read_priv_sec_op (int read_socket, + void *buffer, + size_t buffer_size, + uint32_t *flags, + uint32_t *perms, + size_t *size_arg, const char **arg1, const char **arg2) { @@ -1652,7 +1654,7 @@ read_priv_sec_op (int read_socket, if (rec_len == 0) exit (1); /* Privileged helper died and printed error, so exit silently */ - if ((size_t) rec_len < sizeof (PrivSepOp)) + if ((size_t)rec_len < sizeof (PrivSepOp)) die ("Invalid size %zd from unprivileged helper", rec_len); /* Guarantee zero termination of any strings */ @@ -1678,7 +1680,7 @@ static int is_modifier_option (const char *option) { return strcmp (option, "--perms") == 0 - || strcmp (option, "--size") == 0; + || strcmp(option, "--size") == 0; } static void @@ -1688,10 +1690,10 @@ warn_only_last_option (const char *name) } static void -parse_args_recurse (int *argcp, +parse_args_recurse (int *argcp, const char ***argvp, - bool in_file, - int *total_parsed_argc_p) + bool in_file, + int *total_parsed_argc_p) { SetupOp *op; int argc = *argcp; @@ -1879,8 +1881,8 @@ parse_args_recurse (int *argcp, argv++; argc--; } - else if (strcmp (arg, "--bind") == 0 || - strcmp (arg, "--bind-try") == 0) + else if (strcmp(arg, "--bind") == 0 || + strcmp(arg, "--bind-try") == 0) { if (argc < 3) die ("%s takes two arguments", arg); @@ -1888,14 +1890,14 @@ parse_args_recurse (int *argcp, op = setup_op_new (SETUP_BIND_MOUNT); op->source = argv[1]; op->dest = argv[2]; - if (strcmp (arg, "--bind-try") == 0) + if (strcmp(arg, "--bind-try") == 0) op->flags = ALLOW_NOTEXIST; argv += 2; argc -= 2; } - else if (strcmp (arg, "--ro-bind") == 0 || - strcmp (arg, "--ro-bind-try") == 0) + else if (strcmp(arg, "--ro-bind") == 0 || + strcmp(arg, "--ro-bind-try") == 0) { if (argc < 3) die ("%s takes two arguments", arg); @@ -1903,7 +1905,7 @@ parse_args_recurse (int *argcp, op = setup_op_new (SETUP_RO_BIND_MOUNT); op->source = argv[1]; op->dest = argv[2]; - if (strcmp (arg, "--ro-bind-try") == 0) + if (strcmp(arg, "--ro-bind-try") == 0) op->flags = ALLOW_NOTEXIST; argv += 2; @@ -1918,7 +1920,7 @@ parse_args_recurse (int *argcp, op = setup_op_new (SETUP_DEV_BIND_MOUNT); op->source = argv[1]; op->dest = argv[2]; - if (strcmp (arg, "--dev-bind-try") == 0) + if (strcmp(arg, "--dev-bind-try") == 0) op->flags = ALLOW_NOTEXIST; argv += 2; @@ -2371,7 +2373,7 @@ parse_args_recurse (int *argcp, if (argc < 2) die ("--uid takes an argument"); - if (opt_sandbox_uid != (uid_t) -1) + if (opt_sandbox_uid != (uid_t)-1) warn_only_last_option ("--uid"); the_uid = strtol (argv[1], &endptr, 10); @@ -2391,7 +2393,7 @@ parse_args_recurse (int *argcp, if (argc < 2) die ("--gid takes an argument"); - if (opt_sandbox_gid != (gid_t) -1) + if (opt_sandbox_gid != (gid_t)-1) warn_only_last_option ("--gid"); the_gid = strtol (argv[1], &endptr, 10); @@ -2586,11 +2588,11 @@ parse_args_recurse (int *argcp, /* If --perms was set for the current action but the current action * didn't consume the setting, apparently --perms wasn't suitable for * this action. */ - if (!is_modifier_option (arg) && next_perms >= 0) + if (!is_modifier_option(arg) && next_perms >= 0) die ("--perms must be followed by an option that creates a file"); /* Similarly for --size. */ - if (!is_modifier_option (arg) && next_size_arg != 0) + if (!is_modifier_option(arg) && next_size_arg != 0) die ("--size must be followed by --tmpfs"); argv++; @@ -2602,7 +2604,7 @@ parse_args_recurse (int *argcp, } static void -parse_args (int *argcp, +parse_args (int *argcp, const char ***argvp) { int total_parsed_argc = *argcp; @@ -2634,7 +2636,7 @@ read_overflowids (void) } static void -namespace_ids_read (pid_t pid) +namespace_ids_read (pid_t pid) { cleanup_free char *dir = NULL; cleanup_fd int ns_fd = -1; @@ -2667,8 +2669,8 @@ namespace_ids_read (pid_t pid) } static void -namespace_ids_write (int fd, - bool in_json) +namespace_ids_write (int fd, + bool in_json) { NsInfo *info; @@ -2693,7 +2695,7 @@ namespace_ids_write (int fd, } int -main (int argc, +main (int argc, char **argv) { mode_t old_umask; @@ -2823,7 +2825,7 @@ main (int argc, { cleanup_free char *max_user_ns = NULL; max_user_ns = load_file_at (AT_FDCWD, "/proc/sys/user/max_user_namespaces"); - if (max_user_ns != NULL && strcmp (max_user_ns, "0\n") == 0) + if (max_user_ns != NULL && strcmp(max_user_ns, "0\n") == 0) disabled = TRUE; } @@ -2838,11 +2840,11 @@ main (int argc, if (argc <= 0) usage (EXIT_FAILURE, stderr); - __debug__ ("Creating root mount point\n"); + __debug__ (("Creating root mount point\n")); - if (opt_sandbox_uid == (uid_t) -1) + if (opt_sandbox_uid == (uid_t)-1) opt_sandbox_uid = real_uid; - if (opt_sandbox_gid == (gid_t) -1) + if (opt_sandbox_gid == (gid_t)-1) opt_sandbox_gid = real_gid; if (!opt_unshare_user && opt_userns_fd == -1 && opt_sandbox_uid != real_uid) @@ -2874,7 +2876,7 @@ main (int argc, * access ourselves. */ base_path = "/tmp"; - __debug__ ("creating new namespace\n"); + __debug__ (("creating new namespace\n")); if (opt_unshare_pid && !opt_as_pid_1) { @@ -3225,8 +3227,7 @@ main (int argc, * We're aiming to make /newroot the real root, and get rid of /oldroot. To do * that we need a temporary place to store it before we can unmount it. */ - { - cleanup_fd int oldrootfd = open ("/", O_DIRECTORY | O_RDONLY); + { cleanup_fd int oldrootfd = open ("/", O_DIRECTORY | O_RDONLY); if (oldrootfd < 0) die_with_error ("can't open /"); if (chdir ("/newroot") != 0) @@ -3352,7 +3353,7 @@ main (int argc, if (label_exec (opt_exec_label) == -1) die_with_error ("label_exec %s", argv[0]); - __debug__ ("forking for child\n"); + __debug__ (("forking for child\n")); if (!opt_as_pid_1 && (opt_unshare_pid || lock_files != NULL || opt_sync_fd != -1)) { @@ -3390,7 +3391,7 @@ main (int argc, } } - __debug__ ("launch executable %s\n", argv[0]); + __debug__ (("launch executable %s\n", argv[0])); if (proc_fd != -1) close (proc_fd);