image.scm 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2020, 2021 Mathieu Othacehe <m.othacehe@gmail.com>
  3. ;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
  4. ;;;
  5. ;;; This file is part of GNU Guix.
  6. ;;;
  7. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  8. ;;; under the terms of the GNU General Public License as published by
  9. ;;; the Free Software Foundation; either version 3 of the License, or (at
  10. ;;; your option) any later version.
  11. ;;;
  12. ;;; GNU Guix is distributed in the hope that it will be useful, but
  13. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;;; GNU General Public License for more details.
  16. ;;;
  17. ;;; You should have received a copy of the GNU General Public License
  18. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  19. (define-module (gnu system image)
  20. #:use-module (guix diagnostics)
  21. #:use-module (guix discovery)
  22. #:use-module (guix gexp)
  23. #:use-module (guix modules)
  24. #:use-module (guix monads)
  25. #:use-module (guix records)
  26. #:use-module (guix store)
  27. #:use-module (guix ui)
  28. #:use-module (guix utils)
  29. #:use-module ((guix self) #:select (make-config.scm))
  30. #:use-module (gnu bootloader)
  31. #:use-module (gnu bootloader grub)
  32. #:use-module (gnu image)
  33. #:use-module (gnu platform)
  34. #:use-module (gnu services)
  35. #:use-module (gnu services base)
  36. #:use-module (gnu system)
  37. #:use-module (gnu system file-systems)
  38. #:use-module (gnu system linux-container)
  39. #:use-module (gnu system uuid)
  40. #:use-module (gnu system vm)
  41. #:use-module (guix packages)
  42. #:use-module (gnu packages base)
  43. #:use-module (gnu packages bootloaders)
  44. #:use-module (gnu packages cdrom)
  45. #:use-module (gnu packages compression)
  46. #:use-module (gnu packages disk)
  47. #:use-module (gnu packages gawk)
  48. #:use-module (gnu packages genimage)
  49. #:use-module (gnu packages guile)
  50. #:autoload (gnu packages gnupg) (guile-gcrypt)
  51. #:use-module (gnu packages hurd)
  52. #:use-module (gnu packages linux)
  53. #:use-module (gnu packages mtools)
  54. #:use-module (gnu packages virtualization)
  55. #:use-module ((srfi srfi-1) #:prefix srfi-1:)
  56. #:use-module (srfi srfi-11)
  57. #:use-module (srfi srfi-26)
  58. #:use-module (srfi srfi-34)
  59. #:use-module (srfi srfi-35)
  60. #:use-module (rnrs bytevectors)
  61. #:use-module (ice-9 format)
  62. #:use-module (ice-9 match)
  63. #:export (root-offset
  64. root-label
  65. esp-partition
  66. root-partition
  67. efi-disk-image
  68. iso9660-image
  69. docker-image
  70. raw-with-offset-disk-image
  71. image-with-os
  72. efi-raw-image-type
  73. qcow2-image-type
  74. iso-image-type
  75. uncompressed-iso-image-type
  76. docker-image-type
  77. raw-with-offset-image-type
  78. image-with-label
  79. system-image
  80. %image-types
  81. lookup-image-type-by-name))
  82. ;;;
  83. ;;; Images definitions.
  84. ;;;
  85. ;; This is the offset before the first partition. GRUB will install itself in
  86. ;; this post-MBR gap.
  87. (define root-offset (* 512 2048))
  88. ;; Generic root partition label.
  89. (define root-label "Guix_image")
  90. (define esp-partition
  91. (partition
  92. (size (* 40 (expt 2 20)))
  93. (offset root-offset)
  94. (label "GNU-ESP") ;cosmetic only
  95. ;; Use "vfat" here since this property is used when mounting. The actual
  96. ;; FAT-ness is based on file system size (16 in this case).
  97. (file-system "vfat")
  98. (flags '(esp))
  99. (initializer (gexp initialize-efi-partition))))
  100. (define root-partition
  101. (partition
  102. (size 'guess)
  103. (label root-label)
  104. (file-system "ext4")
  105. (flags '(boot))
  106. (initializer (gexp initialize-root-partition))))
  107. (define efi-disk-image
  108. (image
  109. (format 'disk-image)
  110. (partitions (list esp-partition root-partition))))
  111. (define iso9660-image
  112. (image
  113. (format 'iso9660)
  114. (partitions
  115. (list (partition
  116. (size 'guess)
  117. (label "GUIX_IMAGE")
  118. (flags '(boot)))))))
  119. (define docker-image
  120. (image
  121. (format 'docker)))
  122. (define* (raw-with-offset-disk-image #:optional (offset root-offset))
  123. (image
  124. (format 'disk-image)
  125. (partitions
  126. (list (partition
  127. (inherit root-partition)
  128. (offset offset))))
  129. ;; FIXME: Deleting and creating "/var/run" and "/tmp" on the overlayfs
  130. ;; fails.
  131. (volatile-root? #f)))
  132. ;;;
  133. ;;; Images types.
  134. ;;;
  135. (define-syntax-rule (image-with-os base-image os)
  136. "Return an image inheriting from BASE-IMAGE, with the operating-system field
  137. set to the given OS."
  138. (image
  139. (inherit base-image)
  140. (operating-system os)))
  141. (define efi-raw-image-type
  142. (image-type
  143. (name 'efi-raw)
  144. (constructor (cut image-with-os efi-disk-image <>))))
  145. (define qcow2-image-type
  146. (image-type
  147. (name 'qcow2)
  148. (constructor (cut image-with-os
  149. (image
  150. (inherit efi-disk-image)
  151. (name 'image.qcow2)
  152. (format 'compressed-qcow2))
  153. <>))))
  154. (define iso-image-type
  155. (image-type
  156. (name 'iso9660)
  157. (constructor (cut image-with-os iso9660-image <>))))
  158. (define uncompressed-iso-image-type
  159. (image-type
  160. (name 'uncompressed-iso9660)
  161. (constructor (cut image-with-os
  162. (image
  163. (inherit iso9660-image)
  164. (compression? #f))
  165. <>))))
  166. (define docker-image-type
  167. (image-type
  168. (name 'docker)
  169. (constructor (cut image-with-os docker-image <>))))
  170. (define raw-with-offset-image-type
  171. (image-type
  172. (name 'raw-with-offset)
  173. (constructor (cut image-with-os (raw-with-offset-disk-image) <>))))
  174. ;;
  175. ;; Helpers.
  176. ;;
  177. (define not-config?
  178. ;; Select (guix …) and (gnu …) modules, except (guix config).
  179. (match-lambda
  180. (('guix 'config) #f)
  181. (('guix rest ...) #t)
  182. (('gnu rest ...) #t)
  183. (rest #f)))
  184. (define (partition->gexp partition)
  185. "Turn PARTITION, a <partition> object, into a list-valued gexp suitable for
  186. 'make-partition-image'."
  187. #~'(#$@(list (partition-size partition))
  188. #$(partition-file-system partition)
  189. #$(partition-file-system-options partition)
  190. #$(partition-label partition)
  191. #$(and=> (partition-uuid partition)
  192. uuid-bytevector)))
  193. (define gcrypt-sqlite3&co
  194. ;; Guile-Gcrypt, Guile-SQLite3, and their propagated inputs.
  195. (srfi-1:append-map
  196. (lambda (package)
  197. (cons package
  198. (match (package-transitive-propagated-inputs package)
  199. (((labels packages) ...)
  200. packages))))
  201. (list guile-gcrypt guile-sqlite3)))
  202. (define-syntax-rule (with-imported-modules* gexp* ...)
  203. (with-extensions gcrypt-sqlite3&co
  204. (with-imported-modules `(,@(source-module-closure
  205. '((gnu build image)
  206. (gnu build bootloader)
  207. (gnu build hurd-boot)
  208. (gnu build linux-boot)
  209. (guix store database))
  210. #:select? not-config?)
  211. ((guix config) => ,(make-config.scm)))
  212. #~(begin
  213. (use-modules (gnu build image)
  214. (gnu build bootloader)
  215. (gnu build hurd-boot)
  216. (gnu build linux-boot)
  217. (guix store database)
  218. (guix build utils))
  219. gexp* ...))))
  220. (define (root-partition? partition)
  221. "Return true if PARTITION is the root partition, false otherwise."
  222. (member 'boot (partition-flags partition)))
  223. (define (find-root-partition image)
  224. "Return the root partition of the given IMAGE."
  225. (srfi-1:find root-partition? (image-partitions image)))
  226. (define (root-partition-index image)
  227. "Return the index of the root partition of the given IMAGE."
  228. (1+ (srfi-1:list-index root-partition? (image-partitions image))))
  229. ;;
  230. ;; Disk image.
  231. ;;
  232. (define* (system-disk-image image
  233. #:key
  234. (name "disk-image")
  235. bootcfg
  236. bootloader
  237. register-closures?
  238. (inputs '()))
  239. "Return as a file-like object, the disk-image described by IMAGE. Said
  240. image can be copied on a USB stick as is. BOOTLOADER is the bootloader that
  241. will be installed and configured according to BOOTCFG parameter.
  242. Raw images of the IMAGE partitions are first created. Then, genimage is used
  243. to assemble the partition images into a disk-image without resorting to a
  244. virtual machine.
  245. INPUTS is a list of inputs (as for packages). When REGISTER-CLOSURES? is
  246. true, register INPUTS in the store database of the image so that Guix can be
  247. used in the image."
  248. (define genimage-name "image")
  249. (define (image->genimage-cfg image)
  250. ;; Return as a file-like object, the genimage configuration file
  251. ;; describing the given IMAGE.
  252. (define (format->image-type format)
  253. ;; Return the genimage format corresponding to FORMAT. For now, only
  254. ;; the hdimage format (raw disk-image) is supported.
  255. (cond
  256. ((memq format '(disk-image compressed-qcow2)) "hdimage")
  257. (else
  258. (raise (condition
  259. (&message
  260. (message
  261. (format #f (G_ "Unsupported image type ~a~%.") format))))))))
  262. (define (partition->dos-type partition)
  263. ;; Return the MBR partition type corresponding to the given PARTITION.
  264. ;; See: https://en.wikipedia.org/wiki/Partition_type.
  265. (let ((flags (partition-flags partition)))
  266. (cond
  267. ((member 'esp flags) "0xEF")
  268. (else "0x83"))))
  269. (define (partition->gpt-type partition)
  270. ;; Return the genimage GPT partition type code corresponding to PARTITION.
  271. ;; See https://github.com/pengutronix/genimage/blob/master/README.rst
  272. (let ((flags (partition-flags partition)))
  273. (cond
  274. ((member 'esp flags) "U")
  275. (else "L"))))
  276. (define (partition-image partition)
  277. ;; Return as a file-like object, an image of the given PARTITION. A
  278. ;; directory, filled by calling the PARTITION initializer procedure, is
  279. ;; first created within the store. Then, an image of this directory is
  280. ;; created using tools such as 'mke2fs' or 'mkdosfs', depending on the
  281. ;; partition file-system type.
  282. (let* ((os (image-operating-system image))
  283. (schema (local-file (search-path %load-path
  284. "guix/store/schema.sql")))
  285. (graph (match inputs
  286. (((names . _) ...)
  287. names)))
  288. (type (partition-file-system partition))
  289. (image-builder
  290. (with-imported-modules*
  291. (let ((initializer #$(partition-initializer partition))
  292. (inputs '#+(list e2fsprogs fakeroot dosfstools mtools))
  293. (image-root "tmp-root"))
  294. (sql-schema #$schema)
  295. (set-path-environment-variable "PATH" '("bin" "sbin") inputs)
  296. ;; Allow non-ASCII file names--e.g., 'nss-certs'--to be
  297. ;; decoded.
  298. (setenv "GUIX_LOCPATH"
  299. #+(file-append glibc-utf8-locales "/lib/locale"))
  300. (setlocale LC_ALL "en_US.utf8")
  301. (initializer image-root
  302. #:references-graphs '#$graph
  303. #:deduplicate? #f
  304. #:copy-closures? (not
  305. #$(image-shared-store? image))
  306. #:system-directory #$os
  307. #:grub-efi #+grub-efi
  308. #:bootloader-package
  309. #+(bootloader-package bootloader)
  310. #:bootloader-installer
  311. #+(bootloader-installer bootloader)
  312. #:bootcfg #$bootcfg
  313. #:bootcfg-location
  314. #$(bootloader-configuration-file bootloader))
  315. (make-partition-image #$(partition->gexp partition)
  316. #$output
  317. image-root)))))
  318. (computed-file "partition.img" image-builder
  319. ;; Allow offloading so that this I/O-intensive process
  320. ;; doesn't run on the build farm's head node.
  321. #:local-build? #f
  322. #:options `(#:references-graphs ,inputs))))
  323. (define (gpt-image? image)
  324. (eq? 'gpt (image-partition-table-type image)))
  325. (define (partition-type-values image partition)
  326. (if (gpt-image? image)
  327. (values "partition-type-uuid" (partition->gpt-type partition))
  328. (values "partition-type" (partition->dos-type partition))))
  329. (define (partition->config image partition)
  330. ;; Return the genimage partition configuration for PARTITION.
  331. (let-values (((partition-type-attribute partition-type-value)
  332. (partition-type-values image partition)))
  333. (let ((label (partition-label partition))
  334. (image (partition-image partition))
  335. (offset (partition-offset partition)))
  336. #~(format #f "~/partition ~a {
  337. ~/~/~a = ~a
  338. ~/~/image = \"~a\"
  339. ~/~/offset = \"~a\"
  340. ~/}"
  341. #$label
  342. #$partition-type-attribute
  343. #$partition-type-value
  344. #$image
  345. #$offset))))
  346. (define (genimage-type-options image-type image)
  347. (cond
  348. ((equal? image-type "hdimage")
  349. (format #f "~%~/~/gpt = ~a~%~/"
  350. (if (gpt-image? image) "true" "false")))
  351. (else "")))
  352. (let* ((format (image-format image))
  353. (image-type (format->image-type format))
  354. (image-type-options (genimage-type-options image-type image))
  355. (partitions (image-partitions image))
  356. (partitions-config (map (cut partition->config image <>) partitions))
  357. (builder
  358. #~(begin
  359. (let ((format (@ (ice-9 format) format)))
  360. (call-with-output-file #$output
  361. (lambda (port)
  362. (format port
  363. "\
  364. image ~a {
  365. ~/~a {~a}
  366. ~{~a~^~%~}
  367. }~%" #$genimage-name #$image-type #$image-type-options
  368. (list #$@partitions-config))))))))
  369. (computed-file "genimage.cfg" builder)))
  370. (let* ((image-name (image-name image))
  371. (name (if image-name
  372. (symbol->string image-name)
  373. name))
  374. (format (image-format image))
  375. (substitutable? (image-substitutable? image))
  376. (builder
  377. (with-imported-modules*
  378. (let ((inputs '#+(list genimage coreutils findutils qemu-minimal))
  379. (bootloader-installer
  380. #+(bootloader-disk-image-installer bootloader))
  381. (out-image (string-append "images/" #$genimage-name)))
  382. (set-path-environment-variable "PATH" '("bin" "sbin") inputs)
  383. (genimage #$(image->genimage-cfg image))
  384. ;; Install the bootloader directly on the disk-image.
  385. (when bootloader-installer
  386. (bootloader-installer
  387. #+(bootloader-package bootloader)
  388. #$(root-partition-index image)
  389. out-image))
  390. (convert-disk-image out-image '#$format #$output)))))
  391. (computed-file name builder
  392. #:local-build? #f ;too I/O-intensive
  393. #:options `(#:substitutable? ,substitutable?))))
  394. ;;
  395. ;; ISO9660 image.
  396. ;;
  397. (define (has-guix-service-type? os)
  398. "Return true if OS contains a service of the type GUIX-SERVICE-TYPE."
  399. (not (not (srfi-1:find (lambda (service)
  400. (eq? (service-kind service) guix-service-type))
  401. (operating-system-services os)))))
  402. (define* (system-iso9660-image image
  403. #:key
  404. (name "image.iso")
  405. bootcfg
  406. bootloader
  407. register-closures?
  408. (inputs '())
  409. (grub-mkrescue-environment '()))
  410. "Return as a file-like object a bootable, stand-alone iso9660 image.
  411. INPUTS is a list of inputs (as for packages). When REGISTER-CLOSURES? is
  412. true, register INPUTS in the store database of the image so that Guix can be
  413. used in the image. "
  414. (define root-label
  415. (match (image-partitions image)
  416. ((partition)
  417. (partition-label partition))))
  418. (define root-uuid
  419. (match (image-partitions image)
  420. ((partition)
  421. (uuid-bytevector (partition-uuid partition)))))
  422. (let* ((os (image-operating-system image))
  423. (bootloader (bootloader-package bootloader))
  424. (compression? (image-compression? image))
  425. (substitutable? (image-substitutable? image))
  426. (schema (local-file (search-path %load-path
  427. "guix/store/schema.sql")))
  428. (graph (match inputs
  429. (((names . _) ...)
  430. names)))
  431. (builder
  432. (with-imported-modules*
  433. (let* ((inputs '#$(list parted e2fsprogs dosfstools xorriso
  434. sed grep coreutils findutils gawk))
  435. (image-root "tmp-root"))
  436. (sql-schema #$schema)
  437. ;; Allow non-ASCII file names--e.g., 'nss-certs'--to be decoded.
  438. (setenv "GUIX_LOCPATH"
  439. #+(file-append glibc-utf8-locales "/lib/locale"))
  440. (setlocale LC_ALL "en_US.utf8")
  441. (set-path-environment-variable "PATH" '("bin" "sbin") inputs)
  442. (initialize-root-partition image-root
  443. #:references-graphs '#$graph
  444. #:deduplicate? #f
  445. #:system-directory #$os)
  446. (make-iso9660-image #$xorriso
  447. '#$grub-mkrescue-environment
  448. #$bootloader
  449. #$bootcfg
  450. #$os
  451. image-root
  452. #$output
  453. #:references-graphs '#$graph
  454. #:register-closures? #$register-closures?
  455. #:compression? #$compression?
  456. #:volume-id #$root-label
  457. #:volume-uuid #$root-uuid)))))
  458. (computed-file name builder
  459. ;; Allow offloading so that this I/O-intensive process
  460. ;; doesn't run on the build farm's head node.
  461. #:local-build? #f
  462. #:options `(#:references-graphs ,inputs
  463. #:substitutable? ,substitutable?))))
  464. (define (image-with-label base-image label)
  465. "The volume ID of an ISO is the label of the first partition. This procedure
  466. returns an image record where the first partition's label is set to <label>."
  467. (image
  468. (inherit base-image)
  469. (partitions
  470. (match (image-partitions base-image)
  471. ((boot others ...)
  472. (cons
  473. (partition
  474. (inherit boot)
  475. (label label))
  476. others))))))
  477. ;;
  478. ;; Docker image.
  479. ;;
  480. (define* (system-docker-image image
  481. #:key
  482. (name "docker-image"))
  483. "Build a docker image for IMAGE. NAME is the base name to use for the
  484. output file."
  485. (define boot-program
  486. ;; Program that runs the boot script of OS, which in turn starts shepherd.
  487. (program-file "boot-program"
  488. #~(let ((system (cadr (command-line))))
  489. (setenv "GUIX_NEW_SYSTEM" system)
  490. (execl #$(file-append guile-3.0 "/bin/guile")
  491. "guile" "--no-auto-compile"
  492. (string-append system "/boot")))))
  493. (define shared-network?
  494. (image-shared-network? image))
  495. (let* ((os (operating-system-with-gc-roots
  496. (containerized-operating-system
  497. (image-operating-system image) '()
  498. #:shared-network?
  499. shared-network?)
  500. (list boot-program)))
  501. (substitutable? (image-substitutable? image))
  502. (register-closures? (has-guix-service-type? os))
  503. (schema (and register-closures?
  504. (local-file (search-path %load-path
  505. "guix/store/schema.sql"))))
  506. (name (string-append name ".tar.gz"))
  507. (graph "system-graph"))
  508. (define builder
  509. (with-extensions (cons guile-json-3 ;for (guix docker)
  510. gcrypt-sqlite3&co) ;for (guix store database)
  511. (with-imported-modules `(,@(source-module-closure
  512. '((guix docker)
  513. (guix store database)
  514. (guix build utils)
  515. (guix build store-copy)
  516. (gnu build image))
  517. #:select? not-config?)
  518. ((guix config) => ,(make-config.scm)))
  519. #~(begin
  520. (use-modules (guix docker)
  521. (guix build utils)
  522. (gnu build image)
  523. (srfi srfi-19)
  524. (guix build store-copy)
  525. (guix store database))
  526. ;; Set the SQL schema location.
  527. (sql-schema #$schema)
  528. ;; Allow non-ASCII file names--e.g., 'nss-certs'--to be decoded.
  529. (setenv "GUIX_LOCPATH"
  530. #+(file-append glibc-utf8-locales "/lib/locale"))
  531. (setlocale LC_ALL "en_US.utf8")
  532. (set-path-environment-variable "PATH" '("bin" "sbin") '(#+tar))
  533. (let ((image-root (string-append (getcwd) "/tmp-root")))
  534. (mkdir-p image-root)
  535. (initialize-root-partition image-root
  536. #:references-graphs '(#$graph)
  537. #:copy-closures? #f
  538. #:register-closures? #$register-closures?
  539. #:deduplicate? #f
  540. #:system-directory #$os)
  541. (build-docker-image
  542. #$output
  543. (cons* image-root
  544. (map store-info-item
  545. (call-with-input-file #$graph
  546. read-reference-graph)))
  547. #$os
  548. #:entry-point '(#$boot-program #$os)
  549. #:compressor '(#+(file-append gzip "/bin/gzip") "-9n")
  550. #:creation-time (make-time time-utc 0 1)
  551. #:transformations `((,image-root -> ""))))))))
  552. (computed-file name builder
  553. ;; Allow offloading so that this I/O-intensive process
  554. ;; doesn't run on the build farm's head node.
  555. #:local-build? #f
  556. #:options `(#:references-graphs ((,graph ,os))
  557. #:substitutable? ,substitutable?))))
  558. ;;
  559. ;; Image creation.
  560. ;;
  561. (define (image->root-file-system image)
  562. "Return the IMAGE root partition file-system type."
  563. (case (image-format image)
  564. ((iso9660) "iso9660")
  565. ((docker) "dummy")
  566. (else
  567. (partition-file-system (find-root-partition image)))))
  568. (define (root-size image)
  569. "Return the root partition size of IMAGE."
  570. (let* ((image-size (image-size image))
  571. (root-partition (find-root-partition image))
  572. (root-size (partition-size root-partition)))
  573. (cond
  574. ((and (eq? root-size 'guess) image-size)
  575. image-size)
  576. (else root-size))))
  577. (define* (image-with-os* base-image os)
  578. "Return an image based on BASE-IMAGE but with the operating-system field set
  579. to OS. Also set the UUID and the size of the root partition."
  580. (define root-file-system
  581. (srfi-1:find
  582. (lambda (fs)
  583. (string=? (file-system-mount-point fs) "/"))
  584. (operating-system-file-systems os)))
  585. (image
  586. (inherit base-image)
  587. (operating-system os)
  588. (partitions
  589. (map (lambda (p)
  590. (if (root-partition? p)
  591. (partition
  592. (inherit p)
  593. (uuid (file-system-device root-file-system))
  594. (size (root-size base-image)))
  595. p))
  596. (image-partitions base-image)))))
  597. (define (operating-system-for-image image)
  598. "Return an operating-system based on the one specified in IMAGE, but
  599. suitable for image creation. Assign an UUID to the root file-system, so that
  600. it can be used for bootloading."
  601. (define volatile-root? (if (eq? (image-format image) 'iso9660)
  602. #t
  603. (image-volatile-root? image)))
  604. (define (root-uuid os)
  605. ;; UUID of the root file system, computed in a deterministic fashion.
  606. ;; This is what we use to locate the root file system so it has to be
  607. ;; different from the user's own file system UUIDs.
  608. (let ((type (if (eq? (image-format image) 'iso9660)
  609. 'iso9660
  610. 'dce)))
  611. (operating-system-uuid os type)))
  612. (let* ((root-file-system-type (image->root-file-system image))
  613. (base-os (image-operating-system image))
  614. (file-systems-to-keep
  615. (srfi-1:remove
  616. (lambda (fs)
  617. (let ((mount-point (file-system-mount-point fs)))
  618. (or (string=? mount-point "/")
  619. (string=? mount-point "/boot/efi"))))
  620. (operating-system-file-systems base-os)))
  621. (format (image-format image))
  622. (os
  623. (operating-system
  624. (inherit base-os)
  625. (initrd (lambda (file-systems . rest)
  626. (apply (operating-system-initrd base-os)
  627. file-systems
  628. #:volatile-root? volatile-root?
  629. rest)))
  630. (bootloader (if (eq? format 'iso9660)
  631. (bootloader-configuration
  632. (inherit
  633. (operating-system-bootloader base-os))
  634. (bootloader grub-mkrescue-bootloader))
  635. (operating-system-bootloader base-os)))
  636. (file-systems (cons (file-system
  637. (mount-point "/")
  638. (device "/dev/placeholder")
  639. (type root-file-system-type))
  640. file-systems-to-keep))))
  641. (uuid (root-uuid os)))
  642. (operating-system
  643. (inherit os)
  644. (file-systems (cons (file-system
  645. (mount-point "/")
  646. (device uuid)
  647. (type root-file-system-type))
  648. file-systems-to-keep)))))
  649. (define* (system-image image)
  650. "Return the derivation of IMAGE. It can be a raw disk-image or an ISO9660
  651. image, depending on IMAGE format."
  652. (define substitutable? (image-substitutable? image))
  653. (define platform (image-platform image))
  654. ;; The image platform definition may provide the appropriate "system"
  655. ;; architecture for the image. If we are already running on this system,
  656. ;; the image can be built natively. If we are running on a different
  657. ;; system, then we need to cross-compile, using the "target" provided by the
  658. ;; image definition.
  659. (define system (and=> platform platform-system))
  660. (define target (cond
  661. ;; No defined platform, let's use the user defined
  662. ;; system/target parameters.
  663. ((not platform)
  664. (%current-target-system))
  665. ;; The current system is the same as the platform system, no
  666. ;; need to cross-compile.
  667. ((and system
  668. (string=? system (%current-system)))
  669. #f)
  670. ;; If there is a user defined target let's override the
  671. ;; platform target. Otherwise, we can cross-compile to the
  672. ;; platform target.
  673. (else
  674. (or (%current-target-system)
  675. (and=> platform platform-target)))))
  676. (with-parameters ((%current-target-system target))
  677. (let* ((os (operating-system-for-image image))
  678. (image* (image-with-os* image os))
  679. (image-format (image-format image))
  680. (register-closures? (has-guix-service-type? os))
  681. (bootcfg (operating-system-bootcfg os))
  682. (bootloader (bootloader-configuration-bootloader
  683. (operating-system-bootloader os))))
  684. (cond
  685. ((memq image-format '(disk-image compressed-qcow2))
  686. (system-disk-image image*
  687. #:bootcfg bootcfg
  688. #:bootloader bootloader
  689. #:register-closures? register-closures?
  690. #:inputs `(("system" ,os)
  691. ("bootcfg" ,bootcfg))))
  692. ((memq image-format '(docker))
  693. (system-docker-image image*))
  694. ((memq image-format '(iso9660))
  695. (system-iso9660-image
  696. image*
  697. #:bootcfg bootcfg
  698. #:bootloader bootloader
  699. #:register-closures? register-closures?
  700. #:inputs `(("system" ,os)
  701. ("bootcfg" ,bootcfg))
  702. ;; Make sure to use a mode that does no imply
  703. ;; HFS+ tree creation that may fail with:
  704. ;;
  705. ;; "libisofs: FAILURE : Too much files to mangle,
  706. ;; cannot guarantee unique file names"
  707. ;;
  708. ;; This happens if some limits are exceeded, see:
  709. ;; https://lists.gnu.org/archive/html/grub-devel/2020-06/msg00048.html
  710. #:grub-mkrescue-environment
  711. '(("MKRESCUE_SED_MODE" . "mbr_only"))))))))
  712. ;;
  713. ;; Image detection.
  714. ;;
  715. (define (image-modules)
  716. "Return the list of image modules."
  717. (cons (resolve-interface '(gnu system image))
  718. (all-modules (map (lambda (entry)
  719. `(,entry . "gnu/system/images/"))
  720. %load-path)
  721. #:warn warn-about-load-error)))
  722. (define %image-types
  723. ;; The list of publically-known image types.
  724. (delay (fold-module-public-variables (lambda (obj result)
  725. (if (image-type? obj)
  726. (cons obj result)
  727. result))
  728. '()
  729. (image-modules))))
  730. (define (lookup-image-type-by-name name)
  731. "Return the image type called NAME."
  732. (or (srfi-1:find (lambda (image-type)
  733. (eq? name (image-type-name image-type)))
  734. (force %image-types))
  735. (raise
  736. (formatted-message (G_ "~a: no such image type") name))))
  737. ;;; image.scm ends here