dbeb0d34364ae28954d30db5660f3e6395b0c344
[jackhill/guix/guix.git] / gnu / system / mapped-devices.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
3 ;;; Copyright © 2016 Andreas Enge <andreas@enge.fr>
4 ;;; Copyright © 2017 Mark H Weaver <mhw@netris.org>
5 ;;;
6 ;;; This file is part of GNU Guix.
7 ;;;
8 ;;; GNU Guix is free software; you can redistribute it and/or modify it
9 ;;; under the terms of the GNU General Public License as published by
10 ;;; the Free Software Foundation; either version 3 of the License, or (at
11 ;;; your option) any later version.
12 ;;;
13 ;;; GNU Guix is distributed in the hope that it will be useful, but
14 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;;; GNU General Public License for more details.
17 ;;;
18 ;;; You should have received a copy of the GNU General Public License
19 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20
21 (define-module (gnu system mapped-devices)
22 #:use-module (guix gexp)
23 #:use-module (guix records)
24 #:use-module (guix modules)
25 #:use-module (guix i18n)
26 #:use-module ((guix utils)
27 #:select (source-properties->location
28 &error-location))
29 #:use-module (gnu services)
30 #:use-module (gnu services shepherd)
31 #:use-module (gnu system uuid)
32 #:autoload (gnu build file-systems) (find-partition-by-luks-uuid)
33 #:autoload (gnu packages cryptsetup) (cryptsetup-static)
34 #:autoload (gnu packages linux) (mdadm-static)
35 #:use-module (srfi srfi-1)
36 #:use-module (srfi srfi-34)
37 #:use-module (srfi srfi-35)
38 #:use-module (ice-9 match)
39 #:export (mapped-device
40 mapped-device?
41 mapped-device-source
42 mapped-device-target
43 mapped-device-type
44 mapped-device-location
45
46 mapped-device-kind
47 mapped-device-kind?
48 mapped-device-kind-open
49 mapped-device-kind-close
50 mapped-device-kind-check
51
52 device-mapping-service-type
53 device-mapping-service
54
55 luks-device-mapping
56 raid-device-mapping))
57
58 ;;; Commentary:
59 ;;;
60 ;;; This module supports "device mapping", a concept implemented by Linux's
61 ;;; device-mapper.
62 ;;;
63 ;;; Code:
64
65 (define-record-type* <mapped-device> mapped-device
66 make-mapped-device
67 mapped-device?
68 (source mapped-device-source) ;string | list of strings
69 (target mapped-device-target) ;string
70 (type mapped-device-type) ;<mapped-device-kind>
71 (location mapped-device-location
72 (default (current-source-location)) (innate)))
73
74 (define-record-type* <mapped-device-type> mapped-device-kind
75 make-mapped-device-kind
76 mapped-device-kind?
77 (open mapped-device-kind-open) ;source target -> gexp
78 (close mapped-device-kind-close ;source target -> gexp
79 (default (const #~(const #f))))
80 (check mapped-device-kind-check ;source -> Boolean
81 (default (const #t))))
82
83 \f
84 ;;;
85 ;;; Device mapping as a Shepherd service.
86 ;;;
87
88 (define device-mapping-service-type
89 (shepherd-service-type
90 'device-mapping
91 (match-lambda
92 (($ <mapped-device> source target
93 ($ <mapped-device-type> open close))
94 (shepherd-service
95 (provision (list (symbol-append 'device-mapping- (string->symbol target))))
96 (requirement '(udev))
97 (documentation "Map a device node using Linux's device mapper.")
98 (start #~(lambda () #$(open source target)))
99 (stop #~(lambda _ (not #$(close source target))))
100 (respawn? #f))))))
101
102 (define (device-mapping-service mapped-device)
103 "Return a service that sets up @var{mapped-device}."
104 (service device-mapping-service-type mapped-device))
105
106 \f
107 ;;;
108 ;;; Common device mappings.
109 ;;;
110
111 (define (open-luks-device source target)
112 "Return a gexp that maps SOURCE to TARGET as a LUKS device, using
113 'cryptsetup'."
114 (with-imported-modules (source-module-closure
115 '((gnu build file-systems)))
116 #~(let ((source #$(if (uuid? source)
117 (uuid-bytevector source)
118 source)))
119 ;; XXX: 'use-modules' should be at the top level.
120 (use-modules (rnrs bytevectors) ;bytevector?
121 ((gnu build file-systems)
122 #:select (find-partition-by-luks-uuid)))
123
124 ;; Use 'cryptsetup-static', not 'cryptsetup', to avoid pulling the
125 ;; whole world inside the initrd (for when we're in an initrd).
126 (zero? (system* #$(file-append cryptsetup-static "/sbin/cryptsetup")
127 "open" "--type" "luks"
128
129 ;; Note: We cannot use the "UUID=source" syntax here
130 ;; because 'cryptsetup' implements it by searching the
131 ;; udev-populated /dev/disk/by-id directory but udev may
132 ;; be unavailable at the time we run this.
133 (if (bytevector? source)
134 (or (let loop ((tries-left 10))
135 (and (positive? tries-left)
136 (or (find-partition-by-luks-uuid source)
137 ;; If the underlying partition is
138 ;; not found, try again after
139 ;; waiting a second, up to ten
140 ;; times. FIXME: This should be
141 ;; dealt with in a more robust way.
142 (begin (sleep 1)
143 (loop (- tries-left 1))))))
144 (error "LUKS partition not found" source))
145 source)
146
147 #$target)))))
148
149 (define (close-luks-device source target)
150 "Return a gexp that closes TARGET, a LUKS device."
151 #~(zero? (system* #$(file-append cryptsetup-static "/sbin/cryptsetup")
152 "close" #$target)))
153
154 (define (check-luks-device md)
155 "Ensure the source of MD is valid."
156 (let ((source (mapped-device-source md)))
157 (or (not (uuid? source))
158 (not (zero? (getuid)))
159 (find-partition-by-luks-uuid (uuid-bytevector source))
160 (raise (condition
161 (&message
162 (message (format #f (G_ "no LUKS partition with UUID '~a'")
163 (uuid->string source))))
164 (&error-location
165 (location (source-properties->location
166 (mapped-device-location md)))))))))
167
168 (define luks-device-mapping
169 ;; The type of LUKS mapped devices.
170 (mapped-device-kind
171 (open open-luks-device)
172 (close close-luks-device)
173 (check check-luks-device)))
174
175 (define (open-raid-device sources target)
176 "Return a gexp that assembles SOURCES (a list of devices) to the RAID device
177 TARGET (e.g., \"/dev/md0\"), using 'mdadm'."
178 #~(let ((sources '#$sources)
179
180 ;; XXX: We're not at the top level here. We could use a
181 ;; non-top-level 'use-modules' form but that doesn't work when the
182 ;; code is eval'd, like the Shepherd does.
183 (every (@ (srfi srfi-1) every))
184 (format (@ (ice-9 format) format)))
185 (let loop ((attempts 0))
186 (unless (every file-exists? sources)
187 (when (> attempts 20)
188 (error "RAID devices did not show up; bailing out"
189 sources))
190
191 (format #t "waiting for RAID source devices~{ ~a~}...~%"
192 sources)
193 (sleep 1)
194 (loop (+ 1 attempts))))
195
196 ;; Use 'mdadm-static' rather than 'mdadm' to avoid pulling its whole
197 ;; closure (80 MiB) in the initrd when a RAID device is needed for boot.
198 (zero? (apply system* #$(file-append mdadm-static "/sbin/mdadm")
199 "--assemble" #$target sources))))
200
201 (define (close-raid-device sources target)
202 "Return a gexp that stops the RAID device TARGET."
203 #~(zero? (system* #$(file-append mdadm-static "/sbin/mdadm")
204 "--stop" #$target)))
205
206 (define raid-device-mapping
207 ;; The type of RAID mapped devices.
208 (mapped-device-kind
209 (open open-raid-device)
210 (close close-raid-device)))
211
212 ;;; mapped-devices.scm ends here