summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxim Cournoyer <maxim.cournoyer@gmail.com>2021-06-17 01:22:35 -0400
committerMaxim Cournoyer <maxim.cournoyer@gmail.com>2021-06-29 14:53:21 -0400
commit4f3bdc8f21657dbda857027b3ec8754dd4c7c67b (patch)
treec2a13dca9fdd301c8042696ea5f8f62dcfe43818
parent6b0e55cde901dd5f6eae72cee10723b7739cadf7 (diff)
pack: Prevent duplicate files in tar archives.
Tar translate duplicate files in the archive into hard links. These can cause problems, as not every tool support them; for example dpkg doesn't. * gnu/system/file-systems.scm (reduce-directories): New procedure. (file-prefix?): Lift the restriction on file prefix. The procedure can be useful for comparing relative file names. Adjust doc. (file-name-depth): New procedure, extracted from ... (btrfs-store-subvolume-file-name): ... here. * guix/scripts/pack.scm (self-contained-tarball/builder): Use reduce-directories. * tests/file-systems.scm ("reduce-directories"): New test.
-rw-r--r--gnu/system/file-systems.scm56
-rw-r--r--guix/scripts/pack.scm6
-rw-r--r--tests/file-systems.scm7
3 files changed, 48 insertions, 21 deletions
diff --git a/gnu/system/file-systems.scm b/gnu/system/file-systems.scm
index 464e87cb181..fb87bfc85b9 100644
--- a/gnu/system/file-systems.scm
+++ b/gnu/system/file-systems.scm
@@ -55,6 +55,7 @@
55 file-system-dependencies 55 file-system-dependencies
56 file-system-location 56 file-system-location
57 57
58 reduce-directories
58 file-system-type-predicate 59 file-system-type-predicate
59 btrfs-subvolume? 60 btrfs-subvolume?
60 btrfs-store-subvolume-file-name 61 btrfs-store-subvolume-file-name
@@ -231,8 +232,8 @@
231 (char-set-complement (char-set #\/))) 232 (char-set-complement (char-set #\/)))
232 233
233(define (file-prefix? file1 file2) 234(define (file-prefix? file1 file2)
234 "Return #t if FILE1 denotes the name of a file that is a parent of FILE2, 235 "Return #t if FILE1 denotes the name of a file that is a parent of FILE2.
235where both FILE1 and FILE2 are absolute file name. For example: 236For example:
236 237
237 (file-prefix? \"/gnu\" \"/gnu/store\") 238 (file-prefix? \"/gnu\" \"/gnu/store\")
238 => #t 239 => #t
@@ -240,19 +241,41 @@ where both FILE1 and FILE2 are absolute file name. For example:
240 (file-prefix? \"/gn\" \"/gnu/store\") 241 (file-prefix? \"/gn\" \"/gnu/store\")
241 => #f 242 => #f
242" 243"
243 (and (string-prefix? "/" file1) 244 (let loop ((file1 (string-tokenize file1 %not-slash))
244 (string-prefix? "/" file2) 245 (file2 (string-tokenize file2 %not-slash)))
245 (let loop ((file1 (string-tokenize file1 %not-slash)) 246 (match file1
246 (file2 (string-tokenize file2 %not-slash))) 247 (()
247 (match file1 248 #t)
248 (() 249 ((head1 tail1 ...)
249 #t) 250 (match file2
250 ((head1 tail1 ...) 251 ((head2 tail2 ...)
251 (match file2 252 (and (string=? head1 head2) (loop tail1 tail2)))
252 ((head2 tail2 ...) 253 (()
253 (and (string=? head1 head2) (loop tail1 tail2))) 254 #f))))))
254 (() 255
255 #f))))))) 256(define (file-name-depth file-name)
257 (length (string-tokenize file-name %not-slash)))
258
259(define (reduce-directories file-names)
260 "Eliminate entries in FILE-NAMES that are children of other entries in
261FILE-NAMES. This is for example useful when passing a list of files to GNU
262tar, which would otherwise descend into each directory passed and archive the
263duplicate files as hard links, which can be undesirable."
264 (let* ((file-names/sorted
265 ;; Ascending sort by file hierarchy depth, then by file name length.
266 (stable-sort (delete-duplicates file-names)
267 (lambda (f1 f2)
268 (let ((depth1 (file-name-depth f1))
269 (depth2 (file-name-depth f2)))
270 (if (= depth1 depth2)
271 (string< f1 f2)
272 (< depth1 depth2)))))))
273 (reverse (fold (lambda (file-name results)
274 (if (find (cut file-prefix? <> file-name) results)
275 results ;parent found -- skipping
276 (cons file-name results)))
277 '()
278 file-names/sorted))))
256 279
257(define* (file-system-device->string device #:key uuid-type) 280(define* (file-system-device->string device #:key uuid-type)
258 "Return the string representations of the DEVICE field of a <file-system> 281 "Return the string representations of the DEVICE field of a <file-system>
@@ -624,9 +647,6 @@ store is located, else #f."
624 s 647 s
625 (string-append "/" s))) 648 (string-append "/" s)))
626 649
627 (define (file-name-depth file-name)
628 (length (string-tokenize file-name %not-slash)))
629
630 (and-let* ((btrfs-subvolume-fs (filter btrfs-subvolume? file-systems)) 650 (and-let* ((btrfs-subvolume-fs (filter btrfs-subvolume? file-systems))
631 (btrfs-subvolume-fs* 651 (btrfs-subvolume-fs*
632 (sort btrfs-subvolume-fs 652 (sort btrfs-subvolume-fs
diff --git a/guix/scripts/pack.scm b/guix/scripts/pack.scm
index 952c1455bee..cee14441103 100644
--- a/guix/scripts/pack.scm
+++ b/guix/scripts/pack.scm
@@ -230,13 +230,15 @@ its source property."
230 `((guix build pack) 230 `((guix build pack)
231 (guix build utils) 231 (guix build utils)
232 (guix build union) 232 (guix build union)
233 (gnu build install)) 233 (gnu build install)
234 (gnu system file-systems))
234 #:select? import-module?) 235 #:select? import-module?)
235 #~(begin 236 #~(begin
236 (use-modules (guix build pack) 237 (use-modules (guix build pack)
237 (guix build utils) 238 (guix build utils)
238 ((guix build union) #:select (relative-file-name)) 239 ((guix build union) #:select (relative-file-name))
239 (gnu build install) 240 (gnu build install)
241 ((gnu system file-systems) #:select (reduce-directories))
240 (srfi srfi-1) 242 (srfi srfi-1)
241 (srfi srfi-26) 243 (srfi srfi-26)
242 (ice-9 match)) 244 (ice-9 match))
@@ -303,7 +305,7 @@ its source property."
303 305
304 ,(string-append "." (%store-directory)) 306 ,(string-append "." (%store-directory))
305 307
306 ,@(delete-duplicates 308 ,@(reduce-directories
307 (filter-map (match-lambda 309 (filter-map (match-lambda
308 (('directory directory) 310 (('directory directory)
309 (string-append "." directory)) 311 (string-append "." directory))
diff --git a/tests/file-systems.scm b/tests/file-systems.scm
index 7f7c373884f..80acb6d5b91 100644
--- a/tests/file-systems.scm
+++ b/tests/file-systems.scm
@@ -1,6 +1,6 @@
1;;; GNU Guix --- Functional package management for GNU 1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2015, 2017 Ludovic Courtès <ludo@gnu.org> 2;;; Copyright © 2015, 2017 Ludovic Courtès <ludo@gnu.org>
3;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com> 3;;; Copyright © 2020, 2021 Maxim Cournoyer <maxim.cournoyer@gmail.com>
4;;; 4;;;
5;;; This file is part of GNU Guix. 5;;; This file is part of GNU Guix.
6;;; 6;;;
@@ -50,6 +50,11 @@
50 (device "/foo") 50 (device "/foo")
51 (flags '(bind-mount read-only))))))))) 51 (flags '(bind-mount read-only)))))))))
52 52
53(test-equal "reduce-directories"
54 '("./opt/gnu/" "./opt/gnuism" "a/b/c")
55 (reduce-directories '("./opt/gnu/etc" "./opt/gnu/" "./opt/gnu/bin"
56 "./opt/gnu/lib/debug" "./opt/gnuism" "a/b/c" "a/b/c")))
57
53(test-assert "does not pull (guix config)" 58(test-assert "does not pull (guix config)"
54 ;; This module is meant both for the host side and "build side", so make 59 ;; This module is meant both for the host side and "build side", so make
55 ;; sure it doesn't pull in (guix config), which depends on the user's 60 ;; sure it doesn't pull in (guix config), which depends on the user's