summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovico de Nittis <ludovico.denittis@collabora.com>2021-02-16 13:46:10 +0100
committerLudovico de Nittis <ludovico.denittis@collabora.com>2021-02-16 13:46:10 +0100
commit04c0ca17ad945c50aaff61abad475bbd627e0584 (patch)
treeb3027528f0c21a4af729c9afa1aae9a2cc160c01
parentbae85baf7208c4acddd9cf032059d1429f179e4a (diff)
downloadbubblewrap-04c0ca17ad945c50aaff61abad475bbd627e0584.tar.gz
Add support for bind-mount on case-insensitive filesystems
If we are using a case-insensitive filesystem the bind-mount operation might fail when `/proc/self/mountinfo` is checked. In a case-insensitive filesystem, if we ask to mount a certain directory, e.g. '/CI_fs/foo', the kernel might add its entry in `mountinfo` as '/CI_fs/FOO'. This happens because the kernel populates `mountinfo` with whatever case combination first appeared in the dcache. With this patch we open the requested path and look at its `/proc/self/fd`, using readlink(), to get the path case combination that the kernel is also expected to be using. Signed-off-by: Ludovico de Nittis <ludovico.denittis@collabora.com>
-rw-r--r--bind-mount.c28
-rw-r--r--bubblewrap.c8
-rw-r--r--utils.c31
-rw-r--r--utils.h2
4 files changed, 59 insertions, 10 deletions
diff --git a/bind-mount.c b/bind-mount.c
index 4bd187d..e692762 100644
--- a/bind-mount.c
+++ b/bind-mount.c
@@ -385,6 +385,10 @@ bind_mount (int proc_fd,
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)
@@ -399,14 +403,34 @@ bind_mount (int proc_fd,
if (resolved_dest == NULL)
return 2;
- mount_tab = parse_mountinfo (proc_fd, resolved_dest);
+ dest_fd = open (resolved_dest, O_PATH | O_CLOEXEC);
+ if (dest_fd < 0)
+ return 2;
+
+ /* 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)
+ die_with_error ("Can't read the link in %s", oldroot_dest_proc);
+
+ mount_tab = parse_mountinfo (proc_fd, kernel_case_combination);
if (mount_tab[0].mountpoint == NULL)
{
errno = EINVAL;
return 2; /* No mountpoint at dest */
}
- assert (path_equal (mount_tab[0].mountpoint, resolved_dest));
+ 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 &&
diff --git a/bubblewrap.c b/bubblewrap.c
index efca03c..2172904 100644
--- a/bubblewrap.c
+++ b/bubblewrap.c
@@ -861,14 +861,6 @@ get_newroot_path (const char *path)
return strconcat ("/newroot/", path);
}
-static char *
-get_oldroot_path (const char *path)
-{
- while (*path == '/')
- path++;
- return strconcat ("/oldroot/", path);
-}
-
static void
write_uid_gid_map (uid_t sandbox_uid,
uid_t parent_uid,
diff --git a/utils.c b/utils.c
index a99a865..e45ae56 100644
--- a/utils.c
+++ b/utils.c
@@ -764,6 +764,37 @@ read_pid_from_socket (int socket)
die ("No pid returned on socket");
}
+/* Sets errno on error (== NULL),
+ * Always ensures terminating zero */
+char *
+readlink_malloc (const char *pathname)
+{
+ size_t size = 50;
+ ssize_t n;
+ cleanup_free char *value = NULL;
+
+ do
+ {
+ size *= 2;
+ value = xrealloc (value, size);
+ n = readlink (pathname, value, size - 1);
+ if (n < 0)
+ return NULL;
+ }
+ while (size - 2 < n);
+
+ value[n] = 0;
+ return steal_pointer (&value);
+}
+
+char *
+get_oldroot_path (const char *path)
+{
+ while (*path == '/')
+ path++;
+ return strconcat ("/oldroot/", path);
+}
+
int
raw_clone (unsigned long flags,
void *child_stack)
diff --git a/utils.h b/utils.h
index 8c4db61..c90c8d7 100644
--- a/utils.h
+++ b/utils.h
@@ -112,6 +112,8 @@ int mkdir_with_parents (const char *pathname,
void create_pid_socketpair (int sockets[2]);
void send_pid_on_socket (int socket);
int read_pid_from_socket (int socket);
+char *get_oldroot_path (const char *path);
+char *readlink_malloc (const char *pathname);
/* syscall wrappers */
int raw_clone (unsigned long flags,