‘guix environment -C’ fails with Linux 4.19 (Debian)

  • Done
  • quality assurance status badge
Details
5 participants
  • Andreas Enge
  • Lucas Nussbaum
  • Ludovic Courtès
  • Ludovic Courtès
  • zimoun
Owner
unassigned
Submitted by
Ludovic Courtès
Severity
normal
L
L
Ludovic Courtès wrote on 4 Feb 2021 11:43
(address . bug-guix@gnu.org)
87h7ms8658.fsf@inria.fr
I’m observing this:

Toggle snippet (8 lines)
$ guix environment --ad-hoc coreutils -C
guix environment: error: mount: mount "/gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16" on "/tmp/guix-directory.Nagh8Y//gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16": Operation not permitted
$ uname -rv
4.19.0-13-amd64 #1 SMP Debian 4.19.160-2 (2020-11-28)
$ cat /proc/sys/kernel/unprivileged_userns_clone
1

Excerpt of the strace log:

Toggle snippet (18 lines)
7605 mkdir("/tmp/guix-directory.EtXAVT/dev/mqueue", 0777) = 0
7605 mount("mqueue", "/tmp/guix-directory.EtXAVT//dev/mqueue", "mqueue", MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL) = 0
7605 stat("/home/lcourtes", {st_mode=S_IFDIR|0710, st_size=4096, ...}) = 0
7605 mkdir("/tmp", 0777) = -1 EEXIST (File exists)
7605 mkdir("/tmp/guix-directory.EtXAVT", 0777) = -1 EEXIST (File exists)
7605 mkdir("/tmp/guix-directory.EtXAVT/home", 0777) = 0
7605 mkdir("/tmp/guix-directory.EtXAVT/home/lcourtes", 0777) = 0
7605 mount("/home/lcourtes", "/tmp/guix-directory.EtXAVT//home/lcourtes", 0xeea390, MS_BIND, NULL) = 0
7605 stat("/gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16", {st_mode=S_IFDIR|0555, st_size=4096, ...}) = 0
7605 mkdir("/tmp", 0777) = -1 EEXIST (File exists)
7605 mkdir("/tmp/guix-directory.EtXAVT", 0777) = -1 EEXIST (File exists)
7605 mkdir("/tmp/guix-directory.EtXAVT/gnu", 0777) = 0
7605 mkdir("/tmp/guix-directory.EtXAVT/gnu/store", 0777) = 0
7605 mkdir("/tmp/guix-directory.EtXAVT/gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16", 0777) = 0
7605 mount("/gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16", "/tmp/guix-directory.EtXAVT//gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16", 0xeea3b0, MS_RDONLY|MS_BIND, NULL) = 0
7605 mount("/gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16", "/tmp/guix-directory.EtXAVT//gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16", 0xeea3d0, MS_RDONLY|MS_REMOUNT|MS_BIND, NULL) = -1 EPERM (Operation not permitted)

The read-only remount comes from ‘mount-file-system’ in (gnu build
file-systems):

;; For read-only bind mounts, an extra remount is needed, as per
;; http://lwn.net/Articles/281157/, which still applies to Linux
;; 4.0.
(when (and (= MS_BIND (logand flags MS_BIND))
(= MS_RDONLY (logand flags MS_RDONLY)))
(let ((flags (logior MS_BIND MS_REMOUNT MS_RDONLY)))
(mount source mount-point type flags #f)))

This recipe has been working well “forever”, although it’s probably
unnecessary with recent kernels (the LWN article is from 2008).

The problem may have to do with the fact that /gnu/store is an NFS
mount. Indeed, similar commands fail on $HOME (also an NFS mount):

Toggle snippet (7 lines)
$ mkdir t m
$ unshare -mrf
# mount --bind ./t ./m
# mount --bind -r -o remount ./t ./m
mount: /home/lcourtes/m: permission denied.

… but they succeed on /tmp (not an NFS mount):

Toggle snippet (7 lines)
$ mkdir /tmp/t
$ mkdir /tmp/m
$ unshare -mrf
# mount --bind /tmp/{t,m}
# mount --bind -r -o remount /tmp/{t,m}

To be continued…

Ludo’.
Z
Z
zimoun wrote on 4 Feb 2021 13:38
Re: bug#46292: ‘guix environment -C ’ fails with Linux 4.19 (Debian)
(name . Dimitri DELABROYE)(address . dimitri.delabroye@inria.fr)
868s84c8is.fsf@gmail.com
Hi,

On Thu, 04 Feb 2021 at 11:43, Ludovic Courtès <ludovic.courtes@inria.fr> wrote:

Toggle quote (9 lines)
> --8<---------------cut here---------------start------------->8---
> $ guix environment --ad-hoc coreutils -C
> guix environment: error: mount: mount "/gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16" on "/tmp/guix-directory.Nagh8Y//gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16": Operation not permitted
> $ uname -rv
> 4.19.0-13-amd64 #1 SMP Debian 4.19.160-2 (2020-11-28)
> $ cat /proc/sys/kernel/unprivileged_userns_clone
> 1
> --8<---------------cut here---------------end--------------->8---

With a bit older Debian than yours:

Toggle snippet (9 lines)
$ guix environment --ad-hoc coreutils -C
[env]$ uname -rv
4.19.0-8-amd64 #1 SMP Debian 4.19.98-1 (2020-01-26)
[env]$ cat /proc/sys/kernel/unprivileged_userns_clone
1
[env]$ exit
exit

On another machine with the same kernel:

Toggle snippet (10 lines)
$ guix environment --ad-hoc coreutils -C
[env]$ uname -rv
4.19.0-13-amd64 #1 SMP Debian 4.19.160-2 (2020-11-28)
[env]$ cat /proc/sys/kernel/unprivileged_userns_clone
1
[env]$ exit
exit


Maybe I misconfigured mines or something is different on yours. :-)


All the best,
simon
L
L
Ludovic Courtès wrote on 4 Feb 2021 15:41
(address . 46292@debbugs.gnu.org)(name . Dimitri DELABROYE)(address . dimitri.delabroye@inria.fr)
87im777v5o.fsf@gnu.org
Ludovic Courtès <ludovic.courtes@inria.fr> skribis:

Toggle quote (3 lines)
> The problem may have to do with the fact that /gnu/store is an NFS
> mount.

Maybe not? I tested on a similar setup where it Just Works:

Toggle snippet (16 lines)
$ guix describe
Generation 6 Feb 04 2021 15:37:16 (current)
guix e7195e8
repository URL: https://git.savannah.gnu.org/git/guix.git
branch: master
commit: e7195e83c85a83131c0981bae2b6e5613669ebd1
$ df -h /gnu/store
Filesystem Size Used Avail Use% Mounted on
<redacted>:/GNUSTORE 973G 118G 856G 13% /gnu/store
$ uname -rv
4.9.0-11-amd64 #1 SMP Debian 4.9.189-3+deb9u2 (2019-11-11)
$ guix environment -C --ad-hoc coreutils
[env]$ id
uid=11279(lcourtes) gid=10038(users) groups=10038(users),65534(overflow)

Ludo’.
L
L
Lucas Nussbaum wrote on 10 Feb 2021 07:04
more info
(address . 46292@debbugs.gnu.org)
20210210060403.GA15175@xanadu.blop.info
Hi,

This is not due to NFS, but due to the fact that the NFS mount is
mounted nosuid (and nodev, probably). I can reproduce it on a local
filesystem mounted nosuid.

It seems that, when remounting a bind mount which is originally nosuid
inside a mount ns, you need to specify explicitely the nosuid option, or
else can_change_locked_flags()[1] will return false.


There's a concept of "locked mount flags" that cannot be cleared by a
less privileged user (see [2]). Our call to 'mount -o remount' ignores the
fact that the filesystem is mounted nosuid (and does not include this
flag), so the remount call tries to remove nosuid, and fails.


This probably needs to be fixed in Guix by fetching the current mount
flags and including them in the bind+remount+readonly call.
Unfortunately I did not find an easy way to convert mount flags in
/proc/$$/mountinfo to flags for the mount syscall...

Lucas
L
L
Ludovic Courtès wrote on 18 Feb 2021 12:36
Re: bug#46292: ‘guix environment -C ’ fails with Linux 4.19 (Debian)
(address . 46292@debbugs.gnu.org)(name . Dimitri DELABROYE)(address . dimitri.delabroye@inria.fr)
878s7lsj4o.fsf@gnu.org
Ludovic Courtès <ludovic.courtes@inria.fr> skribis:

Toggle quote (14 lines)
> The read-only remount comes from ‘mount-file-system’ in (gnu build
> file-systems):
>
> ;; For read-only bind mounts, an extra remount is needed, as per
> ;; <http://lwn.net/Articles/281157/>, which still applies to Linux
> ;; 4.0.
> (when (and (= MS_BIND (logand flags MS_BIND))
> (= MS_RDONLY (logand flags MS_RDONLY)))
> (let ((flags (logior MS_BIND MS_REMOUNT MS_RDONLY)))
> (mount source mount-point type flags #f)))
>
> This recipe has been working well “forever”, although it’s probably
> unnecessary with recent kernels (the LWN article is from 2008).

Apparently the extra remount is still necessary, and the ‘mount’ command
does it for you if you combine ‘--bind’ with ‘-o ro’:

Toggle snippet (10 lines)
# strace -e mount mount --bind -o ro t m
mount("/tmp/t", "/tmp/m", 0xde1930, MS_RDONLY|MS_BIND, NULL) = 0
mount("none", "/tmp/m", NULL, MS_RDONLY|MS_REMOUNT|MS_BIND, NULL) = 0
+++ exited with 0 +++
# mount --version
mount from util-linux 2.35.1 (libmount 2.35.1: btrfs, namespaces, assert, debug)
# uname -sr
Linux 5.10.10-gnu

Ludo’.
L
L
Ludovic Courtès wrote on 18 Feb 2021 12:38
(name . Lucas Nussbaum)(address . lucas.nussbaum@inria.fr)(address . 46292@debbugs.gnu.org)
877dn5sj14.fsf_-_@gnu.org
Hi Lucas,

Lucas Nussbaum <lucas.nussbaum@inria.fr> skribis:

Toggle quote (17 lines)
> This is not due to NFS, but due to the fact that the NFS mount is
> mounted nosuid (and nodev, probably). I can reproduce it on a local
> filesystem mounted nosuid.
>
> It seems that, when remounting a bind mount which is originally nosuid
> inside a mount ns, you need to specify explicitely the nosuid option, or
> else can_change_locked_flags()[1] will return false.
>
> [1] https://github.com/torvalds/linux/blame/master/fs/namespace.c#L2480
>
> There's a concept of "locked mount flags" that cannot be cleared by a
> less privileged user (see [2]). Our call to 'mount -o remount' ignores the
> fact that the filesystem is mounted nosuid (and does not include this
> flag), so the remount call tries to remove nosuid, and fails.
>
> [2] https://github.com/torvalds/linux/commit/9566d6742852c527bf5af38af5cbb878dad75705

Ooh, thanks for investigating!

Toggle quote (5 lines)
> This probably needs to be fixed in Guix by fetching the current mount
> flags and including them in the bind+remount+readonly call.
> Unfortunately I did not find an easy way to convert mount flags in
> /proc/$$/mountinfo to flags for the mount syscall...

I tried grabbing mount options from there and reapplying them to the
MS_REMOUNT call (patch below). However, that still doesn’t work:

Toggle snippet (3 lines)
14273 mount("/gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16", "/tmp/guix-directory.Plgkgt//gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16", 0x236a4b0, MS_RDONLY|MS_REMOUNT|MS_BIND, "rw,nosuid,nodev,relatime") = -1 EPERM (Operation not permitted)

Interestingly, the ‘mount’ command does not attempt to re-apply the
original mount options (“nosuid” & co.):

Toggle snippet (9 lines)
# strace -e mount mount --bind -o ro t m
mount("/home/lcourtes/t", "/home/lcourtes/m", 0x564dde270cb0, MS_RDONLY|MS_BIND, NULL) = 0
mount("none", "/home/lcourtes/m", NULL, MS_RDONLY|MS_REMOUNT|MS_BIND, NULL) = -1 EPERM (Operation not permitted)
mount: /home/lcourtes/m: filesystem was mounted, but any subsequent operation failed: Unknown error 5005.
+++ exited with 32 +++
# mount --version
mount from util-linux 2.33.1 (libmount 2.33.1: selinux, smack, btrfs, namespaces, assert, debug)

To be continued…

Ludo’.
Attachment: file
L
L
Lucas Nussbaum wrote on 18 Feb 2021 14:23
Re: bug#46292: ‘guix envi ronment -C’ fails with Linux 4.19 (Debian)
(name . Ludovic Courtès)(address . ludovic.courtes@inria.fr)(address . 46292@debbugs.gnu.org)
20210218132334.GC20744@xanadu.blop.info
Hi Ludovic,

On 18/02/21 at 12:38 +0100, Ludovic Courtès wrote:
Toggle quote (35 lines)
> Hi Lucas,
>
> Lucas Nussbaum <lucas.nussbaum@inria.fr> skribis:
>
> > This is not due to NFS, but due to the fact that the NFS mount is
> > mounted nosuid (and nodev, probably). I can reproduce it on a local
> > filesystem mounted nosuid.
> >
> > It seems that, when remounting a bind mount which is originally nosuid
> > inside a mount ns, you need to specify explicitely the nosuid option, or
> > else can_change_locked_flags()[1] will return false.
> >
> > [1] https://github.com/torvalds/linux/blame/master/fs/namespace.c#L2480
> >
> > There's a concept of "locked mount flags" that cannot be cleared by a
> > less privileged user (see [2]). Our call to 'mount -o remount' ignores the
> > fact that the filesystem is mounted nosuid (and does not include this
> > flag), so the remount call tries to remove nosuid, and fails.
> >
> > [2] https://github.com/torvalds/linux/commit/9566d6742852c527bf5af38af5cbb878dad75705
>
> Ooh, thanks for investigating!
>
> > This probably needs to be fixed in Guix by fetching the current mount
> > flags and including them in the bind+remount+readonly call.
> > Unfortunately I did not find an easy way to convert mount flags in
> > /proc/$$/mountinfo to flags for the mount syscall...
>
> I tried grabbing mount options from there and reapplying them to the
> MS_REMOUNT call (patch below). However, that still doesn’t work:
>
> --8<---------------cut here---------------start------------->8---
> 14273 mount("/gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16", "/tmp/guix-directory.Plgkgt//gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16", 0x236a4b0, MS_RDONLY|MS_REMOUNT|MS_BIND, "rw,nosuid,nodev,relatime") = -1 EPERM (Operation not permitted)
> --8<---------------cut here---------------end--------------->8---

That's strange: it worked in my manual tests.

Toggle quote (15 lines)
> Interestingly, the ‘mount’ command does not attempt to re-apply the
> original mount options (“nosuid” & co.):
>
> --8<---------------cut here---------------start------------->8---
> # strace -e mount mount --bind -o ro t m
> mount("/home/lcourtes/t", "/home/lcourtes/m", 0x564dde270cb0, MS_RDONLY|MS_BIND, NULL) = 0
> mount("none", "/home/lcourtes/m", NULL, MS_RDONLY|MS_REMOUNT|MS_BIND, NULL) = -1 EPERM (Operation not permitted)
> mount: /home/lcourtes/m: filesystem was mounted, but any subsequent operation failed: Unknown error 5005.
> +++ exited with 32 +++
> # mount --version
> mount from util-linux 2.33.1 (libmount 2.33.1: selinux, smack, btrfs, namespaces, assert, debug)
> --8<---------------cut here---------------end--------------->8---
>
> To be continued…

I think that's something I also initially misunderstood as well: mount
-o remount,<flags> essentially means: remount the filesystem with a
fresh set of flags. The set of flags previously configured is completely
ignored.

Lucas
L
L
Ludovic Courtès wrote on 22 Feb 2021 10:46
Re: bug#46292: ‘guix environment -C ’ fails with Linux 4.19 (Debian)
(name . Lucas Nussbaum)(address . lucas.nussbaum@inria.fr)(address . 46292@debbugs.gnu.org)
871rd8e8p2.fsf@gnu.org
Hi Lucas,

Lucas Nussbaum <lucas.nussbaum@inria.fr> skribis:

Toggle quote (2 lines)
> On 18/02/21 at 12:38 +0100, Ludovic Courtès wrote:

[...]

Toggle quote (9 lines)
>> I tried grabbing mount options from there and reapplying them to the
>> MS_REMOUNT call (patch below). However, that still doesn’t work:
>>
>> --8<---------------cut here---------------start------------->8---
>> 14273 mount("/gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16", "/tmp/guix-directory.Plgkgt//gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16", 0x236a4b0, MS_RDONLY|MS_REMOUNT|MS_BIND, "rw,nosuid,nodev,relatime") = -1 EPERM (Operation not permitted)
>> --8<---------------cut here---------------end--------------->8---
>
> That's strange: it worked in my manual tests.

I investigated some more and can’t get it to work. Do you happen to
have a working invocation or C snippet?

Thanks,
Ludo’.
L
L
Lucas Nussbaum wrote on 22 Feb 2021 11:57
Re: bug#46292: ‘guix envi ronment -C’ fails with Linux 4.19 (Debian)
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 46292@debbugs.gnu.org)
20210222105736.GA31789@xanadu.blop.info
On 22/02/21 at 10:46 +0100, Ludovic Courtès wrote:
Toggle quote (20 lines)
> Hi Lucas,
>
> Lucas Nussbaum <lucas.nussbaum@inria.fr> skribis:
>
> > On 18/02/21 at 12:38 +0100, Ludovic Courtès wrote:
>
> [...]
>
> >> I tried grabbing mount options from there and reapplying them to the
> >> MS_REMOUNT call (patch below). However, that still doesn’t work:
> >>
> >> --8<---------------cut here---------------start------------->8---
> >> 14273 mount("/gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16", "/tmp/guix-directory.Plgkgt//gnu/store/mmhimfwmmidf09jw1plw3aw1g1zn2nkh-bash-static-5.0.16", 0x236a4b0, MS_RDONLY|MS_REMOUNT|MS_BIND, "rw,nosuid,nodev,relatime") = -1 EPERM (Operation not permitted)
> >> --8<---------------cut here---------------end--------------->8---
> >
> > That's strange: it worked in my manual tests.
>
> I investigated some more and can’t get it to work. Do you happen to
> have a working invocation or C snippet?

Here is an example:

root@grisou-48:/tmp# mkdir t m

without nosuid:

root@grisou-48:/tmp# unshare -mrf
mesg: cannot open /dev/pts/0: Permission denied
root@grisou-48:/tmp# mount --bind t m
root@grisou-48:/tmp# mount --bind -r -o remount ./t ./m
root@grisou-48:/tmp# logout

now remount with nosuid:

root@grisou-48:/tmp# mount -o remount,nosuid /tmp
root@grisou-48:/tmp# mount |grep /tmp
/dev/sda5 on /tmp type ext4 (rw,nosuid,relatime)

and try again:

root@grisou-48:/tmp# unshare -mrf
mesg: cannot open /dev/pts/0: Permission denied
root@grisou-48:/tmp# mount --bind t m
root@grisou-48:/tmp# mount |grep /tmp
/dev/sda5 on /tmp type ext4 (rw,nosuid,relatime)
/dev/sda5 on /tmp/m type ext4 (rw,nosuid,relatime)
root@grisou-48:/tmp# mount --bind -r -o remount ./t ./m
mount: /tmp/m: permission denied.

^ that's expected
but it works when specifying nosuid:

root@grisou-48:/tmp# mount --bind -r -o remount,nosuid ./t ./m
root@grisou-48:/tmp#

From strace:
mount("/tmp/t", "/tmp/m", 0x55e75bf38cb0, MS_RDONLY|MS_NOSUID|MS_REMOUNT|MS_BIND, NULL) = 0

MS_NOSUID is missing from mountflags in your invocation. Apparently data
can be NULL.
--
Lucas Nussbaum <lucas.nussbaum@inria.fr> +33 3 54 95 86 19
Responsable du programme plateformes d'expérimentation
DDO-SDT - Direction Générale Déléguée à l'Innovation - Inria
L
L
Ludovic Courtès wrote on 22 Feb 2021 14:59
Re: bug#46292: ‘guix environment -C ’ fails with Linux 4.19 (Debian)
(name . Lucas Nussbaum)(address . lucas.nussbaum@inria.fr)(address . 46292@debbugs.gnu.org)
8735xob3ua.fsf@gnu.org
Hi,

Lucas Nussbaum <lucas.nussbaum@inria.fr> skribis:

Toggle quote (6 lines)
>>From strace:
> mount("/tmp/t", "/tmp/m", 0x55e75bf38cb0, MS_RDONLY|MS_NOSUID|MS_REMOUNT|MS_BIND, NULL) = 0
>
> MS_NOSUID is missing from mountflags in your invocation. Apparently data
> can be NULL.

Ooooh, got it. It’s another instance of the mount flag vs. option
confusion (/proc/mounts & co. lump flags and options together in one
string).

The attached patch solves that. I’ll polish it and push soon.

Thank you!

Ludo’.
Attachment: file
L
L
Ludovic Courtès wrote on 22 Feb 2021 17:44
[PATCH 1/3] syscalls: Define MS_RELATIME.
(address . 46292@debbugs.gnu.org)(name . Ludovic Courtès)(address . ludo@gnu.org)
20210222164413.30996-1-ludo@gnu.org
* guix/build/syscalls.scm (MS_RELATIME): New variable.
---
guix/build/syscalls.scm | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

Toggle diff (26 lines)
diff --git a/guix/build/syscalls.scm b/guix/build/syscalls.scm
index 85c1c45f81..b19a7a271b 100644
--- a/guix/build/syscalls.scm
+++ b/guix/build/syscalls.scm
@@ -43,9 +43,10 @@
MS_NOEXEC
MS_REMOUNT
MS_NOATIME
+ MS_STRICTATIME
+ MS_RELATIME
MS_BIND
MS_MOVE
- MS_STRICTATIME
MS_LAZYTIME
MNT_FORCE
MNT_DETACH
@@ -466,6 +467,7 @@ the returned procedure is called."
(define MS_NOATIME 1024)
(define MS_BIND 4096)
(define MS_MOVE 8192)
+(define MS_RELATIME 2097152)
(define MS_STRICTATIME 16777216)
(define MS_LAZYTIME 33554432)
--
2.30.0
L
L
Ludovic Courtès wrote on 22 Feb 2021 17:44
[PATCH 2/3] syscalls: Add 'mounts' and the <mount> record type.
(address . 46292@debbugs.gnu.org)(name . Ludovic Courtès)(address . ludo@gnu.org)
20210222164413.30996-2-ludo@gnu.org
* guix/build/syscalls.scm (<mount>): New record type.
(option-string->mount-flags, mount-flags)
(octal-decode, mounts): New procedures.
(mount-points): Rewrite in terms of 'mount'.
* tests/syscalls.scm ("mounts"): New test.
---
guix/build/syscalls.scm | 112 +++++++++++++++++++++++++++++++++++++---
tests/syscalls.scm | 16 +++++-
2 files changed, 121 insertions(+), 7 deletions(-)

Toggle diff (176 lines)
diff --git a/guix/build/syscalls.scm b/guix/build/syscalls.scm
index b19a7a271b..552343a481 100644
--- a/guix/build/syscalls.scm
+++ b/guix/build/syscalls.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2015 David Thompson <davet@gnu.org>
;;; Copyright © 2015 Mark H Weaver <mhw@netris.org>
;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
@@ -54,7 +54,18 @@
UMOUNT_NOFOLLOW
restart-on-EINTR
+
+ mount?
+ mount-device-number
+ mount-source
+ mount-point
+ mount-type
+ mount-options
+ mount-flags
+
+ mounts
mount-points
+
swapon
swapoff
@@ -521,17 +532,106 @@ constants from <sys/mount.h>."
(when update-mtab?
(remove-from-mtab target)))))
-(define (mount-points)
- "Return the mounts points for currently mounted file systems."
- (call-with-input-file "/proc/mounts"
+;; Mount point information.
+(define-record-type <mount>
+ (%mount source point devno type options)
+ mount?
+ (devno mount-device-number) ;st_dev
+ (source mount-source) ;string
+ (point mount-point) ;string
+ (type mount-type) ;string
+ (options mount-options)) ;string
+
+(define (option-string->mount-flags str)
+ "Parse the \"option string\" STR as it appears in /proc/mounts and similar,
+and return two values: a mount bitmask (inclusive or of MS_* constants), and
+the remaining unprocessed options."
+ ;; Why do we need to do this? Because mount flags and mount options are
+ ;; often lumped together; this is the case in /proc/mounts & co., so we need
+ ;; to extract the bits that actually correspond to mount flags.
+
+ (define not-comma
+ (char-set-complement (char-set #\,)))
+
+ (define lst
+ (string-tokenize str not-comma))
+
+ (let loop ((options lst)
+ (mask 0)
+ (remainder '()))
+ (match options
+ (()
+ (values mask (string-concatenate-reverse remainder)))
+ ((head . tail)
+ (letrec-syntax ((match-options (syntax-rules (=>)
+ ((_)
+ (loop tail mask
+ (cons head remainder)))
+ ((_ (str => bit) rest ...)
+ (if (string=? str head)
+ (loop tail (logior bit mask)
+ remainder)
+ (match-options rest ...))))))
+ (match-options ("rw" => 0)
+ ("ro" => MS_RDONLY)
+ ("nosuid" => MS_NOSUID)
+ ("nodev" => MS_NODEV)
+ ("noexec" => MS_NOEXEC)
+ ("relatime" => MS_RELATIME)
+ ("noatime" => MS_NOATIME)))))))
+
+(define (mount-flags mount)
+ "Return the mount flags of MOUNT, a <mount> record, as an inclusive or of
+MS_* constants."
+ (option-string->mount-flags (mount-options mount)))
+
+(define (octal-decode str)
+ "Decode octal escapes from STR and return the corresponding string. STR may
+look like this: \"white\\040space\", which is decoded as \"white space\"."
+ (define char-set:octal
+ (char-set #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7))
+ (define (octal? c)
+ (char-set-contains? char-set:octal c))
+
+ (let loop ((chars (string->list str))
+ (result '()))
+ (match chars
+ (()
+ (list->string (reverse result)))
+ ((#\\ (? octal? a) (? octal? b) (? octal? c) . rest)
+ (loop rest
+ (cons (integer->char
+ (string->number (list->string (list a b c)) 8))
+ result)))
+ ((head . tail)
+ (loop tail (cons head result))))))
+
+(define (mounts)
+ "Return the list of mounts (<mount> records) visible in the namespace of the
+current process."
+ (define (string->device-number str)
+ (match (string-split str #\:)
+ (((= string->number major) (= string->number minor))
+ (+ (* major 256) minor))))
+
+ (call-with-input-file "/proc/self/mountinfo"
(lambda (port)
(let loop ((result '()))
(let ((line (read-line port)))
(if (eof-object? line)
(reverse result)
(match (string-tokenize line)
- ((source mount-point _ ...)
- (loop (cons mount-point result))))))))))
+ ((id parent-id major:minor root mount-point
+ options _ type source _ ...)
+ (let ((devno (string->device-number major:minor)))
+ (loop (cons (%mount (octal-decode source)
+ (octal-decode mount-point)
+ devno type options)
+ result)))))))))))
+
+(define (mount-points)
+ "Return the mounts points for currently mounted file systems."
+ (map mount-point (mounts)))
(define swapon
(let ((proc (syscall->procedure int "swapon" (list '* int))))
diff --git a/tests/syscalls.scm b/tests/syscalls.scm
index 09aa228e8e..706dd4177f 100644
--- a/tests/syscalls.scm
+++ b/tests/syscalls.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2015 David Thompson <davet@gnu.org>
;;; Copyright © 2020 Simon South <simon@simonsouth.net>
;;; Copyright © 2020 Mathieu Othacehe <m.othacehe@gmail.com>
@@ -56,6 +56,20 @@
;; Both return values have been encountered in the wild.
(memv (system-error-errno args) (list EPERM ENOENT)))))
+(test-assert "mounts"
+ ;; Check for one of the common mount points.
+ (let ((mounts (mounts)))
+ (any (match-lambda
+ ((point . type)
+ (let ((mount (find (lambda (mount)
+ (string=? (mount-point mount) point))
+ mounts)))
+ (and mount
+ (string=? (mount-type mount) type)))))
+ '(("/proc" . "proc")
+ ("/sys" . "sysfs")
+ ("/dev/shm" . "tmpfs")))))
+
(test-assert "mount-points"
;; Reportedly "/" is not always listed as a mount point, so check a few
;; others (see <http://bugs.gnu.org/20261>.)
--
2.30.0
L
L
Ludovic Courtès wrote on 22 Feb 2021 17:44
[PATCH 3/3] file-systems: 'mount-file-system' preserves source flags for bind mounts.
(address . 46292@debbugs.gnu.org)(name . Ludovic Courtès)(address . ludovic.courtes@inria.fr)
20210222164413.30996-3-ludo@gnu.org
From: Ludovic Courtès <ludovic.courtes@inria.fr>


* gnu/build/file-systems.scm (mount-file-system): If FS is a bind mount,
add its original mount flags to FLAGS.
---
gnu/build/file-systems.scm | 45 +++++++++++++++++++++++++-------------
1 file changed, 30 insertions(+), 15 deletions(-)

Toggle diff (80 lines)
diff --git a/gnu/build/file-systems.scm b/gnu/build/file-systems.scm
index ddf6117b67..aca4aad848 100644
--- a/gnu/build/file-systems.scm
+++ b/gnu/build/file-systems.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2014, 2015, 2016, 2017, 2018, 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2014, 2015, 2016, 2017, 2018, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2016, 2017 David Craven <david@craven.ch>
;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
;;; Copyright © 2019 Guillaume Le Vaillant <glv@posteo.net>
@@ -909,12 +909,27 @@ corresponds to the symbols listed in FLAGS."
(if options
(string-append "," options)
"")))))
- (let ((type (file-system-type fs))
- (options (file-system-options fs))
- (source (canonicalize-device-spec (file-system-device fs)))
- (mount-point (string-append root "/"
- (file-system-mount-point fs)))
- (flags (mount-flags->bit-mask (file-system-flags fs))))
+ (let* ((type (file-system-type fs))
+ (source (canonicalize-device-spec (file-system-device fs)))
+ (target (string-append root "/"
+ (file-system-mount-point fs)))
+ (flags (logior (mount-flags->bit-mask (file-system-flags fs))
+
+ ;; For bind mounts, preserve the original flags such
+ ;; as MS_NOSUID, etc. Failing to do that, the
+ ;; MS_REMOUNT call below fails with EPERM.
+ ;; See <https://bugs.gnu.org/46292>
+ (if (memq 'bind-mount (file-system-flags fs))
+ (or (and=> (find (let ((devno (stat:dev
+ (lstat source))))
+ (lambda (mount)
+ (= (mount-device-number mount)
+ devno)))
+ (mounts))
+ mount-flags)
+ 0)
+ 0)))
+ (options (file-system-options fs)))
(when (file-system-check? fs)
(check-file-system source type))
@@ -925,24 +940,24 @@ corresponds to the symbols listed in FLAGS."
;; needed.
(if (and (= MS_BIND (logand flags MS_BIND))
(not (file-is-directory? source)))
- (unless (file-exists? mount-point)
- (mkdir-p (dirname mount-point))
- (call-with-output-file mount-point (const #t)))
- (mkdir-p mount-point))
+ (unless (file-exists? target)
+ (mkdir-p (dirname target))
+ (call-with-output-file target (const #t)))
+ (mkdir-p target))
(cond
((string-prefix? "nfs" type)
- (mount-nfs source mount-point type flags options))
+ (mount-nfs source target type flags options))
(else
- (mount source mount-point type flags options)))
+ (mount source target type flags options)))
;; For read-only bind mounts, an extra remount is needed, as per
;; <http://lwn.net/Articles/281157/>, which still applies to Linux
;; 4.0.
(when (and (= MS_BIND (logand flags MS_BIND))
(= MS_RDONLY (logand flags MS_RDONLY)))
- (let ((flags (logior MS_BIND MS_REMOUNT MS_RDONLY)))
- (mount source mount-point type flags #f))))
+ (let ((flags (logior MS_REMOUNT flags)))
+ (mount source target type flags options))))
(lambda args
(or (file-system-mount-may-fail? fs)
(apply throw args))))))
--
2.30.0
L
L
Ludovic Courtès wrote on 25 Feb 2021 11:43
Re: bug#46292: ‘guix environment -C ’ fails with Linux 4.19 (Debian)
(name . Lucas Nussbaum)(address . lucas.nussbaum@inria.fr)
87tuq0zav7.fsf@gnu.org
Pushed as dcb640f02b1f9590c3bd4301a22bf31bd60c56d4, thanks!

Ludo’.
Closed
A
A
Andreas Enge wrote on 9 Mar 2021 17:19
Reopen
(address . 46292@debbugs.gnu.org)
YEegFFjobWC0cqGp@jurong
Hello,

I notice the exact same problem still on a Guix System (!) freshly
reconfigured with commit b1cabedd28b92324259875fc52ca5d52d411a026,
so the kernel is 5.11.4-gnu.

In my case /tmp is just an ordinary subdirectory of /, which itself is
LUKS encrypted and mounted from /dev/mapper/cryptroot.

The "permission denied" occurs for ordinary users and root.

Andreas
A
Closed
?