aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--guix/store.scm40
-rw-r--r--tests/store.scm12
2 files changed, 48 insertions, 4 deletions
diff --git a/guix/store.scm b/guix/store.scm
index 58f7e36762..c1898c5c81 100644
--- a/guix/store.scm
+++ b/guix/store.scm
@@ -21,6 +21,7 @@
#:use-module (guix utils)
#:use-module (guix config)
#:use-module (guix serialization)
+ #:autoload (guix base32) (bytevector->base32-string)
#:use-module (rnrs bytevectors)
#:use-module (rnrs io ports)
#:use-module (srfi srfi-1)
@@ -35,6 +36,7 @@
#:use-module (ice-9 vlist)
#:use-module (ice-9 popen)
#:export (%daemon-socket-file
+ %gc-roots-directory
nix-server?
nix-server-major-version
@@ -63,6 +65,8 @@
build-derivations
add-temp-root
add-indirect-root
+ add-permanent-root
+ remove-permanent-root
substitutable?
substitutable-path
@@ -570,12 +574,40 @@ Return #t."
boolean)
(define-operation (add-indirect-root (string file-name))
- "Make FILE-NAME an indirect root for the garbage collector; FILE-NAME
-can be anywhere on the file system, but it must be an absolute file
-name--it is the caller's responsibility to ensure that it is an absolute
-file name. Return #t on success."
+ "Make the symlink FILE-NAME an indirect root for the garbage collector:
+whatever store item FILE-NAME points to will not be collected. Return #t on
+success.
+
+FILE-NAME can be anywhere on the file system, but it must be an absolute file
+name--it is the caller's responsibility to ensure that it is an absolute file
+name."
boolean)
+(define %gc-roots-directory
+ ;; The place where garbage collector roots (symlinks) are kept.
+ (string-append %state-directory "/gcroots"))
+
+(define (add-permanent-root target)
+ "Add a garbage collector root pointing to TARGET, an element of the store,
+preventing TARGET from even being collected. This can also be used if TARGET
+does not exist yet.
+
+Raise an error if the caller does not have write access to the GC root
+directory."
+ (let* ((root (string-append %gc-roots-directory "/" (basename target))))
+ (catch 'system-error
+ (lambda ()
+ (symlink target root))
+ (lambda args
+ ;; If ROOT already exists, this is fine; otherwise, re-throw.
+ (unless (= EEXIST (system-error-errno args))
+ (apply throw args))))))
+
+(define (remove-permanent-root target)
+ "Remove the permanent garbage collector root pointing to TARGET. Raise an
+error if there is no such root."
+ (delete-file (string-append %gc-roots-directory "/" (basename target))))
+
(define references
(operation (query-references (store-path path))
"Return the list of references of PATH."
diff --git a/tests/store.scm b/tests/store.scm
index 3932a8eb45..90137b9754 100644
--- a/tests/store.scm
+++ b/tests/store.scm
@@ -147,6 +147,18 @@
;; (valid-path? %store p1)
;; (member (pk p2) (live-paths %store)))))
+(test-assert "permanent root"
+ (let* ((p (with-store store
+ (let ((p (add-text-to-store store "random-text"
+ (random-text))))
+ (add-permanent-root p)
+ (add-permanent-root p) ; should not throw
+ p))))
+ (and (member p (live-paths %store))
+ (begin
+ (remove-permanent-root p)
+ (->bool (member p (dead-paths %store)))))))
+
(test-assert "dead path can be explicitly collected"
(let ((p (add-text-to-store %store "random-text"
(random-text) '())))