file-systems: 'mount-file-system' now takes a <file-system> object.
[jackhill/guix/guix.git] / gnu / build / file-systems.scm
CommitLineData
e2f4b305 1;;; GNU Guix --- Functional package management for GNU
2fe4ceee 2;;; Copyright © 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
ab4e939c 3;;; Copyright © 2016, 2017 David Craven <david@craven.ch>
a5e13c3b 4;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
e2f4b305
LC
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 build file-systems)
47cef4ec 22 #:use-module (gnu system uuid)
1c65cca5 23 #:use-module (gnu system file-systems)
e2f4b305 24 #:use-module (guix build utils)
6eb43907 25 #:use-module (guix build bournish)
1c65cca5
LC
26 #:use-module ((guix build syscalls)
27 #:hide (file-system-type))
e2f4b305
LC
28 #:use-module (rnrs io ports)
29 #:use-module (rnrs bytevectors)
30 #:use-module (ice-9 match)
31 #:use-module (ice-9 rdelim)
32 #:use-module (system foreign)
33 #:autoload (system repl repl) (start-repl)
34 #:use-module (srfi srfi-1)
35 #:use-module (srfi srfi-26)
36 #:export (disk-partitions
37 partition-label-predicate
0ec5ee94 38 partition-uuid-predicate
a1ccefaa 39 partition-luks-uuid-predicate
e2f4b305 40 find-partition-by-label
0ec5ee94 41 find-partition-by-uuid
a1ccefaa 42 find-partition-by-luks-uuid
e2f4b305
LC
43 canonicalize-device-spec
44
e2f4b305
LC
45 bind-mount
46
47 mount-flags->bit-mask
48 check-file-system
a5e13c3b 49 mount-file-system))
e2f4b305
LC
50
51;;; Commentary:
52;;;
53;;; This modules provides tools to deal with disk partitions, and to mount and
54;;; check file systems.
55;;;
56;;; Code:
57
e2f4b305
LC
58(define (bind-mount source target)
59 "Bind-mount SOURCE at TARGET."
60 (mount source target "" MS_BIND))
61
2fe4ceee
LC
62(define (seek* fd/port offset whence)
63 "Like 'seek' but return -1 instead of throwing to 'system-error' upon
64EINVAL. This makes it easier to catch cases like OFFSET being too large for
65FD/PORT."
66 (catch 'system-error
67 (lambda ()
68 (seek fd/port offset whence))
69 (lambda args
70 (if (= EINVAL (system-error-errno args))
71 -1
72 (apply throw args)))))
73
974e02da
DC
74(define (read-superblock device offset size magic?)
75 "Read a superblock of SIZE from OFFSET and DEVICE. Return the raw
76superblock on success, and #f if no valid superblock was found. MAGIC?
77takes a bytevector and returns #t when it's a valid superblock."
78 (call-with-input-file device
79 (lambda (port)
2fe4ceee
LC
80 (and (= offset (seek* port offset SEEK_SET))
81 (let ((block (make-bytevector size)))
82 (match (get-bytevector-n! port block 0 (bytevector-length block))
83 ((? eof-object?)
84 #f)
85 ((? number? len)
86 (and (= len (bytevector-length block))
87 (and (magic? block)
88 block)))))))))
974e02da 89
b0377e58
DC
90(define null-terminated-latin1->string
91 (cut latin1->string <> zero?))
92
a1ccefaa
LC
93\f
94;;;
95;;; Ext2 file systems.
96;;;
97
974e02da
DC
98;; <http://www.nongnu.org/ext2-doc/ext2.html#DEF-SUPERBLOCK>.
99;; TODO: Use "packed structs" from Guile-OpenGL or similar.
100
e2f4b305
LC
101(define-syntax %ext2-endianness
102 ;; Endianness of ext2 file systems.
103 (identifier-syntax (endianness little)))
104
974e02da
DC
105(define (ext2-superblock? sblock)
106 "Return #t when SBLOCK is an ext2 superblock."
107 (let ((magic (bytevector-u16-ref sblock 56 %ext2-endianness)))
108 (= magic #xef53)))
e2f4b305
LC
109
110(define (read-ext2-superblock device)
111 "Return the raw contents of DEVICE's ext2 superblock as a bytevector, or #f
112if DEVICE does not contain an ext2 file system."
974e02da 113 (read-superblock device 1024 264 ext2-superblock?))
e2f4b305
LC
114
115(define (ext2-superblock-uuid sblock)
116 "Return the UUID of ext2 superblock SBLOCK as a 16-byte bytevector."
974e02da 117 (sub-bytevector sblock 104 16))
e2f4b305
LC
118
119(define (ext2-superblock-volume-name sblock)
120 "Return the volume name of SBLOCK as a string of at most 16 characters, or
121#f if SBLOCK has no volume name."
974e02da 122 (null-terminated-latin1->string (sub-bytevector sblock 120 16)))
e2f4b305 123
26905ec8
DC
124(define (check-ext2-file-system device)
125 "Return the health of an ext2 file system on DEVICE."
126 (match (status:exit-val
127 (system* "e2fsck" "-v" "-p" "-C" "0" device))
128 (0 'pass)
129 (1 'errors-corrected)
130 (2 'reboot-required)
131 (_ 'fatal-error)))
e2f4b305 132
a1ccefaa 133\f
b1a505ba
DC
134;;;
135;;; Btrfs file systems.
136;;;
137
138;; <https://btrfs.wiki.kernel.org/index.php/On-disk_Format#Superblock>.
139
140(define-syntax %btrfs-endianness
141 ;; Endianness of btrfs file systems.
142 (identifier-syntax (endianness little)))
143
144(define (btrfs-superblock? sblock)
145 "Return #t when SBLOCK is a btrfs superblock."
146 (bytevector=? (sub-bytevector sblock 64 8)
147 (string->utf8 "_BHRfS_M")))
148
149(define (read-btrfs-superblock device)
150 "Return the raw contents of DEVICE's btrfs superblock as a bytevector, or #f
151if DEVICE does not contain a btrfs file system."
152 (read-superblock device 65536 4096 btrfs-superblock?))
153
154(define (btrfs-superblock-uuid sblock)
155 "Return the UUID of a btrfs superblock SBLOCK as a 16-byte bytevector."
156 (sub-bytevector sblock 32 16))
157
158(define (btrfs-superblock-volume-name sblock)
159 "Return the volume name of SBLOCK as a string of at most 256 characters, or
160#f if SBLOCK has no volume name."
161 (null-terminated-latin1->string (sub-bytevector sblock 299 256)))
162
163(define (check-btrfs-file-system device)
164 "Return the health of a btrfs file system on DEVICE."
165 (match (status:exit-val
166 (system* "btrfs" "device" "scan"))
167 (0 'pass)
168 (_ 'fatal-error)))
169
170\f
b0377e58
DC
171;;;
172;;; FAT32 file systems.
173;;;
174
175;; <http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-107.pdf>.
176
b0377e58
DC
177(define (fat32-superblock? sblock)
178 "Return #t when SBLOCK is a fat32 superblock."
179 (bytevector=? (sub-bytevector sblock 82 8)
180 (string->utf8 "FAT32 ")))
181
182(define (read-fat32-superblock device)
183 "Return the raw contents of DEVICE's fat32 superblock as a bytevector, or
184#f if DEVICE does not contain a fat32 file system."
185 (read-superblock device 0 90 fat32-superblock?))
186
187(define (fat32-superblock-uuid sblock)
188 "Return the Volume ID of a fat superblock SBLOCK as a 4-byte bytevector."
189 (sub-bytevector sblock 67 4))
190
b0377e58
DC
191(define (fat32-superblock-volume-name sblock)
192 "Return the volume name of SBLOCK as a string of at most 11 characters, or
193#f if SBLOCK has no volume name. The volume name is a latin1 string.
194Trailing spaces are trimmed."
195 (string-trim-right (latin1->string (sub-bytevector sblock 71 11) (lambda (c) #f)) #\space))
196
197(define (check-fat32-file-system device)
198 "Return the health of a fat file system on DEVICE."
199 (match (status:exit-val
200 (system* "fsck.vfat" "-v" "-a" device))
201 (0 'pass)
202 (1 'errors-corrected)
203 (_ 'fatal-error)))
204
205\f
06110559
DM
206;;;
207;;; ISO9660 file systems.
208;;;
209
210;; <http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf>.
211
212(define (iso9660-superblock? sblock)
3dba9b37 213 "Return #t when SBLOCK is an iso9660 volume descriptor."
06110559
DM
214 (bytevector=? (sub-bytevector sblock 1 6)
215 ;; Note: "\x01" is the volume descriptor format version
216 (string->utf8 "CD001\x01")))
217
218(define (read-iso9660-primary-volume-descriptor device offset)
219 "Find and read the first primary volume descriptor, starting at OFFSET.
220 Return #f if not found."
221 (let* ((sblock (read-superblock device offset 2048 iso9660-superblock?))
203a9455
DM
222 (type-code (if sblock
223 (bytevector-u8-ref sblock 0)
224 (error (format #f
225 "Could not read ISO9660 primary
226volume descriptor from ~s"
227 device)))))
06110559
DM
228 (match type-code
229 (255 #f) ; Volume Descriptor Set Terminator.
230 (1 sblock) ; Primary Volume Descriptor
231 (_ (read-iso9660-primary-volume-descriptor device (+ offset 2048))))))
232
233(define (read-iso9660-superblock device)
3dba9b37
DM
234 "Return the raw contents of DEVICE's iso9660 primary volume descriptor
235as a bytevector, or #f if DEVICE does not contain an iso9660 file system."
06110559 236 ;; Start reading at sector 16.
fb03f44b
DM
237 ;; Since we are not sure that the device contains an ISO9660 filesystem,
238 ;; we have to find that out first.
239 (if (read-superblock device (* 2048 16) 2048 iso9660-superblock?)
240 (read-iso9660-primary-volume-descriptor device (* 2048 16))
241 #f)) ; Device does not contain an iso9660 filesystem.
06110559
DM
242
243(define (iso9660-superblock-uuid sblock)
3dba9b37 244 "Return the modification time of an iso9660 primary volume descriptor
c6aee77e 245SBLOCK as a bytevector. If that's not set, returns the creation time."
06110559
DM
246 ;; Drops GMT offset for compatibility with Grub, blkid and /dev/disk/by-uuid.
247 ;; Compare Grub: "2014-12-02-19-30-23-00".
248 ;; Compare blkid result: "2014-12-02-19-30-23-00".
249 ;; Compare /dev/disk/by-uuid entry: "2014-12-02-19-30-23-00".
c6aee77e
DM
250 (let* ((creation-time (sub-bytevector sblock 813 17))
251 (modification-time (sub-bytevector sblock 830 17))
252 (unset-time (make-bytevector 17 0))
253 (time (if (bytevector=? unset-time modification-time)
254 creation-time
255 modification-time)))
256 (sub-bytevector time 0 16))) ; strips GMT offset.
06110559 257
06110559
DM
258(define (iso9660-superblock-volume-name sblock)
259 "Return the volume name of SBLOCK as a string. The volume name is an ASCII
260string. Trailing spaces are trimmed."
cdc701ea 261 ;; Note: Valid characters are of the set "[0-9][A-Z]_" (ECMA-119 Appendix A)
06110559
DM
262 (string-trim-right (latin1->string (sub-bytevector sblock 40 32)
263 (lambda (c) #f)) #\space))
264
265\f
a1ccefaa
LC
266;;;
267;;; LUKS encrypted devices.
268;;;
269
270;; The LUKS header format is described in "LUKS On-Disk Format Specification":
de975de3 271;; <https://gitlab.com/cryptsetup/cryptsetup/wikis/Specification>. We follow
a1ccefaa
LC
272;; version 1.2.1 of this document.
273
274(define-syntax %luks-endianness
275 ;; Endianness of LUKS headers.
276 (identifier-syntax (endianness big)))
277
974e02da
DC
278(define (luks-superblock? sblock)
279 "Return #t when SBLOCK is a luks superblock."
280 (define %luks-magic
281 ;; The 'LUKS_MAGIC' constant.
282 (u8-list->bytevector (append (map char->integer (string->list "LUKS"))
283 (list #xba #xbe))))
284 (let ((magic (sub-bytevector sblock 0 6))
285 (version (bytevector-u16-ref sblock 6 %luks-endianness)))
286 (and (bytevector=? magic %luks-magic)
287 (= version 1))))
a1ccefaa
LC
288
289(define (read-luks-header file)
290 "Read a LUKS header from FILE. Return the raw header on success, and #f if
291not valid header was found."
974e02da
DC
292 ;; Size in bytes of the LUKS header, including key slots.
293 (read-superblock file 0 592 luks-superblock?))
a1ccefaa
LC
294
295(define (luks-header-uuid header)
296 "Return the LUKS UUID from HEADER, as a 16-byte bytevector."
297 ;; 40 bytes are reserved for the UUID, but in practice, it contains the 36
298 ;; bytes of its ASCII representation.
299 (let ((uuid (sub-bytevector header 168 36)))
300 (string->uuid (utf8->string uuid))))
301
302\f
303;;;
304;;; Partition lookup.
305;;;
306
e2f4b305
LC
307(define (disk-partitions)
308 "Return the list of device names corresponding to valid disk partitions."
49baaff4 309 (define (partition? name major minor)
9833bcfc
DM
310 ;; grub-mkrescue does some funny things for EFI support which
311 ;; makes it a lot more difficult than one would expect to support
312 ;; booting an ISO-9660 image from an USB flash drive.
313 ;; For example there's a buggy (too small) hidden partition in it
314 ;; which Linux mounts and then proceeds to fail while trying to
315 ;; fall off the edge.
316 ;; In any case, partition tables are supposed to be optional so
317 ;; here we allow checking entire disks for file systems, too.
318 (> major 2)) ;ignore RAM disks and floppy disks
e2f4b305
LC
319
320 (call-with-input-file "/proc/partitions"
321 (lambda (port)
322 ;; Skip the two header lines.
323 (read-line port)
324 (read-line port)
325
326 ;; Read each subsequent line, and extract the last space-separated
327 ;; field.
328 (let loop ((parts '()))
329 (let ((line (read-line port)))
330 (if (eof-object? line)
331 (reverse parts)
332 (match (string-tokenize line)
333 (((= string->number major) (= string->number minor)
334 blocks name)
49baaff4 335 (if (partition? name major minor)
e2f4b305
LC
336 (loop (cons name parts))
337 (loop parts))))))))))
338
24473356
LC
339(define (ENOENT-safe proc)
340 "Wrap the one-argument PROC such that ENOENT errors are caught and lead to a
341warning and #f as the result."
342 (lambda (device)
343 (catch 'system-error
344 (lambda ()
345 (proc device))
346 (lambda args
347 ;; When running on the hand-made /dev,
348 ;; 'disk-partitions' could return partitions for which
349 ;; we have no /dev node. Handle that gracefully.
49baaff4
LC
350 (let ((errno (system-error-errno args)))
351 (cond ((= ENOENT errno)
352 (format (current-error-port)
353 "warning: device '~a' not found~%" device)
354 #f)
355 ((= ENOMEDIUM errno) ;for removable media
356 #f)
b53510e0
AVY
357 ((= EIO errno) ;unreadable hardware like audio CDs
358 (format (current-error-port)
359 "warning: failed to read from device '~a'~%" device)
360 #f)
49baaff4
LC
361 (else
362 (apply throw args))))))))
24473356 363
ab4e939c
DC
364(define (partition-field-reader read field)
365 "Return a procedure that takes a device and returns the value of a FIELD in
366the partition superblock or #f."
367 (let ((read (ENOENT-safe read)))
368 (lambda (device)
369 (let ((sblock (read device)))
370 (and sblock
371 (field sblock))))))
372
373(define (read-partition-field device partition-field-readers)
374 "Returns the value of a FIELD in the partition superblock of DEVICE or #f. It
375takes a list of PARTITION-FIELD-READERS and returns the result of the first
376partition field reader that returned a value."
377 (match (filter-map (cut apply <> (list device)) partition-field-readers)
378 ((field . _) field)
379 (_ #f)))
380
381(define %partition-label-readers
06110559
DM
382 (list (partition-field-reader read-iso9660-superblock
383 iso9660-superblock-volume-name)
384 (partition-field-reader read-ext2-superblock
b1a505ba
DC
385 ext2-superblock-volume-name)
386 (partition-field-reader read-btrfs-superblock
b0377e58
DC
387 btrfs-superblock-volume-name)
388 (partition-field-reader read-fat32-superblock
389 fat32-superblock-volume-name)))
ab4e939c
DC
390
391(define %partition-uuid-readers
06110559
DM
392 (list (partition-field-reader read-iso9660-superblock
393 iso9660-superblock-uuid)
394 (partition-field-reader read-ext2-superblock
b1a505ba
DC
395 ext2-superblock-uuid)
396 (partition-field-reader read-btrfs-superblock
b0377e58
DC
397 btrfs-superblock-uuid)
398 (partition-field-reader read-fat32-superblock
399 fat32-superblock-uuid)))
ab4e939c
DC
400
401(define read-partition-label
402 (cut read-partition-field <> %partition-label-readers))
403
404(define read-partition-uuid
405 (cut read-partition-field <> %partition-uuid-readers))
406
407(define (partition-predicate reader =)
a1ccefaa
LC
408 "Return a predicate that returns true if the FIELD of partition header that
409was READ is = to the given value."
ab4e939c
DC
410 (lambda (expected)
411 (lambda (device)
412 (let ((actual (reader device)))
413 (and actual
414 (= actual expected))))))
0ec5ee94
LC
415
416(define partition-label-predicate
ab4e939c 417 (partition-predicate read-partition-label string=?))
0ec5ee94
LC
418
419(define partition-uuid-predicate
aed1f1b0 420 (partition-predicate read-partition-uuid uuid=?))
a1ccefaa 421
974e02da 422(define luks-partition-uuid-predicate
ab4e939c
DC
423 (partition-predicate
424 (partition-field-reader read-luks-header luks-header-uuid)
aed1f1b0 425 uuid=?))
e2f4b305 426
ab4e939c
DC
427(define (find-partition predicate)
428 "Return the first partition found that matches PREDICATE, or #f if none
e2f4b305 429were found."
ab4e939c
DC
430 (lambda (expected)
431 (find (predicate expected)
432 (map (cut string-append "/dev/" <>)
433 (disk-partitions)))))
434
435(define find-partition-by-label
436 (find-partition partition-label-predicate))
437
438(define find-partition-by-uuid
439 (find-partition partition-uuid-predicate))
440
441(define find-partition-by-luks-uuid
442 (find-partition luks-partition-uuid-predicate))
a1ccefaa 443
f8865db6 444\f
e2f4b305
LC
445(define* (canonicalize-device-spec spec #:optional (title 'any))
446 "Return the device name corresponding to SPEC. TITLE is a symbol, one of
447the following:
448
449 • 'device', in which case SPEC is known to designate a device node--e.g.,
450 \"/dev/sda1\";
451 • 'label', in which case SPEC is known to designate a partition label--e.g.,
452 \"my-root-part\";
0ec5ee94
LC
453 • 'uuid', in which case SPEC must be a UUID (a 16-byte bytevector)
454 designating a partition;
e2f4b305
LC
455 • 'any', in which case SPEC can be anything.
456"
457 (define max-trials
458 ;; Number of times we retry partition label resolution, 1 second per
459 ;; trial. Note: somebody reported a delay of 16 seconds (!) before their
460 ;; USB key would be detected by the kernel, so we must wait for at least
461 ;; this long.
462 20)
463
464 (define canonical-title
465 ;; The realm of canonicalization.
466 (if (eq? title 'any)
0ec5ee94 467 (if (string? spec)
f453f637
LC
468 ;; The "--root=SPEC" kernel command-line option always provides a
469 ;; string, but the string can represent a device, a UUID, or a
470 ;; label. So check for all three.
471 (cond ((string-prefix? "/" spec) 'device)
472 ((string->uuid spec) 'uuid)
473 (else 'label))
0ec5ee94 474 'uuid)
e2f4b305
LC
475 title))
476
0ec5ee94
LC
477 (define (resolve find-partition spec fmt)
478 (let loop ((count 0))
479 (let ((device (find-partition spec)))
480 (or device
481 ;; Some devices take a bit of time to appear, most notably USB
482 ;; storage devices. Thus, wait for the device to appear.
483 (if (> count max-trials)
484 (error "failed to resolve partition" (fmt spec))
485 (begin
486 (format #t "waiting for partition '~a' to appear...~%"
487 (fmt spec))
488 (sleep 1)
489 (loop (+ 1 count))))))))
490
e2f4b305
LC
491 (case canonical-title
492 ((device)
493 ;; Nothing to do.
494 spec)
495 ((label)
496 ;; Resolve the label.
0ec5ee94
LC
497 (resolve find-partition-by-label spec identity))
498 ((uuid)
f453f637
LC
499 (resolve find-partition-by-uuid
500 (if (string? spec)
501 (string->uuid spec)
502 spec)
503 uuid->string))
e2f4b305
LC
504 (else
505 (error "unknown device title" title))))
506
507(define (check-file-system device type)
508 "Run a file system check of TYPE on DEVICE."
26905ec8
DC
509 (define check-procedure
510 (cond
511 ((string-prefix? "ext" type) check-ext2-file-system)
b1a505ba 512 ((string-prefix? "btrfs" type) check-btrfs-file-system)
b0377e58 513 ((string-suffix? "fat" type) check-fat32-file-system)
26905ec8
DC
514 (else #f)))
515
516 (if check-procedure
517 (match (check-procedure device)
518 ('pass
519 #t)
520 ('errors-corrected
521 (format (current-error-port)
522 "File system check corrected errors on ~a; continuing~%"
523 device))
524 ('reboot-required
525 (format (current-error-port)
526 "File system check corrected errors on ~a; rebooting~%"
527 device)
528 (sleep 3)
529 (reboot))
530 ('fatal-error
531 (format (current-error-port)
532 "File system check on ~a failed; spawning Bourne-like REPL~%"
533 device)
534 (start-repl %bournish-language)))
535 (format (current-error-port)
536 "No file system check procedure for ~a; skipping~%"
537 device)))
e2f4b305
LC
538
539(define (mount-flags->bit-mask flags)
540 "Return the number suitable for the 'flags' argument of 'mount' that
541corresponds to the symbols listed in FLAGS."
542 (let loop ((flags flags))
543 (match flags
544 (('read-only rest ...)
545 (logior MS_RDONLY (loop rest)))
546 (('bind-mount rest ...)
547 (logior MS_BIND (loop rest)))
548 (('no-suid rest ...)
549 (logior MS_NOSUID (loop rest)))
550 (('no-dev rest ...)
551 (logior MS_NODEV (loop rest)))
552 (('no-exec rest ...)
553 (logior MS_NOEXEC (loop rest)))
554 (()
555 0))))
556
1c65cca5
LC
557(define* (mount-file-system fs #:key (root "/root"))
558 "Mount the file system described by FS, a <file-system> object, under ROOT.
e2f4b305
LC
559
560DEVICE, MOUNT-POINT, and TYPE must be strings; OPTIONS can be a string or #f;
561FLAGS must be a list of symbols. CHECK? is a Boolean indicating whether to
562run a file system check."
0c85db79
JD
563
564 (define (mount-nfs source mount-point type flags options)
565 (let* ((idx (string-rindex source #\:))
566 (host-part (string-take source idx))
567 ;; Strip [] from around host if present
568 (host (match (string-split host-part (string->char-set "[]"))
569 (("" h "") h)
570 ((h) h)))
571 (aa (match (getaddrinfo host "nfs") ((x . _) x)))
572 (sa (addrinfo:addr aa))
573 (inet-addr (inet-ntop (sockaddr:fam sa)
574 (sockaddr:addr sa))))
575
576 ;; Mounting an NFS file system requires passing the address
577 ;; of the server in the addr= option
578 (mount source mount-point type flags
579 (string-append "addr="
580 inet-addr
581 (if options
582 (string-append "," options)
583 "")))))
1c65cca5
LC
584 (let ((type (file-system-type fs))
585 (options (file-system-options fs))
586 (source (canonicalize-device-spec (file-system-device fs)
587 (file-system-title fs)))
588 (mount-point (string-append root "/"
589 (file-system-mount-point fs)))
590 (flags (mount-flags->bit-mask (file-system-flags fs))))
591 (when (file-system-check? fs)
592 (check-file-system source type))
593
594 ;; Create the mount point. Most of the time this is a directory, but
595 ;; in the case of a bind mount, a regular file or socket may be needed.
596 (if (and (= MS_BIND (logand flags MS_BIND))
597 (not (file-is-directory? source)))
598 (unless (file-exists? mount-point)
599 (mkdir-p (dirname mount-point))
600 (call-with-output-file mount-point (const #t)))
601 (mkdir-p mount-point))
602
603 (cond
604 ((string-prefix? "nfs" type)
605 (mount-nfs source mount-point type flags options))
606 (else
607 (mount source mount-point type flags options)))
608
609 ;; For read-only bind mounts, an extra remount is needed, as per
610 ;; <http://lwn.net/Articles/281157/>, which still applies to Linux 4.0.
611 (when (and (= MS_BIND (logand flags MS_BIND))
612 (= MS_RDONLY (logand flags MS_RDONLY)))
613 (let ((flags (logior MS_BIND MS_REMOUNT MS_RDONLY)))
614 (mount source mount-point type flags #f)))))
e2f4b305
LC
615
616;;; file-systems.scm ends here