linux-initrd.scm 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2013, 2014, 2015, 2018 Ludovic Courtès <ludo@gnu.org>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (gnu build linux-initrd)
  19. #:use-module ((guix cpio) #:prefix cpio:)
  20. #:use-module (guix build utils)
  21. #:use-module (guix build store-copy)
  22. #:use-module (system base compile)
  23. #:use-module (rnrs bytevectors)
  24. #:use-module ((system foreign) #:select (sizeof))
  25. #:use-module (ice-9 ftw)
  26. #:export (write-cpio-archive
  27. build-initrd))
  28. ;;; Commentary:
  29. ;;;
  30. ;;; Tools to create Linux initial RAM disks ("initrds"). Initrds are
  31. ;;; essentially gzipped cpio archives, with a '/init' executable that the
  32. ;;; kernel runs at boot time.
  33. ;;;
  34. ;;; Code:
  35. (define* (write-cpio-archive output directory
  36. #:key
  37. (compress? #t)
  38. (gzip "gzip"))
  39. "Write a cpio archive containing DIRECTORY to file OUTPUT. When
  40. COMPRESS? is true, compress it using GZIP. On success, return OUTPUT."
  41. ;; Note: as per `ramfs-rootfs-initramfs.txt', always add directory entries
  42. ;; before the files that are inside of it: "The Linux kernel cpio
  43. ;; extractor won't create files in a directory that doesn't exist, so the
  44. ;; directory entries must go before the files that go in those
  45. ;; directories."
  46. (define files
  47. ;; Use 'sort' so that (1) the order of files is deterministic, and (2)
  48. ;; directories appear before the files they contain.
  49. (sort (file-system-fold (const #t) ;enter?
  50. (lambda (file stat result) ;leaf
  51. (cons file result))
  52. (lambda (dir stat result) ;down
  53. (if (string=? dir directory)
  54. result
  55. (cons dir result)))
  56. (lambda (file stat result)
  57. result)
  58. (const #f) ;skip
  59. (const #f) ;error
  60. '()
  61. directory)
  62. string<?))
  63. (call-with-output-file output
  64. (lambda (port)
  65. (cpio:write-cpio-archive files port
  66. #:file->header cpio:file->cpio-header*)))
  67. (if compress?
  68. ;; Gzip insists on adding a '.gz' suffix and does nothing if the input
  69. ;; file already has that suffix. Shuffle files around to placate it.
  70. (let* ((gz-suffix? (string-suffix? ".gz" output))
  71. (sans-gz (if gz-suffix?
  72. (string-drop-right output 3)
  73. output)))
  74. (when gz-suffix?
  75. (rename-file output sans-gz))
  76. ;; Use '--no-name' so that gzip records neither a file name nor a time
  77. ;; stamp in its output.
  78. (and (zero? (system* gzip "--best" "--no-name" sans-gz))
  79. (begin
  80. (unless gz-suffix?
  81. (rename-file (string-append output ".gz") output))
  82. output)))
  83. output))
  84. (define (cache-compiled-file-name file)
  85. "Return the file name of the in-cache .go file for FILE, relative to the
  86. current directory.
  87. This is similar to what 'compiled-file-name' in (system base compile) does."
  88. (let loop ((file file))
  89. (let ((target (false-if-exception (readlink file))))
  90. (if target
  91. (loop target)
  92. (format #f ".cache/guile/ccache/~a-~a-~a-~a/~a"
  93. (effective-version)
  94. (if (eq? (native-endianness) (endianness little))
  95. "LE"
  96. "BE")
  97. (sizeof '*)
  98. (effective-version)
  99. file)))))
  100. (define (compile-to-cache file)
  101. "Compile FILE to the cache."
  102. (let ((compiled-file (cache-compiled-file-name file)))
  103. (mkdir-p (dirname compiled-file))
  104. (compile-file file
  105. #:opts %auto-compilation-options
  106. #:output-file compiled-file)))
  107. (define* (build-initrd output
  108. #:key
  109. guile init
  110. (references-graphs '())
  111. (gzip "gzip"))
  112. "Write an initial RAM disk (initrd) to OUTPUT. The initrd starts the script
  113. at INIT, running GUILE. It contains all the items referred to by
  114. REFERENCES-GRAPHS."
  115. (mkdir "contents")
  116. ;; Copy the closures of all the items referenced in REFERENCES-GRAPHS.
  117. (populate-store references-graphs "contents")
  118. (with-directory-excursion "contents"
  119. ;; Make '/init'.
  120. (symlink init "init")
  121. ;; Compile it.
  122. (compile-to-cache "init")
  123. ;; Allow Guile to find out where it is (XXX). See
  124. ;; 'guile-relocatable.patch'.
  125. (mkdir-p "proc/self")
  126. (symlink (string-append guile "/bin/guile") "proc/self/exe")
  127. (readlink "proc/self/exe")
  128. ;; Reset the timestamps of all the files that will make it in the initrd.
  129. (for-each (lambda (file)
  130. (unless (eq? 'symlink (stat:type (lstat file)))
  131. (utime file 0 0 0 0)))
  132. (find-files "." ".*"))
  133. (write-cpio-archive output "." #:gzip gzip))
  134. ;; Make sure directories are writable so we can delete files.
  135. (for-each make-file-writable
  136. (find-files "contents"
  137. (lambda (file stat)
  138. (eq? 'directory (stat:type stat)))
  139. #:directories? #t))
  140. (delete-file-recursively "contents"))
  141. ;;; linux-initrd.scm ends here