From adb158b7396cbdcda347fa298978408e531a03fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20Court=C3=A8s?= Date: Fri, 14 Dec 2018 11:10:25 +0100 Subject: deduplication: Gracefully handle ENOSPC raised by 'link' calls. Reported by Andreas Enge in . * guix/store/deduplication.scm (replace-with-link): Catch ENOSPC around 'get-temp-link'. Do nothing when 'get-temp-link' throws ENOSPC. Move code to restore PARENT's permissions outside of 'catch'. * tests/store-deduplication.scm ("deduplicate, ENOSPC"): New test. --- guix/store/deduplication.scm | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) (limited to 'guix/store') diff --git a/guix/store/deduplication.scm b/guix/store/deduplication.scm index 21b0c81f3d..a777940f86 100644 --- a/guix/store/deduplication.scm +++ b/guix/store/deduplication.scm @@ -99,24 +99,38 @@ LINK-PREFIX." (define* (replace-with-link target to-replace #:key (swap-directory (dirname target))) "Atomically replace the file TO-REPLACE with a link to TARGET. Use -SWAP-DIRECTORY as the directory to store temporary hard links. +SWAP-DIRECTORY as the directory to store temporary hard links. Upon ENOSPC +and EMLINK, TO-REPLACE is left unchanged. Note: TARGET, TO-REPLACE, and SWAP-DIRECTORY must be on the same file system." - (let* ((temp-link (get-temp-link target swap-directory)) - (parent (dirname to-replace)) - (stat (stat parent))) - (make-file-writable parent) + (define temp-link (catch 'system-error (lambda () - (rename-file temp-link to-replace) - - ;; Restore PARENT's mtime and permissions. - (set-file-time parent stat) - (chmod parent (stat:mode stat))) + (get-temp-link target swap-directory)) (lambda args - (delete-file temp-link) - (unless (= EMLINK (system-error-errno args)) - (apply throw args)))))) + ;; We get ENOSPC when we can't fit an additional entry in + ;; SWAP-DIRECTORY. + (if (= ENOSPC (system-error-errno args)) + #f + (apply throw args))))) + + ;; If we couldn't create TEMP-LINK, that's OK: just don't do the + ;; replacement, which means TO-REPLACE won't be deduplicated. + (when temp-link + (let* ((parent (dirname to-replace)) + (stat (stat parent))) + (make-file-writable parent) + (catch 'system-error + (lambda () + (rename-file temp-link to-replace)) + (lambda args + (delete-file temp-link) + (unless (= EMLINK (system-error-errno args)) + (apply throw args)))) + + ;; Restore PARENT's mtime and permissions. + (set-file-time parent stat) + (chmod parent (stat:mode stat))))) (define* (deduplicate path hash #:key (store %store-directory)) "Check if a store item with sha256 hash HASH already exists. If so, -- cgit v1.2.3