digests.scm 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. ;;; Disarchive
  2. ;;; Copyright © 2020 Timothy Sample <samplet@ngyro.com>
  3. ;;;
  4. ;;; This file is part of Disarchive.
  5. ;;;
  6. ;;; Disarchive is free software: you can redistribute it and/or modify
  7. ;;; it under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation, either version 3 of the License, or
  9. ;;; (at your option) any later version.
  10. ;;;
  11. ;;; Disarchive is distributed in the hope that it will be useful,
  12. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with Disarchive. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (disarchive digests)
  19. #:use-module (disarchive git-hash)
  20. #:use-module (gcrypt base16)
  21. #:use-module (gcrypt hash)
  22. #:use-module (ice-9 match)
  23. #:use-module (srfi srfi-9)
  24. #:export (<digest>
  25. make-digest
  26. digest?
  27. digest-algorithm
  28. digest-value
  29. digest-algorithm-name
  30. digest->sexp
  31. sexp->digest
  32. digest->filename
  33. file-digest
  34. file-digest?))
  35. ;;; Commentary:
  36. ;;;
  37. ;;; This module provides a representation of digests (or hashes). A
  38. ;;; digest is a binary hash and the algorithm used to produce it.
  39. ;;;
  40. ;;; Code:
  41. (define-record-type <digest>
  42. (make-digest algorithm value)
  43. digest?
  44. (algorithm digest-algorithm)
  45. (value digest-value))
  46. (define digest-algorithm-name
  47. (compose hash-algorithm-name digest-algorithm))
  48. (define (digest->sexp digest)
  49. (match-let ((($ <digest> algorithm value) digest))
  50. `(,(hash-algorithm-name algorithm)
  51. ,(bytevector->base16-string value))))
  52. (define (sexp->digest sexp)
  53. (match sexp
  54. ((algorithm-symbol value-string)
  55. (let ((algorithm (lookup-hash-algorithm algorithm-symbol))
  56. (value (base16-string->bytevector value-string)))
  57. (unless algorithm
  58. (error "unknown digest algorithm" algorithm-symbol))
  59. (make-digest algorithm value)))))
  60. (define* (digest->filename digest #:optional (base ""))
  61. "Convert DIGEST into a filename (using its algorithm name as the
  62. directory name and its base16 hash as the base name). If BASE is
  63. set, prepend it with a delimiting slash to the resulting filename."
  64. (string-append (if (string-null? base)
  65. ""
  66. (string-append base "/"))
  67. (symbol->string (digest-algorithm-name digest))
  68. "/" (bytevector->base16-string (digest-value digest))))
  69. (define* (file-digest filename #:optional
  70. (algorithm (hash-algorithm sha256)))
  71. "Compute the digest of FILENAME using ALGORITHM. If ALGORITHM is
  72. unspecified, use SHA-256."
  73. (define hash
  74. (and=> (stat filename #f)
  75. (lambda (st)
  76. (case (stat:type st)
  77. ((regular)
  78. (file-hash algorithm filename))
  79. ((directory)
  80. (git-hash-directory filename algorithm))))))
  81. (and hash (make-digest algorithm hash)))
  82. (define (file-digest? filename digest)
  83. "Check if DIGEST matches the digest of FILENAME."
  84. (equal? digest (file-digest filename (digest-algorithm digest))))