bind-mount.c (flatpak-1.15.1.tar.xz) | : | bind-mount.c (flatpak-1.15.2.tar.xz) | ||
---|---|---|---|---|
skipping to change at line 381 | skipping to change at line 381 | |||
end_tab = collect_mounts (&mount_tab[0], &lines[root]); | end_tab = collect_mounts (&mount_tab[0], &lines[root]); | |||
assert (end_tab == &mount_tab[n_mounts]); | assert (end_tab == &mount_tab[n_mounts]); | |||
return steal_pointer (&mount_tab); | return steal_pointer (&mount_tab); | |||
} | } | |||
bind_mount_result | bind_mount_result | |||
bind_mount (int proc_fd, | bind_mount (int proc_fd, | |||
const char *src, | const char *src, | |||
const char *dest, | const char *dest, | |||
bind_option_t options) | bind_option_t options, | |||
char **failing_path) | ||||
{ | { | |||
bool readonly = (options & BIND_READONLY) != 0; | bool readonly = (options & BIND_READONLY) != 0; | |||
bool devices = (options & BIND_DEVICES) != 0; | bool devices = (options & BIND_DEVICES) != 0; | |||
bool recursive = (options & BIND_RECURSIVE) != 0; | bool recursive = (options & BIND_RECURSIVE) != 0; | |||
unsigned long current_flags, new_flags; | unsigned long current_flags, new_flags; | |||
cleanup_mount_tab MountTab mount_tab = NULL; | cleanup_mount_tab MountTab mount_tab = NULL; | |||
cleanup_free char *resolved_dest = NULL; | cleanup_free char *resolved_dest = NULL; | |||
cleanup_free char *dest_proc = NULL; | cleanup_free char *dest_proc = NULL; | |||
cleanup_free char *oldroot_dest_proc = NULL; | cleanup_free char *oldroot_dest_proc = NULL; | |||
cleanup_free char *kernel_case_combination = NULL; | cleanup_free char *kernel_case_combination = NULL; | |||
skipping to change at line 409 | skipping to change at line 410 | |||
} | } | |||
/* The mount operation will resolve any symlinks in the destination | /* 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. */ | path, so to find it in the mount table we need to do that too. */ | |||
resolved_dest = realpath (dest, NULL); | resolved_dest = realpath (dest, NULL); | |||
if (resolved_dest == NULL) | if (resolved_dest == NULL) | |||
return BIND_MOUNT_ERROR_REALPATH_DEST; | return BIND_MOUNT_ERROR_REALPATH_DEST; | |||
dest_fd = open (resolved_dest, O_PATH | O_CLOEXEC); | dest_fd = open (resolved_dest, O_PATH | O_CLOEXEC); | |||
if (dest_fd < 0) | if (dest_fd < 0) | |||
return BIND_MOUNT_ERROR_REOPEN_DEST; | { | |||
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 | /* If we are in a case-insensitive filesystem, mountinfo might contain a | |||
* different case combination of the path we requested to mount. | * 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, | * 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 | * populates mountinfo with whatever case combination first appeared in the | |||
* dcache; kernel developers plan to change this in future so that it | * dcache; kernel developers plan to change this in future so that it | |||
* reflects the on-disk encoding instead. | * reflects the on-disk encoding instead. | |||
* To avoid throwing an error when this happens, we use readlink() result | * 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 | * 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 | * entries with the same case combination that the kernel is expected to | |||
* use. */ | * use. */ | |||
dest_proc = xasprintf ("/proc/self/fd/%d", dest_fd); | dest_proc = xasprintf ("/proc/self/fd/%d", dest_fd); | |||
oldroot_dest_proc = get_oldroot_path (dest_proc); | oldroot_dest_proc = get_oldroot_path (dest_proc); | |||
kernel_case_combination = readlink_malloc (oldroot_dest_proc); | kernel_case_combination = readlink_malloc (oldroot_dest_proc); | |||
if (kernel_case_combination == NULL) | if (kernel_case_combination == NULL) | |||
return BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD; | { | |||
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); | mount_tab = parse_mountinfo (proc_fd, kernel_case_combination); | |||
if (mount_tab[0].mountpoint == NULL) | if (mount_tab[0].mountpoint == NULL) | |||
{ | { | |||
if (failing_path != NULL) | ||||
*failing_path = steal_pointer (&kernel_case_combination); | ||||
errno = EINVAL; | errno = EINVAL; | |||
return BIND_MOUNT_ERROR_FIND_DEST_MOUNT; | return BIND_MOUNT_ERROR_FIND_DEST_MOUNT; | |||
} | } | |||
assert (path_equal (mount_tab[0].mountpoint, kernel_case_combination)); | assert (path_equal (mount_tab[0].mountpoint, kernel_case_combination)); | |||
current_flags = mount_tab[0].options; | current_flags = mount_tab[0].options; | |||
new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0); | new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0); | |||
if (new_flags != current_flags && | if (new_flags != current_flags && | |||
mount ("none", resolved_dest, | mount ("none", resolved_dest, | |||
NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0) | NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0) | |||
return BIND_MOUNT_ERROR_REMOUNT_DEST; | { | |||
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 | /* 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. | * apply the flags to all submounts in the recursive case. | |||
* Note: This does not apply the flags to mounts which are later propagated in to this namespace. | * Note: This does not apply the flags to mounts which are later propagated in to this namespace. | |||
*/ | */ | |||
if (recursive) | if (recursive) | |||
{ | { | |||
for (i = 1; mount_tab[i].mountpoint != NULL; i++) | for (i = 1; mount_tab[i].mountpoint != NULL; i++) | |||
{ | { | |||
current_flags = mount_tab[i].options; | current_flags = mount_tab[i].options; | |||
new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (re adonly ? MS_RDONLY : 0); | new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (re adonly ? MS_RDONLY : 0); | |||
if (new_flags != current_flags && | if (new_flags != current_flags && | |||
mount ("none", mount_tab[i].mountpoint, | mount ("none", mount_tab[i].mountpoint, | |||
NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) ! = 0) | 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 s hould | /* If we can't read the mountpoint we can't remount it, but that s hould | |||
be safe to ignore because its not something the user can access . */ | be safe to ignore because its not something the user can access . */ | |||
if (errno != EACCES) | if (errno != EACCES) | |||
return BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT; | { | |||
if (failing_path != NULL) | ||||
*failing_path = xstrdup (mount_tab[i].mountpoint); | ||||
return BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT; | ||||
} | ||||
} | } | |||
} | } | |||
} | } | |||
return BIND_MOUNT_SUCCESS; | return BIND_MOUNT_SUCCESS; | |||
} | } | |||
/** | /** | |||
* Return a string representing bind_mount_result, like strerror(). | * Return a string representing bind_mount_result, like strerror(). | |||
* If want_errno_p is non-NULL, *want_errno_p is used to indicate whether | * If want_errno_p is non-NULL, *want_errno_p is used to indicate whether | |||
* it would make sense to print strerror(saved_errno). | * it would make sense to print strerror(saved_errno). | |||
*/ | */ | |||
const char * | static char * | |||
bind_mount_result_to_string (bind_mount_result res, | bind_mount_result_to_string (bind_mount_result res, | |||
const char *failing_path, | ||||
bool *want_errno_p) | bool *want_errno_p) | |||
{ | { | |||
const char *string; | char *string = NULL; | |||
bool want_errno = TRUE; | bool want_errno = TRUE; | |||
switch (res) | switch (res) | |||
{ | { | |||
case BIND_MOUNT_ERROR_MOUNT: | case BIND_MOUNT_ERROR_MOUNT: | |||
string = "Unable to mount source on destination"; | string = xstrdup ("Unable to mount source on destination"); | |||
break; | break; | |||
case BIND_MOUNT_ERROR_REALPATH_DEST: | case BIND_MOUNT_ERROR_REALPATH_DEST: | |||
string = "realpath(destination)"; | string = xstrdup ("realpath(destination)"); | |||
break; | break; | |||
case BIND_MOUNT_ERROR_REOPEN_DEST: | case BIND_MOUNT_ERROR_REOPEN_DEST: | |||
string = "open(destination, O_PATH)"; | string = xasprintf ("open(\"%s\", O_PATH)", failing_path); | |||
break; | break; | |||
case BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD: | case BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD: | |||
string = "readlink(/proc/self/fd/<destination>)"; | string = xasprintf ("readlink(/proc/self/fd/N) for \"%s\"", failing_path ); | |||
break; | break; | |||
case BIND_MOUNT_ERROR_FIND_DEST_MOUNT: | case BIND_MOUNT_ERROR_FIND_DEST_MOUNT: | |||
string = "Unable to find destination in mount table"; | string = xasprintf ("Unable to find \"%s\" in mount table", failing_path ); | |||
want_errno = FALSE; | want_errno = FALSE; | |||
break; | break; | |||
case BIND_MOUNT_ERROR_REMOUNT_DEST: | case BIND_MOUNT_ERROR_REMOUNT_DEST: | |||
string = "Unable to remount destination with correct flags"; | string = xasprintf ("Unable to remount destination \"%s\" with correct f | |||
lags", | ||||
failing_path); | ||||
break; | break; | |||
case BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT: | case BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT: | |||
string = "Unable to remount recursively with correct flags"; | string = xasprintf ("Unable to apply mount flags: remount \"%s\"", | |||
failing_path); | ||||
break; | break; | |||
case BIND_MOUNT_SUCCESS: | case BIND_MOUNT_SUCCESS: | |||
string = "Success"; | string = xstrdup ("Success"); | |||
break; | break; | |||
default: | default: | |||
string = "(unknown/invalid bind_mount_result)"; | string = xstrdup ("(unknown/invalid bind_mount_result)"); | |||
break; | break; | |||
} | } | |||
if (want_errno_p != NULL) | if (want_errno_p != NULL) | |||
*want_errno_p = want_errno; | *want_errno_p = want_errno; | |||
return string; | return string; | |||
} | } | |||
void | void | |||
die_with_bind_result (bind_mount_result res, | die_with_bind_result (bind_mount_result res, | |||
int saved_errno, | int saved_errno, | |||
const char *failing_path, | ||||
const char *format, | const char *format, | |||
...) | ...) | |||
{ | { | |||
va_list args; | va_list args; | |||
bool want_errno = TRUE; | bool want_errno = TRUE; | |||
char *message; | ||||
fprintf (stderr, "bwrap: "); | fprintf (stderr, "bwrap: "); | |||
va_start (args, format); | va_start (args, format); | |||
vfprintf (stderr, format, args); | vfprintf (stderr, format, args); | |||
va_end (args); | va_end (args); | |||
fprintf (stderr, ": %s", bind_mount_result_to_string (res, &want_errno)); | message = bind_mount_result_to_string (res, failing_path, &want_errno); | |||
fprintf (stderr, ": %s", message); | ||||
/* message is leaked, but we're exiting unsuccessfully anyway, so ignore */ | ||||
if (want_errno) | if (want_errno) | |||
fprintf (stderr, ": %s", strerror (saved_errno)); | fprintf (stderr, ": %s", strerror (saved_errno)); | |||
fprintf (stderr, "\n"); | fprintf (stderr, "\n"); | |||
exit (1); | exit (1); | |||
} | } | |||
End of changes. 21 change blocks. | ||||
17 lines changed or deleted | 49 lines changed or added |