cpio.scm 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2015 Ludovic Courtès <ludo@gnu.org>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; 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 GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (guix cpio)
  19. #:use-module ((guix build utils) #:select (dump-port))
  20. #:use-module (srfi srfi-9)
  21. #:use-module (srfi srfi-11)
  22. #:use-module (rnrs bytevectors)
  23. #:use-module (rnrs io ports)
  24. #:use-module (ice-9 match)
  25. #:export (cpio-header?
  26. make-cpio-header
  27. file->cpio-header
  28. file->cpio-header*
  29. special-file->cpio-header*
  30. write-cpio-header
  31. read-cpio-header
  32. write-cpio-archive))
  33. ;;; Commentary:
  34. ;;;
  35. ;;; This module implements the cpio "new ASCII" format, bit-for-bit identical
  36. ;;; to GNU cpio with the '-H newc' option.
  37. ;;;
  38. ;;; Code:
  39. ;; Values for 'mode', OR'd together.
  40. (define C_IRUSR #o000400)
  41. (define C_IWUSR #o000200)
  42. (define C_IXUSR #o000100)
  43. (define C_IRGRP #o000040)
  44. (define C_IWGRP #o000020)
  45. (define C_IXGRP #o000010)
  46. (define C_IROTH #o000004)
  47. (define C_IWOTH #o000002)
  48. (define C_IXOTH #o000001)
  49. (define C_ISUID #o004000)
  50. (define C_ISGID #o002000)
  51. (define C_ISVTX #o001000)
  52. (define C_FMT #o170000) ;bit mask
  53. (define C_ISBLK #o060000)
  54. (define C_ISCHR #o020000)
  55. (define C_ISDIR #o040000)
  56. (define C_ISFIFO #o010000)
  57. (define C_ISSOCK #o0140000)
  58. (define C_ISLNK #o0120000)
  59. (define C_ISCTG #o0110000)
  60. (define C_ISREG #o0100000)
  61. (define MAGIC
  62. ;; The "new" portable format with ASCII header, as produced by GNU cpio with
  63. ;; '-H newc'.
  64. (string->number "070701" 16))
  65. (define (read-header-field size port)
  66. (string->number (get-string-n port size) 16))
  67. (define (write-header-field value size port)
  68. (put-bytevector port
  69. (string->utf8
  70. (string-pad (string-upcase (number->string value 16))
  71. size #\0))))
  72. (define-syntax define-pack
  73. (syntax-rules ()
  74. ((_ type ctor pred write read (field-names field-sizes field-getters) ...)
  75. (begin
  76. (define-record-type type
  77. (ctor field-names ...)
  78. pred
  79. (field-names field-getters) ...)
  80. (define (read port)
  81. (set-port-encoding! port "ISO-8859-1")
  82. (ctor (read-header-field field-sizes port)
  83. ...))
  84. (define (write obj port)
  85. (let* ((size (+ field-sizes ...)))
  86. (match obj
  87. (($ type field-names ...)
  88. (write-header-field field-names field-sizes port)
  89. ...))))))))
  90. ;; cpio header in "new ASCII" format, without checksum.
  91. (define-pack <cpio-header>
  92. %make-cpio-header cpio-header?
  93. write-cpio-header read-cpio-header
  94. (magic 6 cpio-header-magic)
  95. (ino 8 cpio-header-inode)
  96. (mode 8 cpio-header-mode)
  97. (uid 8 cpio-header-uid)
  98. (gid 8 cpio-header-gid)
  99. (nlink 8 cpio-header-nlink)
  100. (mtime 8 cpio-header-mtime)
  101. (file-size 8 cpio-header-file-size)
  102. (dev-maj 8 cpio-header-device-major)
  103. (dev-min 8 cpio-header-device-minor)
  104. (rdev-maj 8 cpio-header-rdevice-major)
  105. (rdev-min 8 cpio-header-rdevice-minor)
  106. (name-size 8 cpio-header-name-size)
  107. (checksum 8 cpio-header-checksum)) ;0 for "newc" format
  108. (define* (make-cpio-header #:key
  109. (inode 0)
  110. (mode (logior C_ISREG C_IRUSR))
  111. (uid 0) (gid 0)
  112. (nlink 1) (mtime 0) (size 0)
  113. (dev 0) (rdev 0) (name-size 0))
  114. "Return a new cpio file header."
  115. (let-values (((major minor) (device->major+minor dev))
  116. ((rmajor rminor) (device->major+minor rdev)))
  117. (%make-cpio-header MAGIC
  118. inode mode uid gid
  119. nlink mtime
  120. (if (or (= C_ISLNK (logand mode C_FMT))
  121. (= C_ISREG (logand mode C_FMT)))
  122. size
  123. 0)
  124. major minor rmajor rminor
  125. (+ name-size 1) ;include trailing zero
  126. 0))) ;checksum
  127. (define (mode->type mode)
  128. "Given the number MODE, return a symbol representing the kind of file MODE
  129. denotes, similar to 'stat:type'."
  130. (let ((fmt (logand mode C_FMT)))
  131. (cond ((= C_ISREG fmt) 'regular)
  132. ((= C_ISDIR fmt) 'directory)
  133. ((= C_ISLNK fmt) 'symlink)
  134. ((= C_ISBLK fmt) 'block-special)
  135. ((= C_ISCHR fmt) 'char-special)
  136. (else
  137. (error "unsupported file type" mode)))))
  138. (define (device-number major minor) ;see <sys/sysmacros.h>
  139. "Return the device number for the device with MAJOR and MINOR, for use as
  140. the last argument of `mknod'."
  141. (+ (* major 256) minor))
  142. (define (device->major+minor device)
  143. "Return two values: the major and minor device numbers that make up DEVICE."
  144. (values (ash device -8)
  145. (logand device #xff)))
  146. (define* (file->cpio-header file #:optional (file-name file)
  147. #:key (stat lstat))
  148. "Return a cpio header corresponding to the info returned by STAT for FILE,
  149. using FILE-NAME as its file name."
  150. (let ((st (stat file)))
  151. (make-cpio-header #:inode (stat:ino st)
  152. #:mode (stat:mode st)
  153. #:uid (stat:uid st)
  154. #:gid (stat:gid st)
  155. #:nlink (stat:nlink st)
  156. #:mtime (stat:mtime st)
  157. #:size (stat:size st)
  158. #:dev (stat:dev st)
  159. #:rdev (stat:rdev st)
  160. #:name-size (string-length file-name))))
  161. (define* (file->cpio-header* file
  162. #:optional (file-name file)
  163. #:key (stat lstat))
  164. "Similar to 'file->cpio-header', but return a header with a zeroed
  165. modification time, inode number, UID/GID, etc. This allows archives to be
  166. produced in a deterministic fashion."
  167. (let ((st (stat file)))
  168. (make-cpio-header #:mode (stat:mode st)
  169. #:nlink (stat:nlink st)
  170. #:size (stat:size st)
  171. #:name-size (string-length file-name))))
  172. (define* (special-file->cpio-header* file
  173. device-type
  174. device-major
  175. device-minor
  176. permission-bits
  177. #:optional (file-name file))
  178. "Create a character or block device header.
  179. DEVICE-TYPE is either 'char-special or 'block-special.
  180. The number of hard links is assumed to be 1."
  181. (make-cpio-header #:mode (logior (match device-type
  182. ('block-special C_ISBLK)
  183. ('char-special C_ISCHR))
  184. permission-bits)
  185. #:nlink 1
  186. #:rdev (device-number device-major device-minor)
  187. #:name-size (string-length file-name)))
  188. (define %trailer
  189. "TRAILER!!!")
  190. (define %last-header
  191. ;; The header that marks the end of the archive.
  192. (make-cpio-header #:mode 0
  193. #:name-size (string-length %trailer)))
  194. (define* (write-cpio-archive files port
  195. #:key (file->header file->cpio-header))
  196. "Write to PORT a cpio archive in \"new ASCII\" format containing all of FILES.
  197. The archive written to PORT is intended to be bit-identical to what GNU cpio
  198. produces with the '-H newc' option."
  199. (define (write-padding offset port)
  200. (let ((padding (modulo (- 4 (modulo offset 4)) 4)))
  201. (put-bytevector port (make-bytevector padding))))
  202. (define (pad-block port)
  203. ;; Write padding to PORT such that we finish with a 512-byte block.
  204. ;; XXX: We rely on PORT's internal state, assuming it's a file port.
  205. (let* ((offset (seek port 0 SEEK_CUR))
  206. (padding (modulo (- 512 (modulo offset 512)) 512)))
  207. (put-bytevector port (make-bytevector padding))))
  208. (define (dump-file file)
  209. (let* ((header (file->header file))
  210. (size (cpio-header-file-size header)))
  211. (write-cpio-header header port)
  212. (put-bytevector port (string->utf8 file))
  213. (put-u8 port 0)
  214. ;; We're padding the header + following file name + trailing zero, and
  215. ;; the header is 110 byte long.
  216. (write-padding (+ 110 1 (string-length file)) port)
  217. (case (mode->type (cpio-header-mode header))
  218. ((regular)
  219. (call-with-input-file file
  220. (lambda (input)
  221. (dump-port input port))))
  222. ((symlink)
  223. (let ((target (readlink file)))
  224. (put-string port target)))
  225. ((directory)
  226. #t)
  227. ((block-special)
  228. #t)
  229. ((char-special)
  230. #t)
  231. (else
  232. (error "file type not supported")))
  233. ;; Pad the file content.
  234. (write-padding size port)))
  235. (set-port-encoding! port "ISO-8859-1")
  236. (for-each dump-file files)
  237. (write-cpio-header %last-header port)
  238. (put-bytevector port (string->utf8 %trailer))
  239. (write-padding (string-length %trailer) port)
  240. ;; Pad so the last block is 512-byte long.
  241. (pad-block port))
  242. ;;; cpio.scm ends here