Skip to content

Commit ee19906

Browse files
committed
Use mount_setattr() in bind_mount()
1 parent 05109eb commit ee19906

5 files changed

Lines changed: 103 additions & 63 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 & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -414,72 +414,91 @@ 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+
if (has_mount_setattr())
431418
{
432-
if (failing_path != NULL)
433-
*failing_path = steal_pointer (&resolved_dest);
434-
435-
return BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD;
419+
struct mount_attr attr = {
420+
.attr_clr = 0,
421+
.attr_set = MOUNT_ATTR_NOSUID | (devices ? 0 : MOUNT_ATTR_NODEV) |
422+
(readonly ? MOUNT_ATTR_RDONLY : 0),
423+
};
424+
if (mount_setattr(dest_fd, "",
425+
AT_EMPTY_PATH | (recursive ? AT_RECURSIVE : 0), &attr,
426+
sizeof(attr)) == -1)
427+
{
428+
if (failing_path != NULL)
429+
*failing_path = steal_pointer(&resolved_dest);
430+
return BIND_MOUNT_ERROR_MOUNT_SETATTR;
431+
}
436432
}
437-
438-
mount_tab = parse_mountinfo (proc_fd, kernel_case_combination);
439-
if (mount_tab[0].mountpoint == NULL)
433+
else
440434
{
441-
if (failing_path != NULL)
442-
*failing_path = steal_pointer (&kernel_case_combination);
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++)
466+
assert (path_equal (mount_tab[0].mountpoint, kernel_case_combination));
467+
current_flags = mount_tab[0].options;
468+
new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0);
469+
if (new_flags != current_flags &&
470+
mount ("none", resolved_dest,
471+
NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0)
472+
{
473+
if (failing_path != NULL)
474+
*failing_path = steal_pointer (&resolved_dest);
475+
476+
return BIND_MOUNT_ERROR_REMOUNT_DEST;
477+
}
478+
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)
468484
{
469-
current_flags = mount_tab[i].options;
470-
new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0);
471-
if (new_flags != current_flags &&
472-
mount ("none", mount_tab[i].mountpoint,
473-
NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0)
485+
for (i = 1; mount_tab[i].mountpoint != NULL; i++)
474486
{
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)
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)
478492
{
479-
if (failing_path != NULL)
480-
*failing_path = xstrdup (mount_tab[i].mountpoint);
481-
482-
return BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT;
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+
}
483502
}
484503
}
485504
}
@@ -534,6 +553,10 @@ bind_mount_result_to_string (bind_mount_result res,
534553
failing_path);
535554
break;
536555

556+
case BIND_MOUNT_ERROR_MOUNT_SETATTR:
557+
string = xasprintf("mount_setattr() failed at \"%s\"", failing_path);
558+
break;
559+
537560
case BIND_MOUNT_SUCCESS:
538561
string = xstrdup ("Success");
539562
break;
@@ -587,6 +610,7 @@ die_with_bind_result (bind_mount_result res,
587610
case BIND_MOUNT_ERROR_REOPEN_DEST:
588611
case BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD:
589612
case BIND_MOUNT_ERROR_FIND_DEST_MOUNT:
613+
case BIND_MOUNT_ERROR_MOUNT_SETATTR:
590614
case BIND_MOUNT_SUCCESS:
591615
default:
592616
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.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,3 +1083,16 @@ strappend_escape_for_mount_options (StringBuilder *dest, const char *src)
10831083
src++;
10841084
}
10851085
}
1086+
1087+
bool
1088+
has_mount_setattr (void)
1089+
{
1090+
#ifdef __NR_mount_setattr
1091+
long ret = syscall(__NR_mount_setattr, -1, "", 0, NULL, 0);
1092+
if (ret == -1 && errno == ENOSYS)
1093+
return false;
1094+
return true;
1095+
#else
1096+
return false;
1097+
#endif
1098+
}

utils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,3 +215,5 @@ void strappendf (StringBuilder *dest,
215215
...);
216216
void strappend_escape_for_mount_options (StringBuilder *dest,
217217
const char *src);
218+
219+
bool has_mount_setattr(void);

0 commit comments

Comments
 (0)