diff options
| author | Reepca Russelstein <reepca@russelstein.xyz> | 2026-07-03 17:37:11 -0500 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2026-07-05 11:47:15 +0200 |
| commit | 57fd857c1e3ff11511ba451021d559e874b50e34 (patch) | |
| tree | 3de94e8c946a2f90c3ae03626945a781b0699e04 /tests | |
| parent | cf583b3a3df0741dcc6ab9906da191db2f527a01 (diff) | |
scripts: substitute: Accommodate renaming unwritable directories.
This fixes a regression introduced in 26d7eb8a4adafc648ef035e91b6bbc4945d4c962
when running ‘guix-daemon’ without root privileges.
On rootless installations, we must abide by the usual file access rules, which
include the requirement that a directory be writable before it be renamed (the
rationale being that its ".." entry needs to be modified).
* guix/scripts/substitute.scm (rename-file*): New procedure.
(guix-substitute): Use it.
* tests/substitute.scm (directory-nar-sha256): New procedure.
(%narinfo/directory): New variable.
(call-with-narinfo): Add '#:directory?' keyword argument.
(with-directory-narinfo): New syntax.
("substitute, authorized key, directory"): New test case.
Fixes: guix/guix#9686
Change-Id: I9d399dd5f1717b7d02854d97e1100a0a464fac75
Signed-off-by: Ludovic Courtès <ludo@gnu.org>
Merges: #9702
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/substitute.scm | 75 |
1 files changed, 70 insertions, 5 deletions
diff --git a/tests/substitute.scm b/tests/substitute.scm index b749561347a..fe1e29adb6b 100644 --- a/tests/substitute.scm +++ b/tests/substitute.scm | |||
| @@ -38,6 +38,7 @@ | |||
| 38 | #:use-module (rnrs bytevectors) | 38 | #:use-module (rnrs bytevectors) |
| 39 | #:use-module (rnrs io ports) | 39 | #:use-module (rnrs io ports) |
| 40 | #:use-module (web uri) | 40 | #:use-module (web uri) |
| 41 | #:use-module (ice-9 match) | ||
| 41 | #:use-module (ice-9 regex) | 42 | #:use-module (ice-9 regex) |
| 42 | #:use-module (ice-9 binary-ports) | 43 | #:use-module (ice-9 binary-ports) |
| 43 | #:use-module (srfi srfi-11) | 44 | #:use-module (srfi srfi-11) |
| @@ -65,7 +66,7 @@ it writes to GUIX-WARNING-PORT a messages that matches ERROR-RX." | |||
| 65 | 66 | ||
| 66 | (define (request-substitution item destination) | 67 | (define (request-substitution item destination) |
| 67 | "Run 'guix substitute --substitute' to fetch ITEM to DESTINATION." | 68 | "Run 'guix substitute --substitute' to fetch ITEM to DESTINATION." |
| 68 | (false-if-exception (delete-file destination)) | 69 | (false-if-exception (delete-file-recursively destination)) |
| 69 | (with-input-from-string (string-append "substitute " item " " | 70 | (with-input-from-string (string-append "substitute " item " " |
| 70 | destination "\n") | 71 | destination "\n") |
| 71 | (lambda () | 72 | (lambda () |
| @@ -143,6 +144,29 @@ version identifier.." | |||
| 143 | ;; <https://www.rfc-editor.org/rfc/rfc5737>. | 144 | ;; <https://www.rfc-editor.org/rfc/rfc5737>. |
| 144 | "http://203.0.113.1") | 145 | "http://203.0.113.1") |
| 145 | 146 | ||
| 147 | (define (directory-nar-sha256 string) | ||
| 148 | "Compute the sha256 of the nar of a directory containing a single regular | ||
| 149 | file named \"foo\" containing STRING." | ||
| 150 | (sha256 (call-with-output-bytevector | ||
| 151 | (lambda (port) | ||
| 152 | (let ((bv (string->utf8 string))) | ||
| 153 | (call-with-input-bytevector | ||
| 154 | bv | ||
| 155 | (lambda (contents) | ||
| 156 | (write-file-tree "root" port | ||
| 157 | #:file-type+size | ||
| 158 | (match-lambda | ||
| 159 | ("root" | ||
| 160 | (values 'directory #f)) | ||
| 161 | ("root/foo" | ||
| 162 | (values 'regular | ||
| 163 | (bytevector-length | ||
| 164 | bv)))) | ||
| 165 | #:file-port | ||
| 166 | (const contents) | ||
| 167 | #:directory-entries | ||
| 168 | (const '("foo")))))))))) | ||
| 169 | |||
| 146 | (define (plain-file-nar-sha256 string) | 170 | (define (plain-file-nar-sha256 string) |
| 147 | (sha256 (call-with-output-bytevector | 171 | (sha256 (call-with-output-bytevector |
| 148 | (lambda (port) | 172 | (lambda (port) |
| @@ -172,9 +196,22 @@ References: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bar bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | |||
| 172 | Deriver: cccccccccccccccccccccccccccccccc-foo.drv | 196 | Deriver: cccccccccccccccccccccccccccccccc-foo.drv |
| 173 | System: mips64el-linux\n")) | 197 | System: mips64el-linux\n")) |
| 174 | 198 | ||
| 199 | (define %narinfo/directory | ||
| 200 | (string-append "StorePath: " (%store-prefix) | ||
| 201 | "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo | ||
| 202 | URL: example.nar | ||
| 203 | Compression: none | ||
| 204 | NarHash: sha256:" (bytevector->nix-base32-string | ||
| 205 | (directory-nar-sha256 "Substitutable data.")) " | ||
| 206 | NarSize: 304 | ||
| 207 | References: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bar bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-baz | ||
| 208 | Deriver: cccccccccccccccccccccccccccccccc-foo.drv | ||
| 209 | System: mips64el-linux\n")) | ||
| 210 | |||
| 175 | (define* (call-with-narinfo narinfo thunk | 211 | (define* (call-with-narinfo narinfo thunk |
| 176 | #:optional | 212 | #:optional |
| 177 | (narinfo-directory %main-substitute-directory)) | 213 | (narinfo-directory %main-substitute-directory) |
| 214 | #:key directory?) | ||
| 178 | "Call THUNK in a context where the directory at URL is populated with | 215 | "Call THUNK in a context where the directory at URL is populated with |
| 179 | a file for NARINFO." | 216 | a file for NARINFO." |
| 180 | (mkdir-p narinfo-directory) | 217 | (mkdir-p narinfo-directory) |
| @@ -195,9 +232,16 @@ a file for NARINFO." | |||
| 195 | (cut display narinfo <>)) | 232 | (cut display narinfo <>)) |
| 196 | 233 | ||
| 197 | ;; Prepare the nar. | 234 | ;; Prepare the nar. |
| 198 | (call-with-output-file | 235 | (let ((out (string-append narinfo-directory "/example.out"))) |
| 199 | (string-append narinfo-directory "/example.out") | 236 | (when (file-exists? out) |
| 200 | (cut display "Substitutable data." <>)) | 237 | (delete-file-recursively out)) |
| 238 | (if directory? | ||
| 239 | (begin | ||
| 240 | (mkdir out) | ||
| 241 | (call-with-output-file (string-append out "/foo") | ||
| 242 | (cut display "Substitutable data." <>))) | ||
| 243 | (call-with-output-file out | ||
| 244 | (cut display "Substitutable data." <>)))) | ||
| 201 | (call-with-output-file | 245 | (call-with-output-file |
| 202 | (string-append narinfo-directory "/example.nar") | 246 | (string-append narinfo-directory "/example.nar") |
| 203 | (cute write-file | 247 | (cute write-file |
| @@ -215,6 +259,9 @@ a file for NARINFO." | |||
| 215 | (define-syntax-rule (with-narinfo* narinfo directory body ...) | 259 | (define-syntax-rule (with-narinfo* narinfo directory body ...) |
| 216 | (call-with-narinfo narinfo (lambda () body ...) directory)) | 260 | (call-with-narinfo narinfo (lambda () body ...) directory)) |
| 217 | 261 | ||
| 262 | (define-syntax-rule (with-directory-narinfo narinfo body ...) | ||
| 263 | (call-with-narinfo narinfo (lambda () body ...) #:directory? #t)) | ||
| 264 | |||
| 218 | ;; Transmit these options to 'guix substitute'. | 265 | ;; Transmit these options to 'guix substitute'. |
| 219 | (substitute-urls (list (getenv "GUIX_BINARY_SUBSTITUTE_URL"))) | 266 | (substitute-urls (list (getenv "GUIX_BINARY_SUBSTITUTE_URL"))) |
| 220 | 267 | ||
| @@ -524,6 +571,24 @@ System: mips64el-linux\n"))) | |||
| 524 | (lambda () | 571 | (lambda () |
| 525 | (false-if-exception (delete-file "substitute-retrieved")))))) | 572 | (false-if-exception (delete-file "substitute-retrieved")))))) |
| 526 | 573 | ||
| 574 | (test-equal "substitute, authorized key, directory" | ||
| 575 | '("Substitutable data." 1 #o555) | ||
| 576 | (with-directory-narinfo (string-append %narinfo/directory "Signature: " | ||
| 577 | (signature-field %narinfo/directory)) | ||
| 578 | (dynamic-wind | ||
| 579 | (const #t) | ||
| 580 | (lambda () | ||
| 581 | (request-substitution (string-append (%store-prefix) | ||
| 582 | "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo") | ||
| 583 | "substitute-retrieved") | ||
| 584 | (list (call-with-input-file "substitute-retrieved/foo" get-string-all) | ||
| 585 | (stat:mtime (lstat "substitute-retrieved")) | ||
| 586 | (stat:perms (lstat "substitute-retrieved")))) | ||
| 587 | (lambda () | ||
| 588 | ;; Needs to be writable to be deleted | ||
| 589 | (false-if-exception (chmod "substitute-retrieved" #o755)) | ||
| 590 | (false-if-exception (delete-file-recursively "substitute-retrieved")))))) | ||
| 591 | |||
| 527 | (test-equal "substitute, authorized key, first substitute URL is unroutable" | 592 | (test-equal "substitute, authorized key, first substitute URL is unroutable" |
| 528 | '("Substitutable data." 1 #o444) | 593 | '("Substitutable data." 1 #o444) |
| 529 | (with-narinfo (string-append %narinfo "Signature: " | 594 | (with-narinfo (string-append %narinfo "Signature: " |
