123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- ;;; GNU Guix --- Functional package management for GNU
- ;;; Copyright © 2013, 2014, 2015, 2018 Ludovic Courtès <ludo@gnu.org>
- ;;;
- ;;; This file is part of GNU Guix.
- ;;;
- ;;; GNU Guix is free software; you can redistribute it and/or modify it
- ;;; under the terms of the GNU General Public License as published by
- ;;; the Free Software Foundation; either version 3 of the License, or (at
- ;;; your option) any later version.
- ;;;
- ;;; GNU Guix is distributed in the hope that it will be useful, but
- ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
- ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;;; GNU General Public License for more details.
- ;;;
- ;;; You should have received a copy of the GNU General Public License
- ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
- (define-module (gnu build linux-initrd)
- #:use-module ((guix cpio) #:prefix cpio:)
- #:use-module (guix build utils)
- #:use-module (guix build store-copy)
- #:use-module (system base compile)
- #:use-module (rnrs bytevectors)
- #:use-module ((system foreign) #:select (sizeof))
- #:use-module (ice-9 ftw)
- #:export (write-cpio-archive
- build-initrd))
- ;;; Commentary:
- ;;;
- ;;; Tools to create Linux initial RAM disks ("initrds"). Initrds are
- ;;; essentially gzipped cpio archives, with a '/init' executable that the
- ;;; kernel runs at boot time.
- ;;;
- ;;; Code:
- (define* (write-cpio-archive output directory
- #:key
- (compress? #t)
- (gzip "gzip"))
- "Write a cpio archive containing DIRECTORY to file OUTPUT. When
- COMPRESS? is true, compress it using GZIP. On success, return OUTPUT."
- ;; Note: as per `ramfs-rootfs-initramfs.txt', always add directory entries
- ;; before the files that are inside of it: "The Linux kernel cpio
- ;; extractor won't create files in a directory that doesn't exist, so the
- ;; directory entries must go before the files that go in those
- ;; directories."
- (define files
- ;; Use 'sort' so that (1) the order of files is deterministic, and (2)
- ;; directories appear before the files they contain.
- (sort (file-system-fold (const #t) ;enter?
- (lambda (file stat result) ;leaf
- (cons file result))
- (lambda (dir stat result) ;down
- (if (string=? dir directory)
- result
- (cons dir result)))
- (lambda (file stat result)
- result)
- (const #f) ;skip
- (const #f) ;error
- '()
- directory)
- string<?))
- (call-with-output-file output
- (lambda (port)
- (cpio:write-cpio-archive files port
- #:file->header cpio:file->cpio-header*)))
- (if compress?
- ;; Gzip insists on adding a '.gz' suffix and does nothing if the input
- ;; file already has that suffix. Shuffle files around to placate it.
- (let* ((gz-suffix? (string-suffix? ".gz" output))
- (sans-gz (if gz-suffix?
- (string-drop-right output 3)
- output)))
- (when gz-suffix?
- (rename-file output sans-gz))
- ;; Use '--no-name' so that gzip records neither a file name nor a time
- ;; stamp in its output.
- (and (zero? (system* gzip "--best" "--no-name" sans-gz))
- (begin
- (unless gz-suffix?
- (rename-file (string-append output ".gz") output))
- output)))
- output))
- (define (cache-compiled-file-name file)
- "Return the file name of the in-cache .go file for FILE, relative to the
- current directory.
- This is similar to what 'compiled-file-name' in (system base compile) does."
- (let loop ((file file))
- (let ((target (false-if-exception (readlink file))))
- (if target
- (loop target)
- (format #f ".cache/guile/ccache/~a-~a-~a-~a/~a"
- (effective-version)
- (if (eq? (native-endianness) (endianness little))
- "LE"
- "BE")
- (sizeof '*)
- (effective-version)
- file)))))
- (define (compile-to-cache file)
- "Compile FILE to the cache."
- (let ((compiled-file (cache-compiled-file-name file)))
- (mkdir-p (dirname compiled-file))
- (compile-file file
- #:opts %auto-compilation-options
- #:output-file compiled-file)))
- (define* (build-initrd output
- #:key
- guile init
- (references-graphs '())
- (gzip "gzip"))
- "Write an initial RAM disk (initrd) to OUTPUT. The initrd starts the script
- at INIT, running GUILE. It contains all the items referred to by
- REFERENCES-GRAPHS."
- (mkdir "contents")
- ;; Copy the closures of all the items referenced in REFERENCES-GRAPHS.
- (populate-store references-graphs "contents")
- (with-directory-excursion "contents"
- ;; Make '/init'.
- (symlink init "init")
- ;; Compile it.
- (compile-to-cache "init")
- ;; Allow Guile to find out where it is (XXX). See
- ;; 'guile-relocatable.patch'.
- (mkdir-p "proc/self")
- (symlink (string-append guile "/bin/guile") "proc/self/exe")
- (readlink "proc/self/exe")
- ;; Reset the timestamps of all the files that will make it in the initrd.
- (for-each (lambda (file)
- (unless (eq? 'symlink (stat:type (lstat file)))
- (utime file 0 0 0 0)))
- (find-files "." ".*"))
- (write-cpio-archive output "." #:gzip gzip))
- ;; Make sure directories are writable so we can delete files.
- (for-each make-file-writable
- (find-files "contents"
- (lambda (file stat)
- (eq? 'directory (stat:type stat)))
- #:directories? #t))
- (delete-file-recursively "contents"))
- ;;; linux-initrd.scm ends here
|