install: Validate symlink target in evaluate-populate-directive.

* gnu/build/install.scm (evaluate-populate-directive): By default, error when
the target of a symlink doesn't exist.  Always ensure TARGET ends with "/".
(populate-root-file-system): Call evaluate-populate-directive with
 #:error-on-dangling-symlink #t and add comment.
This commit is contained in:
Maxim Cournoyer 2022-10-25 23:17:09 -04:00
parent 8934827014
commit 0bb872b379
No known key found for this signature in database
GPG key ID: 1260E46482E63562

View file

@ -1,6 +1,7 @@
;;; GNU Guix --- Functional package management for GNU ;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org> ;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2016 Chris Marusich <cmmarusich@gmail.com> ;;; Copyright © 2016 Chris Marusich <cmmarusich@gmail.com>
;;; Copyright © 2022 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;; ;;;
;;; This file is part of GNU Guix. ;;; This file is part of GNU Guix.
;;; ;;;
@ -56,19 +57,24 @@ that the fonts, background images, etc. referred to by BOOTCFG are not GC'd."
(define* (evaluate-populate-directive directive target (define* (evaluate-populate-directive directive target
#:key #:key
(default-gid 0) (default-gid 0)
(default-uid 0)) (default-uid 0)
(error-on-dangling-symlink? #t))
"Evaluate DIRECTIVE, an sexp describing a file or directory to create under "Evaluate DIRECTIVE, an sexp describing a file or directory to create under
directory TARGET. DEFAULT-UID and DEFAULT-GID are the default UID and GID in directory TARGET. DEFAULT-UID and DEFAULT-GID are the default UID and GID in
the context of the caller. If the directive matches those defaults then, the context of the caller. If the directive matches those defaults then,
'chown' won't be run." 'chown' won't be run. When ERROR-ON-DANGLING-SYMLINK? is true, abort with an
error when a dangling symlink would be created."
(define target* (if (string-suffix? "/" target)
target
(string-append target "/")))
(let loop ((directive directive)) (let loop ((directive directive))
(catch 'system-error (catch 'system-error
(lambda () (lambda ()
(match directive (match directive
(('directory name) (('directory name)
(mkdir-p (string-append target name))) (mkdir-p (string-append target* name)))
(('directory name uid gid) (('directory name uid gid)
(let ((dir (string-append target name))) (let ((dir (string-append target* name)))
(mkdir-p dir) (mkdir-p dir)
;; If called from a context without "root" permissions, "chown" ;; If called from a context without "root" permissions, "chown"
;; to root will fail. In that case, do not try to run "chown" ;; to root will fail. In that case, do not try to run "chown"
@ -78,27 +84,38 @@ the context of the caller. If the directive matches those defaults then,
(chown dir uid gid)))) (chown dir uid gid))))
(('directory name uid gid mode) (('directory name uid gid mode)
(loop `(directory ,name ,uid ,gid)) (loop `(directory ,name ,uid ,gid))
(chmod (string-append target name) mode)) (chmod (string-append target* name) mode))
(('file name) (('file name)
(call-with-output-file (string-append target name) (call-with-output-file (string-append target* name)
(const #t))) (const #t)))
(('file name (? string? content)) (('file name (? string? content))
(call-with-output-file (string-append target name) (call-with-output-file (string-append target* name)
(lambda (port) (lambda (port)
(display content port)))) (display content port))))
((new '-> old) ((new '-> old)
(let ((new* (string-append target* new)))
(let try () (let try ()
(catch 'system-error (catch 'system-error
(lambda () (lambda ()
(symlink old (string-append target new))) (when error-on-dangling-symlink?
;; When the symbolic link points to a relative path,
;; checking if its target exists must be done relatively
;; to the link location.
(unless (if (string-prefix? "/" old)
(file-exists? old)
(with-directory-excursion (dirname new*)
(file-exists? old)))
(error (format #f "symlink `~a' points to nonexistent \
file `~a'" new* old))))
(symlink old new*))
(lambda args (lambda args
;; When doing 'guix system init' on the current '/', some ;; When doing 'guix system init' on the current '/', some
;; symlinks may already exists. Override them. ;; symlinks may already exists. Override them.
(if (= EEXIST (system-error-errno args)) (if (= EEXIST (system-error-errno args))
(begin (begin
(delete-file (string-append target new)) (delete-file new*)
(try)) (try))
(apply throw args)))))))) (apply throw args)))))))))
(lambda args (lambda args
;; Usually we can only get here when installing to an existing root, ;; Usually we can only get here when installing to an existing root,
;; as with 'guix system init foo.scm /'. ;; as with 'guix system init foo.scm /'.
@ -142,7 +159,10 @@ STORE."
includes /etc, /var, /run, /bin/sh, etc., and all the symlinks to SYSTEM. includes /etc, /var, /run, /bin/sh, etc., and all the symlinks to SYSTEM.
EXTRAS is a list of directives appended to the built-in directives to populate EXTRAS is a list of directives appended to the built-in directives to populate
TARGET." TARGET."
(for-each (cut evaluate-populate-directive <> target) ;; It's expected that some symbolic link targets do not exist yet, so do not
;; error on dangling links.
(for-each (cut evaluate-populate-directive <> target
#:error-on-dangling-symlink? #f)
(append (directives (%store-directory)) extras)) (append (directives (%store-directory)) extras))
;; Add system generation 1. ;; Add system generation 1.