From debbugs-submit-bounces@debbugs.gnu.org Thu Feb 18 06:38:30 2021 Received: (at 46292) by debbugs.gnu.org; 18 Feb 2021 11:38:30 +0000 Received: from localhost ([127.0.0.1]:46222 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1lChdb-0005vD-6b for submit@debbugs.gnu.org; Thu, 18 Feb 2021 06:38:30 -0500 Received: from mail2-relais-roc.national.inria.fr ([192.134.164.83]:9561) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1lChdV-0005ux-Ni for 46292@debbugs.gnu.org; Thu, 18 Feb 2021 06:38:25 -0500 X-IronPort-AV: E=Sophos;i="5.81,187,1610406000"; d="scan'208";a="493658513" Received: from 91-160-117-201.subs.proxad.net (HELO ribbon) ([91.160.117.201]) by mail2-relais-roc.national.inria.fr with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 18 Feb 2021 12:38:15 +0100 From: =?utf-8?Q?Ludovic_Court=C3=A8s?= To: Lucas Nussbaum Subject: Re: bug#46292: =?utf-8?Q?=E2=80=98guix?= environment =?utf-8?Q?-C?= =?utf-8?Q?=E2=80=99?= fails with Linux 4.19 (Debian) References: <87h7ms8658.fsf@inria.fr> <20210210060403.GA15175@xanadu.blop.info> Date: Thu, 18 Feb 2021 12:38:15 +0100 In-Reply-To: <20210210060403.GA15175@xanadu.blop.info> (Lucas Nussbaum's message of "Wed, 10 Feb 2021 07:04:03 +0100") Message-ID: <877dn5sj14.fsf_-_@gnu.org> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 46292 Cc: 46292@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Hi Lucas, Lucas Nussbaum 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/9566d6742852c527bf5af38af5cb= b878dad75705 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=E2=80=99t 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,no= dev,relatime") =3D -1 EPERM (Operation not permitted) --8<---------------cut here---------------end--------------->8--- Interestingly, the =E2=80=98mount=E2=80=99 command does not attempt to re-a= pply the original mount options (=E2=80=9Cnosuid=E2=80=9D & 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) =3D 0 mount("none", "/home/lcourtes/m", NULL, MS_RDONLY|MS_REMOUNT|MS_BIND, NULL)= =3D -1 EPERM (Operation not permitted) mount: /home/lcourtes/m: filesystem was mounted, but any subsequent operati= on failed: Unknown error 5005. +++ exited with 32 +++ # mount --version mount from util-linux 2.33.1 (libmount 2.33.1: selinux, smack, btrfs, names= paces, assert, debug) --8<---------------cut here---------------end--------------->8--- To be continued=E2=80=A6 Ludo=E2=80=99. --=-=-= Content-Type: text/x-patch; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable diff --git a/gnu/build/file-systems.scm b/gnu/build/file-systems.scm index ddf6117b67..4ecb58c8ea 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 =C2=A9 2014, 2015, 2016, 2017, 2018, 2020 Ludovic Court=C3= =A8s +;;; Copyright =C2=A9 2014, 2015, 2016, 2017, 2018, 2020, 2021 Ludovic Cour= t=C3=A8s ;;; Copyright =C2=A9 2016, 2017 David Craven ;;; Copyright =C2=A9 2017 Mathieu Othacehe ;;; Copyright =C2=A9 2019 Guillaume Le Vaillant @@ -36,6 +36,7 @@ #:use-module (system foreign) #:autoload (system repl repl) (start-repl) #:use-module (srfi srfi-1) + #:use-module (srfi srfi-9) #:use-module (srfi srfi-26) #:export (disk-partitions partition-label-predicate @@ -886,6 +887,59 @@ corresponds to the symbols listed in FLAGS." (() 0)))) =20 +(define-record-type + (%mount source point devno type options) + mount? + (devno mount-device-number) ;st_dev + (source mount-source) + (point mount-point) + (type mount-type) + (options mount-options)) + +(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 (string->device-number str) + (match (string-split str #\:) + (((=3D string->number major) (=3D string->number minor)) + (+ (* major 256) minor)))) + +(define (mounts) + "Return the list of mounts ( records) visible in the namespace of= the +current process." + (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) + ((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-file-system fs #:key (root "/root")) "Mount the file system described by FS, a object, under RO= OT." =20 @@ -894,8 +948,8 @@ corresponds to the symbols listed in FLAGS." (host-part (string-take source idx)) ;; Strip [] from around host if present (host (match (string-split host-part (string->char-set "[]")) - (("" h "") h) - ((h) h))) + (("" h "") h) + ((h) h))) (aa (match (getaddrinfo host "nfs") ((x . _) x))) (sa (addrinfo:addr aa)) (inet-addr (inet-ntop (sockaddr:fam sa) @@ -912,7 +966,7 @@ corresponds to the symbols listed in FLAGS." (let ((type (file-system-type fs)) (options (file-system-options fs)) (source (canonicalize-device-spec (file-system-device fs))) - (mount-point (string-append root "/" + (target (string-append root "/" (file-system-mount-point fs))) (flags (mount-flags->bit-mask (file-system-flags fs)))) (when (file-system-check? fs) @@ -925,24 +979,30 @@ corresponds to the symbols listed in FLAGS." ;; needed. (if (and (=3D 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)) =20 (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))) =20 ;; For read-only bind mounts, an extra remount is needed, as per ;; , which still applies to Linux ;; 4.0. (when (and (=3D MS_BIND (logand flags MS_BIND)) (=3D 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_BIND MS_REMOUNT MS_RDONLY)) + (options (and=3D> (find (let ((devno (stat:dev (lstat sour= ce)))) + (lambda (mount) + (=3D (mount-device-number mount) + devno))) + (mounts)) + mount-options))) + (mount source target type flags options)))) (lambda args (or (file-system-mount-may-fail? fs) (apply throw args)))))) --=-=-=--