Skip to content

Commit 69fdb9e

Browse files
committed
Use mount_setattr() in bind_mount()
Signed-off-by: xxyzz <gitpull@protonmail.com>
1 parent 05109eb commit 69fdb9e

4 files changed

Lines changed: 126 additions & 62 deletions

File tree

.github/workflows/check.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@ name: CI checks
33
on:
44
push:
55
branches:
6-
- main
6+
- main
77
pull_request:
88
branches:
9-
- main
9+
- main
1010

1111
jobs:
1212
meson:
1313
name: Build with Meson and gcc, and test
1414
runs-on: ubuntu-latest
1515
steps:
1616
- name: Check out
17-
uses: actions/checkout@v4
17+
uses: actions/checkout@v6
1818
- name: Install build-dependencies
1919
run: sudo ./ci/builddeps.sh
2020
- name: Enable user namespaces
@@ -71,7 +71,7 @@ jobs:
7171
test ! -e DESTDIR-as-subproject/usr/local/libexec/bwrap
7272
tests/use-as-subproject/assert-correct-rpath.py DESTDIR-as-subproject/usr/local/libexec/not-flatpak-bwrap
7373
- name: Upload test logs
74-
uses: actions/upload-artifact@v4
74+
uses: actions/upload-artifact@v7
7575
if: failure() || cancelled()
7676
with:
7777
name: test logs
@@ -87,11 +87,11 @@ jobs:
8787
- cpp
8888
steps:
8989
- name: Initialize CodeQL
90-
uses: github/codeql-action/init@v2
90+
uses: github/codeql-action/init@v4
9191
with:
9292
languages: ${{ matrix.language }}
9393
- name: Check out
94-
uses: actions/checkout@v4
94+
uses: actions/checkout@v6
9595
- name: Install build-dependencies
9696
run: sudo ./ci/builddeps.sh --clang
9797
- run: meson build -Dselinux=enabled
@@ -102,4 +102,4 @@ jobs:
102102
-Werror=unused-variable
103103
- run: meson compile -C build
104104
- name: CodeQL analysis
105-
uses: github/codeql-action/analyze@v2
105+
uses: github/codeql-action/analyze@v4

bind-mount.c

Lines changed: 80 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -414,72 +414,92 @@ bind_mount (int proc_fd,
414414
return BIND_MOUNT_ERROR_REOPEN_DEST;
415415
}
416416

417-
/* If we are in a case-insensitive filesystem, mountinfo might contain a
418-
* different case combination of the path we requested to mount.
419-
* This is due to the fact that the kernel, as of the beginning of 2021,
420-
* populates mountinfo with whatever case combination first appeared in the
421-
* dcache; kernel developers plan to change this in future so that it
422-
* reflects the on-disk encoding instead.
423-
* To avoid throwing an error when this happens, we use readlink() result
424-
* instead of the provided @root_mount, so that we can compare the mountinfo
425-
* entries with the same case combination that the kernel is expected to
426-
* use. */
427-
dest_proc = xasprintf ("/proc/self/fd/%d", dest_fd);
428-
oldroot_dest_proc = get_oldroot_path (dest_proc);
429-
kernel_case_combination = readlink_malloc (oldroot_dest_proc);
430-
if (kernel_case_combination == NULL)
417+
struct mount_attr attr = {
418+
.attr_clr = 0,
419+
.attr_set = MOUNT_ATTR_NOSUID | (devices ? 0 : MOUNT_ATTR_NODEV) |
420+
(readonly ? MOUNT_ATTR_RDONLY : 0),
421+
};
422+
if (mount_setattr_wrapper (dest_fd, "",
423+
AT_EMPTY_PATH | (recursive ? AT_RECURSIVE : 0), &attr,
424+
sizeof(attr)) == -1)
431425
{
432-
if (failing_path != NULL)
433-
*failing_path = steal_pointer (&resolved_dest);
434-
435-
return BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD;
436-
}
426+
if (errno != ENOSYS)
427+
{
428+
if (failing_path != NULL)
429+
*failing_path = steal_pointer (&resolved_dest);
437430

438-
mount_tab = parse_mountinfo (proc_fd, kernel_case_combination);
439-
if (mount_tab[0].mountpoint == NULL)
440-
{
441-
if (failing_path != NULL)
442-
*failing_path = steal_pointer (&kernel_case_combination);
431+
return BIND_MOUNT_ERROR_MOUNT_SETATTR;
432+
}
433+
else
434+
{
435+
/* If we are in a case-insensitive filesystem, mountinfo might contain a
436+
* different case combination of the path we requested to mount.
437+
* This is due to the fact that the kernel, as of the beginning of 2021,
438+
* populates mountinfo with whatever case combination first appeared in the
439+
* dcache; kernel developers plan to change this in future so that it
440+
* reflects the on-disk encoding instead.
441+
* To avoid throwing an error when this happens, we use readlink() result
442+
* instead of the provided @root_mount, so that we can compare the mountinfo
443+
* entries with the same case combination that the kernel is expected to
444+
* use. */
445+
dest_proc = xasprintf ("/proc/self/fd/%d", dest_fd);
446+
oldroot_dest_proc = get_oldroot_path (dest_proc);
447+
kernel_case_combination = readlink_malloc (oldroot_dest_proc);
448+
if (kernel_case_combination == NULL)
449+
{
450+
if (failing_path != NULL)
451+
*failing_path = steal_pointer (&resolved_dest);
443452

444-
errno = EINVAL;
445-
return BIND_MOUNT_ERROR_FIND_DEST_MOUNT;
446-
}
453+
return BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD;
454+
}
447455

448-
assert (path_equal (mount_tab[0].mountpoint, kernel_case_combination));
449-
current_flags = mount_tab[0].options;
450-
new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0);
451-
if (new_flags != current_flags &&
452-
mount ("none", resolved_dest,
453-
NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0)
454-
{
455-
if (failing_path != NULL)
456-
*failing_path = steal_pointer (&resolved_dest);
456+
mount_tab = parse_mountinfo (proc_fd, kernel_case_combination);
457+
if (mount_tab[0].mountpoint == NULL)
458+
{
459+
if (failing_path != NULL)
460+
*failing_path = steal_pointer (&kernel_case_combination);
457461

458-
return BIND_MOUNT_ERROR_REMOUNT_DEST;
459-
}
462+
errno = EINVAL;
463+
return BIND_MOUNT_ERROR_FIND_DEST_MOUNT;
464+
}
460465

461-
/* We need to work around the fact that a bind mount does not apply the flags, so we need to manually
462-
* apply the flags to all submounts in the recursive case.
463-
* Note: This does not apply the flags to mounts which are later propagated into this namespace.
464-
*/
465-
if (recursive)
466-
{
467-
for (i = 1; mount_tab[i].mountpoint != NULL; i++)
468-
{
469-
current_flags = mount_tab[i].options;
466+
assert (path_equal (mount_tab[0].mountpoint, kernel_case_combination));
467+
current_flags = mount_tab[0].options;
470468
new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0);
471469
if (new_flags != current_flags &&
472-
mount ("none", mount_tab[i].mountpoint,
470+
mount ("none", resolved_dest,
473471
NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0)
474472
{
475-
/* If we can't read the mountpoint we can't remount it, but that should
476-
be safe to ignore because its not something the user can access. */
477-
if (errno != EACCES)
478-
{
479-
if (failing_path != NULL)
480-
*failing_path = xstrdup (mount_tab[i].mountpoint);
473+
if (failing_path != NULL)
474+
*failing_path = steal_pointer (&resolved_dest);
475+
476+
return BIND_MOUNT_ERROR_REMOUNT_DEST;
477+
}
481478

482-
return BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT;
479+
/* We need to work around the fact that a bind mount does not apply the flags, so we need to manually
480+
* apply the flags to all submounts in the recursive case.
481+
* Note: This does not apply the flags to mounts which are later propagated into this namespace.
482+
*/
483+
if (recursive)
484+
{
485+
for (i = 1; mount_tab[i].mountpoint != NULL; i++)
486+
{
487+
current_flags = mount_tab[i].options;
488+
new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0);
489+
if (new_flags != current_flags &&
490+
mount ("none", mount_tab[i].mountpoint,
491+
NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0)
492+
{
493+
/* If we can't read the mountpoint we can't remount it, but that should
494+
be safe to ignore because its not something the user can access. */
495+
if (errno != EACCES)
496+
{
497+
if (failing_path != NULL)
498+
*failing_path = xstrdup (mount_tab[i].mountpoint);
499+
500+
return BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT;
501+
}
502+
}
483503
}
484504
}
485505
}
@@ -534,6 +554,10 @@ bind_mount_result_to_string (bind_mount_result res,
534554
failing_path);
535555
break;
536556

557+
case BIND_MOUNT_ERROR_MOUNT_SETATTR:
558+
string = xasprintf("mount_setattr() failed at \"%s\"", failing_path);
559+
break;
560+
537561
case BIND_MOUNT_SUCCESS:
538562
string = xstrdup ("Success");
539563
break;
@@ -587,6 +611,7 @@ die_with_bind_result (bind_mount_result res,
587611
case BIND_MOUNT_ERROR_REOPEN_DEST:
588612
case BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD:
589613
case BIND_MOUNT_ERROR_FIND_DEST_MOUNT:
614+
case BIND_MOUNT_ERROR_MOUNT_SETATTR:
590615
case BIND_MOUNT_SUCCESS:
591616
default:
592617
fprintf (stderr, ": %s", strerror (saved_errno));

bind-mount.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ typedef enum
3737
BIND_MOUNT_ERROR_FIND_DEST_MOUNT,
3838
BIND_MOUNT_ERROR_REMOUNT_DEST,
3939
BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT,
40+
BIND_MOUNT_ERROR_MOUNT_SETATTR,
4041
} bind_mount_result;
4142

4243
bind_mount_result bind_mount (int proc_fd,

utils.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,3 +215,41 @@ void strappendf (StringBuilder *dest,
215215
...);
216216
void strappend_escape_for_mount_options (StringBuilder *dest,
217217
const char *src);
218+
219+
#ifndef __NR_mount_setattr
220+
#define __NR_mount_setattr 442
221+
#endif
222+
223+
#ifndef MOUNT_ATTR_RDONLY
224+
struct mount_attr
225+
{
226+
__u64 attr_set;
227+
__u64 attr_clr;
228+
__u64 propagation;
229+
__u64 userns_fd;
230+
};
231+
232+
#define MOUNT_ATTR_RDONLY 0x00000001
233+
#define MOUNT_ATTR_NOSUID 0x00000002
234+
#define MOUNT_ATTR_NODEV 0x00000004
235+
#define MOUNT_ATTR_NOEXEC 0x00000008
236+
#define MOUNT_ATTR__ATIME 0x00000070
237+
#define MOUNT_ATTR_RELATIME 0x00000000
238+
#define MOUNT_ATTR_NOATIME 0x00000010
239+
#define MOUNT_ATTR_STRICTATIME 0x00000020
240+
#define MOUNT_ATTR_NODIRATIME 0x00000080
241+
#define MOUNT_ATTR_IDMAP 0x00100000
242+
#define MOUNT_ATTR_NOSYMFOLLOW 0x00200000
243+
#endif
244+
245+
#ifndef AT_EMPTY_PATH
246+
#define AT_EMPTY_PATH 0x1000
247+
#define AT_RECURSIVE 0x8000
248+
#endif
249+
250+
static inline int
251+
mount_setattr_wrapper (int dirfd, const char *path, unsigned int flags,
252+
struct mount_attr *attr, size_t size)
253+
{
254+
return syscall (__NR_mount_setattr, dirfd, path, flags, attr, size);
255+
}

0 commit comments

Comments
 (0)