From 602994847b748937b6fa39a7b819429857cdd8d3 Mon Sep 17 00:00:00 2001 From: Tobias Geerinckx-Rice Date: Sat, 15 May 2021 15:29:40 +0200 Subject: file-systems: Support forced checks & repairs. * gnu/build/file-systems.scm (check-ext2-file-system) (check-bcachefs-file-system, check-btrfs-file-system) (check-fat-file-system, check-jfs-file-system, check-f2fs-file-system) (check-ntfs-file-system, check-file-system): Take and honour new FORCE? and REPAIR arguments. Update the docstring. Adjust all callers. * gnu/system/file-systems.scm : Add new SKIP-CHECK-IF-CLEAN? and REPAIR fields. (file-system->spec, spec->file-system): Adjust accordingly. * gnu/build/linux-boot.scm (mount-root-file-system): Take new SKIP-CHECK-IF-CLEAN? and REPAIR keyword arguments. Thread them through to CHECK-FILE-SYSTEM. * doc/guix.texi (File Systems): Document both new options. --- gnu/build/file-systems.scm | 209 ++++++++++++++++++++++++++++++++------------- gnu/build/linux-boot.scm | 19 ++++- 2 files changed, 164 insertions(+), 64 deletions(-) (limited to 'gnu/build') diff --git a/gnu/build/file-systems.scm b/gnu/build/file-systems.scm index 4eeb81cf26..a54127e888 100644 --- a/gnu/build/file-systems.scm +++ b/gnu/build/file-systems.scm @@ -170,10 +170,19 @@ (define (ext2-superblock-volume-name sblock) #f if SBLOCK has no volume name." (null-terminated-latin1->string (sub-bytevector sblock 120 16))) -(define (check-ext2-file-system device) - "Return the health of an ext2 file system on DEVICE." +(define (check-ext2-file-system device force? repair) + "Return the health of an unmounted ext2 file system on DEVICE. If FORCE? is +true, check the file system even if it's marked as clean. If REPAIR is false, +do not write to the file system to fix errors. If it's #t, fix all +errors. Otherwise, fix only those considered safe to repair automatically." (match (status:exit-val - (system* "e2fsck" "-v" "-p" "-C" "0" device)) + (apply system* `("e2fsck" "-v" "-C" "0" + ,@(if force? '("-f") '()) + ,@(match repair + (#f '("-n")) + (#t '("-y")) + (_ '("-p"))) + ,device))) (0 'pass) (1 'errors-corrected) (2 'reboot-required) @@ -260,15 +269,23 @@ (define (bcachefs-superblock-volume-name sblock) #f if SBLOCK has no volume name." (null-terminated-latin1->string (sub-bytevector sblock 72 32))) -(define (check-bcachefs-file-system device) - "Return the health of a bcachefs file system on DEVICE." +(define (check-bcachefs-file-system device force? repair) + "Return the health of an unmounted bcachefs file system on DEVICE. If FORCE? +is true, check the file system even if it's marked as clean. If REPAIR is +false, do not write to the file system to fix errors. If it's #t, fix all +errors. Otherwise, fix only those considered safe to repair automatically." (let ((ignored-bits (logior 2)) ; DEVICE was mounted read-only (status ;; A number, or #f on abnormal termination (e.g., assertion failure). (status:exit-val - (apply system* "bcachefs" "fsck" "-p" "-v" - ;; Make each multi-device member a separate argument. - (string-split device #\:))))) + (apply system* `("bcachefs" "fsck" "-v" + ,@(if force? '("-f") '()) + ,@(match repair + (#f '("-n")) + (#t '("-y")) + (_ '("-p"))) + ;; Make each multi-device member a separate argument. + ,@(string-split device #\:)))))) (match (and=> status (cut logand <> (lognot ignored-bits))) (0 'pass) (1 'errors-corrected) @@ -304,12 +321,28 @@ (define (btrfs-superblock-volume-name sblock) #f if SBLOCK has no volume name." (null-terminated-latin1->string (sub-bytevector sblock 299 256))) -(define (check-btrfs-file-system device) - "Return the health of a btrfs file system on DEVICE." - (match (status:exit-val - (system* "btrfs" "device" "scan")) - (0 'pass) - (_ 'fatal-error))) +(define (check-btrfs-file-system device force? repair) + "Return the health of an unmounted btrfs file system on DEVICE. If FORCE? is +false, return 'PASS unconditionally as btrfs claims no need for off-line checks. +When FORCE? is true, do perform a real check. This is not recommended! See +@uref{https://bugzilla.redhat.com/show_bug.cgi?id=625967#c8}. If REPAIR is +false, do not write to DEVICE. If it's #t, fix any errors found. Otherwise, +fix only those considered safe to repair automatically." + ;; XXX Why make this conditional on (check? #t) at all? + (system* "btrfs" "device" "scan") ; ignore errors + (if force? + (match (status:exit-val + (apply system* `("btrfs" "check" "--progress" + ;; Btrfs's ‘--force’ is not relevant to us here. + ,@(match repair + ;; Upstream considers ALL repairs dangerous + ;; and will warn the user at run time. + (#t '("--repair")) + (_ '("--readonly"))) ; a no-op for clarity + ,device))) + (0 'pass) + (_ 'fatal-error)) + 'pass)) ;;; @@ -338,10 +371,17 @@ (define (fat32-superblock-volume-name sblock) Trailing spaces are trimmed." (string-trim-right (latin1->string (sub-bytevector sblock 71 11) (lambda (c) #f)) #\space)) -(define (check-fat-file-system device) - "Return the health of a fat file system on DEVICE." +(define (check-fat-file-system device force? repair) + "Return the health of an unmounted FAT file system on DEVICE. FORCE? is +ignored: a full file system scan is always performed. If REPAIR is false, do +not write to the file system to fix errors. Otherwise, automatically fix them +using the least destructive approach." (match (status:exit-val - (system* "fsck.vfat" "-v" "-a" device)) + (apply system* `("fsck.vfat" "-v" + ,@(match repair + (#f '("-n")) + (_ '("-a"))) ; no 'safe/#t distinction + ,device))) (0 'pass) (1 'errors-corrected) (_ 'fatal-error))) @@ -463,10 +503,28 @@ (define (jfs-superblock-volume-name sblock) #f if SBLOCK has no volume name." (null-terminated-latin1->string (sub-bytevector sblock 152 16))) -(define (check-jfs-file-system device) - "Return the health of a JFS file system on DEVICE." +(define (check-jfs-file-system device force? repair) + "Return the health of an unmounted JFS file system on DEVICE. If FORCE? is +true, check the file system even if it's marked as clean. If REPAIR is false, +do not write to the file system to fix errors, and replay the transaction log +only if FORCE? is true. Otherwise, replay the transaction log before checking +and automatically fix found errors." (match (status:exit-val - (system* "jfs_fsck" "-p" "-v" device)) + (apply system* + `("jfs_fsck" "-v" + ;; The ‘LEVEL’ logic is convoluted. To quote fsck/xchkdsk.c + ;; (‘-p’, ‘-a’, and ‘-r’ are aliases in every way): + ;; “If -f was chosen, have it override [-p] by [forcing] a + ;; check regardless of the outcome after the log is + ;; replayed”. + ;; “If -n is specified by itself, don't replay the journal. + ;; If -n is specified with [-p], replay the journal but + ;; don't make any other changes”. + ,@(if force? '("-f") '()) + ,@(match repair + (#f '("-n")) + (_ '("-p"))) ; no 'safe/#t distinction + ,device))) (0 'pass) (1 'errors-corrected) (2 'reboot-required) @@ -517,12 +575,22 @@ (define (f2fs-superblock-volume-name sblock) (sub-bytevector sblock (- (+ #x470 12) #x400) 512) %f2fs-endianness)) -(define (check-f2fs-file-system device) - "Return the health of a F2FS file system on DEVICE." +(define (check-f2fs-file-system device force? repair) + "Return the health of an unmuounted F2FS file system on DEVICE. If FORCE? is +true, check the file system even if it's marked as clean. If either FORCE? or +REPAIR are true, automatically fix found errors." + ;; There's no ‘-n’ equivalent (‘--dry-run’ does not disable writes). + ;; ’-y’ is an alias of ‘-f’. The man page is bad: read main.c. + (when (and force? (not repair)) + (format (current-error-port) + "warning: forced check of F2FS ~a implies repairing any errors~%" + device)) (match (status:exit-val - (system* "fsck.f2fs" "-p" device)) - ;; 0 and -1 are the only two possibilities - ;; (according to the manpage) + (apply system* `("fsck.f2fs" + ,@(if force? '("-f") '()) + ,@(if repair '("-p") '("--dry-run")) + ,device))) + ;; 0 and -1 are the only two possibilities according to the man page. (0 'pass) (_ 'fatal-error))) @@ -600,10 +668,15 @@ (define (ntfs-superblock-uuid sblock) ;; in the BOOT SECTOR like the UUID, but in the MASTER FILE TABLE, which seems ;; way harder to access. -(define (check-ntfs-file-system device) - "Return the health of a NTFS file system on DEVICE." +(define (check-ntfs-file-system device force? repair) + "Return the health of an unmounted NTFS file system on DEVICE. FORCE? is +ignored: a full check is always performed. Repair is not possible: if REPAIR is +true and the volume has been repaired by an external tool, clear the volume +dirty flag to indicate that it's now safe to mount." (match (status:exit-val - (system* "ntfsfix" device)) + (apply system* `("ntfsfix" + ,@(if repair '("--clear-dirty") '("--no-action")) + ,device))) (0 'pass) (_ 'fatal-error))) @@ -816,8 +889,13 @@ (define (resolve find-partition spec fmt) (uuid-bytevector spec) uuid->string)))) -(define (check-file-system device type) - "Run a file system check of TYPE on DEVICE." +(define (check-file-system device type force? repair) + "Check an unmounted TYPE file system on DEVICE. Do nothing but warn if it is +mounted. If FORCE? is true, check even when considered unnecessary. If REPAIR +is false, try not to write to DEVICE at all. If it's #t, try to fix all errors +found. Otherwise, fix only those considered safe to repair automatically. Not +all TYPEs support all values or combinations of FORCE? and REPAIR. Don't throw +an exception in such cases but perform the nearest sane action." (define check-procedure (cond ((string-prefix? "ext" type) check-ext2-file-system) @@ -831,33 +909,40 @@ (define check-procedure (else #f))) (if check-procedure - (match (check-procedure device) - ('pass - #t) - ('errors-corrected - (format (current-error-port) - "File system check corrected errors on ~a; continuing~%" - device)) - ('reboot-required - (format (current-error-port) - "File system check corrected errors on ~a; rebooting~%" - device) - (sleep 3) - (reboot)) - ('fatal-error - (format (current-error-port) "File system check on ~a failed~%" - device) - - ;; Spawn a REPL only if someone would be able to interact with it. - (when (isatty? (current-input-port)) - (format (current-error-port) "Spawning Bourne-like REPL.~%") - - ;; 'current-output-port' is typically connected to /dev/klog (in - ;; PID 1), but here we want to make sure we talk directly to the - ;; user. - (with-output-to-file "/dev/console" - (lambda () - (start-repl %bournish-language)))))) + (let ((mount (find (lambda (mount) + (string=? device (mount-source mount))) + (mounts)))) + (if mount + (format (current-error-port) + "Refusing to check ~a file system already mounted at ~a~%" + device (mount-point mount)) + (match (check-procedure device force? repair) + ('pass + #t) + ('errors-corrected + (format (current-error-port) + "File system check corrected errors on ~a; continuing~%" + device)) + ('reboot-required + (format (current-error-port) + "File system check corrected errors on ~a; rebooting~%" + device) + (sleep 3) + (reboot)) + ('fatal-error + (format (current-error-port) "File system check on ~a failed~%" + device) + + ;; Spawn a REPL only if someone might interact with it. + (when (isatty? (current-input-port)) + (format (current-error-port) "Spawning Bourne-like REPL.~%") + + ;; 'current-output-port' is typically connected to /dev/klog + ;; (in PID 1), but here we want to make sure we talk directly + ;; to the user. + (with-output-to-file "/dev/console" + (lambda () + (start-repl %bournish-language)))))))) (format (current-error-port) "No file system check procedure for ~a; skipping~%" device))) @@ -886,7 +971,11 @@ (define (mount-flags->bit-mask flags) (() 0)))) -(define* (mount-file-system fs #:key (root "/root")) +(define* (mount-file-system fs #:key (root "/root") + (check? (file-system-check? fs)) + (skip-check-if-clean? + (file-system-skip-check-if-clean? fs)) + (repair (file-system-repair fs))) "Mount the file system described by FS, a object, under ROOT." (define (mount-nfs source mount-point type flags options) @@ -924,8 +1013,8 @@ (define (mount-nfs source mount-point type flags options) (file-system-mount-flags (statfs source))) 0))) (options (file-system-options fs))) - (when (file-system-check? fs) - (check-file-system source type)) + (when check? + (check-file-system source type (not skip-check-if-clean?) repair)) (catch 'system-error (lambda () diff --git a/gnu/build/linux-boot.scm b/gnu/build/linux-boot.scm index 95d0a1fe79..ab05d1ba5e 100644 --- a/gnu/build/linux-boot.scm +++ b/gnu/build/linux-boot.scm @@ -408,12 +408,17 @@ (define (pidof program) (define* (mount-root-file-system root type #:key volatile-root? (flags 0) options - check?) + check? skip-check-if-clean? repair) "Mount the root file system of type TYPE at device ROOT. If VOLATILE-ROOT? is true, mount ROOT read-only and make it an overlay with a writable tmpfs using the kernel built-in overlayfs. FLAGS and OPTIONS indicates the options to use to mount ROOT, and behave the same as for the `mount' procedure. -If CHECK? is true, first run ROOT's fsck tool (if any) non-interactively." + +If CHECK? is true, first run ROOT's fsck tool (if any) non-interactively. +If SKIP-CHECK-IF-CLEAN? is true, ask fsck to return immediately if ROOT is +marked as clean. If REPAIR is true, fsck may write to ROOT to perform repairs. +If REPAIR is also 'PREEN, ask fsck to perform only those repairs that it +considers safe." (if volatile-root? (begin @@ -435,7 +440,7 @@ (define* (mount-root-file-system root type "lowerdir=/real-root,upperdir=/rw-root/upper,workdir=/rw-root/work")) (begin (when check? - (check-file-system root type)) + (check-file-system root type (not skip-check-if-clean?) repair)) (mount root "/root" type flags options))) ;; Make sure /root/etc/mtab is a symlink to /proc/self/mounts. @@ -612,7 +617,13 @@ (define (device-string->file-system-device device-string) #:options root-options #:check? (if root-fs (file-system-check? root-fs) - #t)) + #t) + #:skip-check-if-clean? + (and=> root-fs + file-system-skip-check-if-clean?) + #:repair (if root-fs + (file-system-repair root-fs) + 'preen)) (mount "none" "/root" "tmpfs")) ;; Mount the specified file systems. -- cgit v1.2.3 From a75a3d71329d3ca07a2ef18b81fc7b463f703ed7 Mon Sep 17 00:00:00 2001 From: Tobias Geerinckx-Rice Date: Sun, 23 May 2021 17:02:29 +0200 Subject: linux-boot: Honour fsck.mode & fsck.repair. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * gnu/build/linux-boot.scm (boot-system): Honour ‘fsck.mode=’ and ‘fsck.repair=’ kernel command line options. * doc/guix.texi (Initial RAM Disk): Document both. --- doc/guix.texi | 19 +++++++++++++ gnu/build/linux-boot.scm | 72 +++++++++++++++++++++++++++++------------------- 2 files changed, 63 insertions(+), 28 deletions(-) (limited to 'gnu/build') diff --git a/doc/guix.texi b/doc/guix.texi index a62578be26..dc9b039aab 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -33285,6 +33285,25 @@ name like @code{/dev/sda1}, a file system label, or a file system UUID. When unspecified, the device name from the root file system of the operating system declaration is used. +@item fsck.mode=@var{mode} +Whether to check the @var{root} file system for errors before mounting +it. @var{mode} is one of @code{skip} (never check), @code{force} (always +check), or @code{auto} to respect the root file-system object's 'check?' +setting (@pxref{File Systems}) and run a full scan only if the file system +was not cleanly shut down. + +@code{auto} is the default if this option is not present or if @var{mode} +is not one of the above. + +@item fsck.repair=@var{level} +The level of repairs to perform automatically if errors are found in the +@var{root} file system. @var{level} is one of @code{no} (do not write to +@var{root} at all if possible), @code{yes} (repair as much as possible), +or @code{preen} to repair problems considered safe to repair automatically. + +@code{preen} is the default if this option is not present or if @var{level} +is not one of the above. + @item --system=@var{system} Have @file{/run/booted-system} and @file{/run/current-system} point to @var{system}. diff --git a/gnu/build/linux-boot.scm b/gnu/build/linux-boot.scm index ab05d1ba5e..8f0f3eb2fc 100644 --- a/gnu/build/linux-boot.scm +++ b/gnu/build/linux-boot.scm @@ -541,21 +541,36 @@ (define (device-string->file-system-device device-string) (mount-essential-file-systems) (let* ((args (linux-command-line)) (to-load (find-long-option "--load" args)) - (root-fs (find root-mount-point? mounts)) - (root-fs-type (or (and=> root-fs file-system-type) - "ext4")) - (root-fs-device (and=> root-fs file-system-device)) - (root-fs-flags (mount-flags->bit-mask - (or (and=> root-fs file-system-flags) - '()))) - (root-options (if root-fs - (file-system-options root-fs) - #f)) - ;; --root takes precedence over the 'device' field of the root - ;; record. - (root-device (or (and=> (find-long-option "--root" args) - device-string->file-system-device) - root-fs-device))) + ;; If present, ‘--root’ on the kernel command line takes precedence + ;; over the ‘device’ field of the root record. + (root-device (and=> (find-long-option "--root" args) + device-string->file-system-device)) + (root-fs (or (find root-mount-point? mounts) + ;; Fall back to fictitious defaults. + (file-system (device (or root-device "/dev/root")) + (mount-point "/") + (type "ext4")))) + (fsck.mode (find-long-option "fsck.mode" args))) + + (define (check? fs) + (match fsck.mode + ("skip" #f) + ("force" #t) + (_ (file-system-check? fs)))) ; assume "auto" + + (define (skip-check-if-clean? fs) + (match fsck.mode + ("force" #f) + (_ (file-system-skip-check-if-clean? fs)))) + + (define (repair fs) + (let ((arg (find-long-option "fsck.repair" args))) + (if arg + (match arg + ("no" #f) + ("yes" #t) + (_ 'preen)) + (file-system-repair fs)))) (when (member "--repl" args) (start-repl)) @@ -611,23 +626,24 @@ (define (device-string->file-system-device device-string) (if root-device (mount-root-file-system (canonicalize-device-spec root-device) - root-fs-type + (file-system-type root-fs) #:volatile-root? volatile-root? - #:flags root-fs-flags - #:options root-options - #:check? (if root-fs - (file-system-check? root-fs) - #t) + #:flags (mount-flags->bit-mask + (file-system-flags root-fs)) + #:options (file-system-options root-fs) + #:check? (check? root-fs) #:skip-check-if-clean? - (and=> root-fs - file-system-skip-check-if-clean?) - #:repair (if root-fs - (file-system-repair root-fs) - 'preen)) + (skip-check-if-clean? root-fs) + #:repair (repair root-fs)) (mount "none" "/root" "tmpfs")) - ;; Mount the specified file systems. - (for-each mount-file-system + ;; Mount the specified non-root file systems. + (for-each (lambda (fs) + (mount-file-system fs + #:check? (check? fs) + #:skip-check-if-clean? + (skip-check-if-clean? fs) + #:repair (repair fs))) (remove root-mount-point? mounts)) (setenv "EXT2FS_NO_MTAB_OK" #f) -- cgit v1.2.3 From 68b219b9f482f09e7c55aaee4b64222d8c86172a Mon Sep 17 00:00:00 2001 From: Tobias Geerinckx-Rice Date: Sat, 12 Jun 2021 21:36:08 +0200 Subject: gnu: Don't abuse check-btrfs-file-system to scan. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was never guaranteed to be run for non-root file systems. It was for root file systems only due to a bug now fixed. * gnu/build/file-systems.scm (check-btrfs-file-system): Don't invoke ‘btrfs device scan’ here. * gnu/system/linux-initrd.scm (raw-initrd): Do so here if any btrfs file systems are present. --- gnu/build/file-systems.scm | 2 -- gnu/system/linux-initrd.scm | 13 ++++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'gnu/build') diff --git a/gnu/build/file-systems.scm b/gnu/build/file-systems.scm index a54127e888..251ca51fc4 100644 --- a/gnu/build/file-systems.scm +++ b/gnu/build/file-systems.scm @@ -328,8 +328,6 @@ (define (check-btrfs-file-system device force? repair) @uref{https://bugzilla.redhat.com/show_bug.cgi?id=625967#c8}. If REPAIR is false, do not write to DEVICE. If it's #t, fix any errors found. Otherwise, fix only those considered safe to repair automatically." - ;; XXX Why make this conditional on (check? #t) at all? - (system* "btrfs" "device" "scan") ; ignore errors (if force? (match (status:exit-val (apply system* `("btrfs" "check" "--progress" diff --git a/gnu/system/linux-initrd.scm b/gnu/system/linux-initrd.scm index 8c245b8445..7f7740dd6e 100644 --- a/gnu/system/linux-initrd.scm +++ b/gnu/system/linux-initrd.scm @@ -210,6 +210,16 @@ (define device-mapping-commands (open source targets))) mapped-devices)) + (define file-system-scan-commands + ;; File systems like btrfs need help to assemble multi-device file systems + ;; but do not use manually-specified . + (let ((file-system-types (map file-system-type file-systems))) + (if (member "btrfs" file-system-types) + ;; Ignore errors: if the system manages to boot anyway, the better. + #~((system* (string-append #$btrfs-progs/static "/bin/btrfs") + "device" "scan")) + #~()))) + (define kodir (flat-linux-module-directory linux linux-modules)) @@ -245,7 +255,8 @@ (define kodir (map spec->file-system '#$(map file-system->spec file-systems)) #:pre-mount (lambda () - (and #$@device-mapping-commands)) + (and #$@device-mapping-commands + #$@file-system-scan-commands)) #:linux-modules '#$linux-modules #:linux-module-directory '#$kodir #:keymap-file #+(and=> keyboard-layout -- cgit v1.2.3 From 348f0c61efc0f35aedcd0e44bc9fa7bf7f067942 Mon Sep 17 00:00:00 2001 From: Tobias Geerinckx-Rice Date: Sun, 12 Sep 2021 18:07:54 +0200 Subject: syscalls: Deduplicate device number conversion. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * guix/cpio.scm (device-number, device->major+minor): Move to, and subsequently import from, … * guix/build/syscalls.scm (device-number, device-number->major+minor): …here. Note the slight name change. (mounts): Replace 16-bit open code with a DEVICE-NUMBER call. * gnu/build/linux-boot.scm (device-number): Remove duplicate 16-bit implementation in favour of the one above. (resume-if-hibernated): Reuse DEVICE-NUMBER->MAJOR+MINOR. --- gnu/build/linux-boot.scm | 18 ++++-------------- guix/build/syscalls.scm | 29 ++++++++++++++++++++++++++++- guix/cpio.scm | 21 ++++----------------- 3 files changed, 36 insertions(+), 32 deletions(-) (limited to 'gnu/build') diff --git a/gnu/build/linux-boot.scm b/gnu/build/linux-boot.scm index 8f0f3eb2fc..8efe6e5f9c 100644 --- a/gnu/build/linux-boot.scm +++ b/gnu/build/linux-boot.scm @@ -25,6 +25,7 @@ (define-module (gnu build linux-boot) #:autoload (system repl repl) (start-repl) #:use-module (srfi srfi-1) #:use-module (srfi srfi-9) + #:use-module (srfi srfi-11) #:use-module (srfi srfi-26) #:use-module (ice-9 match) #:use-module (ice-9 rdelim) @@ -44,7 +45,6 @@ (define-module (gnu build linux-boot) make-static-device-nodes configure-qemu-networking - device-number boot-system)) ;;; Commentary: @@ -134,14 +134,9 @@ (define (string->major:minor string) ;; is found on the command line; our canonicalize-device-spec gives ;; up after 20 seconds. We could emulate the former by looping… (device (canonicalize-device-spec spec)) - (rdev (stat:rdev (stat device))) - ;; For backwards compatibility, device numbering is a baroque affair. - ;; This is the full 64-bit scheme used by glibc's . - (major (logior (ash (logand #x00000000000fff00 rdev) -8) - (ash (logand #xfffff00000000000 rdev) -32))) - (minor (logior (logand #x00000000000000ff rdev) - (ash (logand #x00000ffffff00000 rdev) -12)))) - (format #f "~a:~a" major minor))) + (rdev (stat:rdev (stat device)))) + (let-values (((major minor) (device-number->major+minor rdev))) + (format #f "~a:~a" major minor)))) ;; Write the resume DEVICE to this magic file, using the MAJOR:MINOR device ;; numbers if possible. The kernel will immediately try to resume from it. @@ -392,11 +387,6 @@ (define* (configure-qemu-networking #:optional (interface "eth0")) (logand (network-interface-flags sock interface) IFF_UP))) -(define (device-number major minor) - "Return the device number for the device with MAJOR and MINOR, for use as -the last argument of `mknod'." - (+ (* major 256) minor)) - (define (pidof program) "Return the PID of the first presumed instance of PROGRAM." (let ((program (basename program))) diff --git a/guix/build/syscalls.scm b/guix/build/syscalls.scm index ac1b0c2eea..99a3b45004 100644 --- a/guix/build/syscalls.scm +++ b/guix/build/syscalls.scm @@ -7,6 +7,7 @@ ;;; Copyright © 2020 Julien Lepiller ;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen ;;; Copyright © 2021 Chris Marusich +;;; Copyright © 2021 Tobias Geerinckx-Rice ;;; ;;; This file is part of GNU Guix. ;;; @@ -56,6 +57,9 @@ (define-module (guix build syscalls) restart-on-EINTR + device-number + device-number->major+minor + mount? mount-device-number mount-source @@ -448,6 +452,29 @@ (define-as-needed proc (lambda* (args ...) body ...))) (module-define! (current-module) 'variable value) (module-export! (current-module) '(variable))))))) + +;;; +;;; Block devices. +;;; + +;; Convert between major:minor pairs and packed ‘device number’ representation. +;; XXX These aren't syscalls, but if you squint very hard they are part of the +;; FFI or however you want to justify me not finding a better fit… :-) +(define (device-number major minor) ; see glibc's + "Return the device number for the device with MAJOR and MINOR, for use as +the last argument of `mknod'." + (logior (ash (logand #x00000fff major) 8) + (ash (logand #xfffff000 major) 32) + (logand #x000000ff minor) + (ash (logand #xffffff00 minor) 12))) + +(define (device-number->major+minor device) ; see glibc's + "Return two values: the major and minor device numbers that make up DEVICE." + (values (logior (ash (logand #x00000000000fff00 device) -8) + (ash (logand #xfffff00000000000 device) -32)) + (logior (logand #x00000000000000ff device) + (ash (logand #x00000ffffff00000 device) -12)))) + ;;; ;;; File systems. @@ -628,7 +655,7 @@ (define (mounts) (define (string->device-number str) (match (string-split str #\:) (((= string->number major) (= string->number minor)) - (+ (* major 256) minor)))) + (device-number major minor)))) (call-with-input-file "/proc/self/mountinfo" (lambda (port) diff --git a/guix/cpio.scm b/guix/cpio.scm index 8038a11f3c..d4a7d5f1e0 100644 --- a/guix/cpio.scm +++ b/guix/cpio.scm @@ -18,6 +18,8 @@ ;;; along with GNU Guix. If not, see . (define-module (guix cpio) + #:use-module ((guix build syscalls) #:select (device-number + device-number->major+minor)) #:use-module ((guix build utils) #:select (dump-port)) #:use-module (srfi srfi-9) #:use-module (srfi srfi-11) @@ -129,8 +131,8 @@ (define* (make-cpio-header #:key (nlink 1) (mtime 0) (size 0) (dev 0) (rdev 0) (name-size 0)) "Return a new cpio file header." - (let-values (((major minor) (device->major+minor dev)) - ((rmajor rminor) (device->major+minor rdev))) + (let-values (((major minor) (device-number->major+minor dev)) + ((rmajor rminor) (device-number->major+minor rdev))) (%make-cpio-header MAGIC inode mode uid gid nlink mtime @@ -154,21 +156,6 @@ (define (mode->type mode) (else (error "unsupported file type" mode))))) -(define (device-number major minor) ; see glibc's - "Return the device number for the device with MAJOR and MINOR, for use as -the last argument of `mknod'." - (logior (ash (logand #x00000fff major) 8) - (ash (logand #xfffff000 major) 32) - (logand #x000000ff minor) - (ash (logand #xffffff00 minor) 12))) - -(define (device->major+minor device) ; see glibc's - "Return two values: the major and minor device numbers that make up DEVICE." - (values (logior (ash (logand #x00000000000fff00 device) -8) - (ash (logand #xfffff00000000000 device) -32)) - (logior (logand #x00000000000000ff device) - (ash (logand #x00000ffffff00000 device) -12)))) - (define* (file->cpio-header file #:optional (file-name file) #:key (stat lstat)) "Return a cpio header corresponding to the info returned by STAT for FILE, -- cgit v1.2.3 From 09a8fb1f37dec62070a4baa3682e48a45f49467d Mon Sep 17 00:00:00 2001 From: Tobias Geerinckx-Rice Date: Wed, 22 Sep 2021 18:53:35 +0200 Subject: file-systems: Add file system to *-SUPERBLOCK-VOLUME-NAME docstrings. * gnu/build/file-systems.scm (ext2-superblock-volume-name) (bcachefs-superblock-volume-name, btrfs-superblock-volume-name) (fat32-superblock-volume-name, fat16-superblock-volume-name) (iso9660-superblock-volume-name, jfs-superblock-volume-name) (f2fs-superblock-volume-name): Mention the file system type in the docstring for consistency with the other superblock procedures. --- gnu/build/file-systems.scm | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'gnu/build') diff --git a/gnu/build/file-systems.scm b/gnu/build/file-systems.scm index 251ca51fc4..e79037c12c 100644 --- a/gnu/build/file-systems.scm +++ b/gnu/build/file-systems.scm @@ -166,8 +166,8 @@ (define (ext2-superblock-uuid sblock) (sub-bytevector sblock 104 16)) (define (ext2-superblock-volume-name sblock) - "Return the volume name of SBLOCK as a string of at most 16 characters, or -#f if SBLOCK has no volume name." + "Return the volume name of ext2 superblock SBLOCK as a string of at most 16 +characters, or #f if SBLOCK has no volume name." (null-terminated-latin1->string (sub-bytevector sblock 120 16))) (define (check-ext2-file-system device force? repair) @@ -265,8 +265,8 @@ (define (bcachefs-superblock-external-uuid sblock) (sub-bytevector sblock 56 16)) (define (bcachefs-superblock-volume-name sblock) - "Return the volume name of SBLOCK as a string of at most 32 characters, or -#f if SBLOCK has no volume name." + "Return the volume name of bcachefs superblock SBLOCK as a string of at most +32 characters, or #f if SBLOCK has no volume name." (null-terminated-latin1->string (sub-bytevector sblock 72 32))) (define (check-bcachefs-file-system device force? repair) @@ -317,8 +317,8 @@ (define (btrfs-superblock-uuid sblock) (sub-bytevector sblock 32 16)) (define (btrfs-superblock-volume-name sblock) - "Return the volume name of SBLOCK as a string of at most 256 characters, or -#f if SBLOCK has no volume name." + "Return the volume name of btrfs superblock SBLOCK as a string of at most 256 +characters, or #f if SBLOCK has no volume name." (null-terminated-latin1->string (sub-bytevector sblock 299 256))) (define (check-btrfs-file-system device force? repair) @@ -364,9 +364,9 @@ (define (fat32-superblock-uuid sblock) (sub-bytevector sblock 67 4)) (define (fat32-superblock-volume-name sblock) - "Return the volume name of SBLOCK as a string of at most 11 characters, or -#f if SBLOCK has no volume name. The volume name is a latin1 string. -Trailing spaces are trimmed." + "Return the volume name of fat superblock SBLOCK as a string of at most 11 +characters, or #f if SBLOCK has no volume name. The volume name is a latin1 +string. Trailing spaces are trimmed." (string-trim-right (latin1->string (sub-bytevector sblock 71 11) (lambda (c) #f)) #\space)) (define (check-fat-file-system device force? repair) @@ -404,9 +404,9 @@ (define (fat16-superblock-uuid sblock) (sub-bytevector sblock 39 4)) (define (fat16-superblock-volume-name sblock) - "Return the volume name of SBLOCK as a string of at most 11 characters, or -#f if SBLOCK has no volume name. The volume name is a latin1 string. -Trailing spaces are trimmed." + "Return the volume name of fat superblock SBLOCK as a string of at most 11 +characters, or #f if SBLOCK has no volume name. The volume name is a latin1 +string. Trailing spaces are trimmed." (string-trim-right (latin1->string (sub-bytevector sblock 43 11) (lambda (c) #f)) #\space)) @@ -465,8 +465,8 @@ (define (iso9660-superblock-uuid sblock) (sub-bytevector time 0 16))) ; strips GMT offset. (define (iso9660-superblock-volume-name sblock) - "Return the volume name of SBLOCK as a string. The volume name is an ASCII -string. Trailing spaces are trimmed." + "Return the volume name of iso9660 superblock SBLOCK as a string. The volume +name is an ASCII string. Trailing spaces are trimmed." ;; Note: Valid characters are of the set "[0-9][A-Z]_" (ECMA-119 Appendix A) (string-trim-right (latin1->string (sub-bytevector sblock 40 32) (lambda (c) #f)) #\space)) @@ -497,8 +497,8 @@ (define (jfs-superblock-uuid sblock) (sub-bytevector sblock 136 16)) (define (jfs-superblock-volume-name sblock) - "Return the volume name of SBLOCK as a string of at most 16 characters, or -#f if SBLOCK has no volume name." + "Return the volume name of JFS superblock SBLOCK as a string of at most 16 +characters, or #f if SBLOCK has no volume name." (null-terminated-latin1->string (sub-bytevector sblock 152 16))) (define (check-jfs-file-system device force? repair) @@ -567,8 +567,8 @@ (define (f2fs-superblock-uuid sblock) 16)) (define (f2fs-superblock-volume-name sblock) - "Return the volume name of SBLOCK as a string of at most 512 characters, or -#f if SBLOCK has no volume name." + "Return the volume name of F2FS superblock SBLOCK as a string of at most 512 +characters, or #f if SBLOCK has no volume name." (null-terminated-utf16->string (sub-bytevector sblock (- (+ #x470 12) #x400) 512) %f2fs-endianness)) -- cgit v1.2.3 From 34c105f929b73560a2476486660c0cbba7a7410a Mon Sep 17 00:00:00 2001 From: Tobias Geerinckx-Rice Date: Wed, 22 Sep 2021 19:00:42 +0200 Subject: file-systems: Add support for XFS. * gnu/build/file-systems.scm (%xfs-endianness): New syntax. (xfs-superblock?, read-xfs-superblock, xfs-superblock-uuid) (xfs-superblock-volume-name, check-xfs-file-system): New procedures. (%partition-label-readers, %partition-uuid-readers, check-file-system): Register them. * doc/guix.texi (Keyboard Layout and Networking and Partitioning): Note XFS support. --- doc/guix.texi | 2 +- gnu/build/file-systems.scm | 72 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 3 deletions(-) (limited to 'gnu/build') diff --git a/doc/guix.texi b/doc/guix.texi index dc9b039aab..9bb91b94fd 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -2453,7 +2453,7 @@ bootloaders. Once you are done partitioning the target hard disk drive, you have to create a file system on the relevant partition(s)@footnote{Currently -Guix System only supports ext4, btrfs, JFS, and F2FS file systems. In +Guix System only supports ext4, btrfs, JFS, F2FS, and XFS file systems. In particular, code that reads file system UUIDs and labels only works for these file system types.}. For the ESP, if you have one and assuming it is @file{/dev/sda1}, run: diff --git a/gnu/build/file-systems.scm b/gnu/build/file-systems.scm index e79037c12c..2a4dcd4c82 100644 --- a/gnu/build/file-systems.scm +++ b/gnu/build/file-systems.scm @@ -678,6 +678,69 @@ (define (check-ntfs-file-system device force? repair) (0 'pass) (_ 'fatal-error))) + + +;;; +;;; XFS file systems. +;;; + +;; + +(define-syntax %xfs-endianness + ;; Endianness of XFS file systems. + (identifier-syntax (endianness big))) + +(define (xfs-superblock? sblock) + "Return #t when SBLOCK is an XFS superblock." + (bytevector=? (sub-bytevector sblock 0 4) + (string->utf8 "XFSB"))) + +(define (read-xfs-superblock device) + "Return the raw contents of DEVICE's XFS superblock as a bytevector, or #f +if DEVICE does not contain an XFS file system." + (read-superblock device 0 120 xfs-superblock?)) + +(define (xfs-superblock-uuid sblock) + "Return the UUID of XFS superblock SBLOCK as a 16-byte bytevector." + (sub-bytevector sblock 32 16)) + +(define (xfs-superblock-volume-name sblock) + "Return the volume name of XFS superblock SBLOCK as a string of at most 12 +characters, or #f if SBLOCK has no volume name." + (null-terminated-latin1->string (sub-bytevector sblock 108 12))) + +(define (check-xfs-file-system device force? repair) + "Return the health of an unmounted XFS file system on DEVICE. If FORCE? is +false, return 'PASS unconditionally as XFS claims no need for off-line checks. +When FORCE? is true, do perform a thorough check. If REPAIR is false, do not +write to DEVICE. If it's #t, replay the log, check, and fix any errors found. +Otherwise, only replay the log, and check without attempting further repairs." + (define (xfs_repair) + (status:exit-val + (apply system* `("xfs_repair" "-Pv" + ,@(match repair + (#t '("-e")) + (_ '("-n"))) ; will miss some errors + ,device)))) + (if force? + ;; xfs_repair fails with exit status 2 if the log is dirty, which is + ;; likely in situations where you're running xfs_repair. Only the kernel + ;; can replay the log by {,un}mounting it cleanly. + (match (let ((status (xfs_repair))) + (if (and repair (eq? 2 status)) + (let ((target "/replay-XFS-log")) + ;; The kernel helpfully prints a ‘Mounting…’ notice for us. + (mkdir target) + (mount device target "xfs") + (umount target) + (rmdir target) + (xfs_repair)) + status)) + (0 'pass) + (4 'errors-corrected) + (_ 'fatal-error)) + 'pass)) + ;;; ;;; Partition lookup. @@ -771,7 +834,9 @@ (define %partition-label-readers (partition-field-reader read-jfs-superblock jfs-superblock-volume-name) (partition-field-reader read-f2fs-superblock - f2fs-superblock-volume-name))) + f2fs-superblock-volume-name) + (partition-field-reader read-xfs-superblock + xfs-superblock-volume-name))) (define %partition-uuid-readers (list (partition-field-reader read-iso9660-superblock @@ -793,7 +858,9 @@ (define %partition-uuid-readers (partition-field-reader read-f2fs-superblock f2fs-superblock-uuid) (partition-field-reader read-ntfs-superblock - ntfs-superblock-uuid))) + ntfs-superblock-uuid) + (partition-field-reader read-xfs-superblock + xfs-superblock-uuid))) (define read-partition-label (cut read-partition-field <> %partition-label-readers)) @@ -904,6 +971,7 @@ (define check-procedure ((string-prefix? "f2fs" type) check-f2fs-file-system) ((string-prefix? "ntfs" type) check-ntfs-file-system) ((string-prefix? "nfs" type) (const 'pass)) + ((string-prefix? "xfs" type) check-xfs-file-system) (else #f))) (if check-procedure -- cgit v1.2.3 From e91c9ce32e6fa3a4759d83a0d7ae7d49d813668d Mon Sep 17 00:00:00 2001 From: Tobias Geerinckx-Rice Date: Tue, 28 Sep 2021 21:50:16 +0200 Subject: file-systems: Prefer low-memory mode of ‘btrfs check’. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * gnu/build/file-systems.scm (check-btrfs-file-system): Add ‘--mode lowmem’ arguments when supported. Reported by Noisytoot on #guix. --- gnu/build/file-systems.scm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'gnu/build') diff --git a/gnu/build/file-systems.scm b/gnu/build/file-systems.scm index 2a4dcd4c82..d8a5ddf1e5 100644 --- a/gnu/build/file-systems.scm +++ b/gnu/build/file-systems.scm @@ -336,7 +336,10 @@ (define (check-btrfs-file-system device force? repair) ;; Upstream considers ALL repairs dangerous ;; and will warn the user at run time. (#t '("--repair")) - (_ '("--readonly"))) ; a no-op for clarity + (_ '("--readonly" ; a no-op for clarity + ;; A 466G file system with 180G used is + ;; enough to kill btrfs with 6G of RAM. + "--mode" "lowmem"))) ,device))) (0 'pass) (_ 'fatal-error)) -- cgit v1.2.3