unzip-translator.lisp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. (defpackage :unzip-translator
  2. (:use :cl :hurd-common :mach
  3. :hurd :hurd-translator
  4. :hurd-tree-translator
  5. :hurd-streams
  6. :flexi-streams
  7. :trivial-gray-streams
  8. :zip))
  9. (in-package :unzip-translator)
  10. ;;
  11. ;; This is a simple unzip translator.
  12. ;; Right now it supports file and directory listing.
  13. ;;
  14. (defconstant +seq-cache-size+ 10 "Number of reads before disposing the extract array sequence.")
  15. (defvar *must-save* nil)
  16. (defclass zip-translator (tree-translator)
  17. ((timestamp :initform nil
  18. :accessor timestamp
  19. :initarg :timestamp)
  20. (underlying-stream :initform nil
  21. :accessor underlying-stream
  22. :initarg :stream))
  23. (:documentation "Zip translator."))
  24. (defclass dirty-entry ()
  25. ((dirty :initform nil
  26. :accessor dirty)))
  27. (defclass zip-entry (entry dirty-entry)
  28. ((name :initarg :name
  29. :accessor name)
  30. (entry :initarg :entry
  31. :accessor entry
  32. :initform nil
  33. :documentation "The zip entry associated with this file.")
  34. (to-write :initform nil
  35. :initarg :to-write
  36. :accessor to-write)
  37. (data-sequence :initarg :data
  38. :initform nil
  39. :accessor data
  40. :documentation "The zip data associated with this file.")
  41. (number-reads :initform +seq-cache-size+
  42. :accessor number-reads
  43. :documentation "Count of reads."))
  44. (:documentation "Extends entry with a zip-entry."))
  45. (defmethod to-write-p ((entry zip-entry))
  46. (to-write entry))
  47. (defmethod activate-write ((entry zip-entry))
  48. (setf (to-write entry) t
  49. (entry entry) nil
  50. *must-save* t))
  51. (defmethod name ((foo dir-entry))
  52. (declare (ignore foo))
  53. nil)
  54. (defclass zip-dir-entry (dir-entry dirty-entry)
  55. ((name :initarg :name
  56. :initform nil
  57. :accessor name)))
  58. (define-callback chown-file zip-translator
  59. (node user uid gid)
  60. (declare (ignore node user uid gid))
  61. nil)
  62. (define-callback chmod-file zip-translator
  63. (node user mode)
  64. (declare (ignore node user mode))
  65. nil)
  66. (define-callback create-hard-link zip-translator
  67. (dir user file name)
  68. (declare (ignore dir user file name))
  69. nil)
  70. (define-callback allow-link-p zip-translator
  71. (node user)
  72. (declare (ignore node user))
  73. nil)
  74. (define-callback create-symlink zip-translator
  75. (node user target)
  76. (declare (ignore node user target))
  77. nil)
  78. (defun %get-entry-sequence (entry)
  79. (let ((data-stream
  80. (make-in-memory-output-stream
  81. :element-type '(unsigned-byte 8))))
  82. (zipfile-entry-contents entry data-stream)
  83. (get-output-stream-sequence data-stream)))
  84. (defun extract-node (node)
  85. (setf (data node) (%get-entry-sequence (entry node))))
  86. (define-callback read-file zip-translator
  87. (node user start amount stream)
  88. (unless (has-access-p node user :read)
  89. (return-from read-file :permission-denied))
  90. (when (is-dir-p (stat node))
  91. (return-from read-file :is-a-directory))
  92. (unless (data node)
  93. (extract-node node))
  94. (unless (to-write-p node)
  95. (decf (number-reads node)))
  96. (let* ((size (stat-get (stat node) 'st-size))
  97. (size-res (- size start)))
  98. (cond
  99. ((not (plusp size-res)) t)
  100. (t
  101. (let* ((total (min size-res amount))
  102. (end (+ start total)))
  103. (write-sequence (subseq (data node) start end)
  104. stream)
  105. t)))))
  106. (defun create-adjustable-array ()
  107. (make-array 0
  108. :fill-pointer 0
  109. :adjustable t
  110. :element-type '(unsigned-byte 8)))
  111. (define-callback create-file zip-translator
  112. (node user filename mode)
  113. (unless (has-access-p node user :write)
  114. (return-from create-file nil))
  115. (let ((entry (make-instance 'zip-entry
  116. :name filename
  117. :to-write t
  118. :data (create-adjustable-array)
  119. :stat (make-stat (stat node)
  120. :mode mode
  121. :size 0
  122. :type :reg)
  123. :parent node)))
  124. (setf *must-save* t)
  125. (add-entry node entry filename)
  126. entry))
  127. (defun ensure-write-data (node &optional new-size)
  128. (cond
  129. ((and (null (data node))
  130. (or (null new-size)
  131. (plusp new-size)))
  132. (extract-node node))
  133. ((null (data node))
  134. (setf (data node) (create-adjustable-array))))
  135. (activate-write node))
  136. (define-callback file-change-size zip-translator
  137. (node user new-size)
  138. (when (is-dir-p (stat node))
  139. (return-from file-change-size :is-a-directory))
  140. (when (is-owner-p node user)
  141. (ensure-write-data node new-size)
  142. (adjust-array (data node) new-size :fill-pointer t)
  143. (setf (stat-get (stat node) 'st-size) new-size)
  144. t))
  145. (defun %read-sequence (stream amount)
  146. (let ((arr (make-array amount
  147. :element-type '(unsigned-byte 8))))
  148. (read-sequence arr stream)
  149. arr))
  150. (define-callback write-file zip-translator
  151. (node user offset stream amount)
  152. (unless (has-access-p node user :write)
  153. (return-from write-file nil))
  154. (when (is-dir-p (stat node))
  155. (return-from write-file :is-a-directory))
  156. (ensure-write-data node)
  157. (let* ((size (stat-get (stat node) 'st-size))
  158. (arr (%read-sequence stream amount))
  159. (final-size (max (+ amount offset) size)))
  160. (unless (= final-size size)
  161. (adjust-array (data node)
  162. final-size
  163. :fill-pointer t))
  164. (replace (data node) arr :start1 offset)
  165. ; Update stat size.
  166. (setf (stat-get (stat node) 'st-size) final-size)
  167. t))
  168. (define-callback file-rename zip-translator
  169. (user old-dir old-name new-dir new-name)
  170. (declare (ignore user old-dir old-name))
  171. (when (call-next-method)
  172. (let ((new-entry (get-entry new-dir new-name)))
  173. (when new-entry
  174. (setf (name new-entry) new-name)
  175. t))))
  176. (define-callback create-directory zip-translator
  177. (node user name mode)
  178. (when (not-permitted-entries-p name)
  179. (return-from create-directory nil))
  180. (unless (is-owner-p node user)
  181. (return-from create-directory nil))
  182. (let ((old (get-entry node name)))
  183. (cond
  184. (old nil)
  185. (t
  186. (setf *must-save* t)
  187. (add-entry node
  188. (make-instance 'zip-dir-entry
  189. :stat (make-stat (stat node) :mode mode)
  190. :name name
  191. :parent node)
  192. name)))))
  193. (define-callback refresh-node zip-translator
  194. (node user)
  195. (declare (ignore node user))
  196. (with-accessors ((underlying-node underlying-node)) translator
  197. (let* ((stat (io-stat underlying-node))
  198. (new-timestamp (stat-get stat 'st-mtime)))
  199. (when (time-value-newer-p new-timestamp (timestamp translator))
  200. ; Mark every node as un-visited.
  201. (iterate-entries-deep (root translator)
  202. (lambda (name node)
  203. (declare (ignore name))
  204. (setf (dirty node) nil)
  205. t))
  206. (with-accessors ((underlying-stream underlying-stream) (root root)) translator
  207. (setf (stream-file-position underlying-stream) :start)
  208. (let ((zip-handle (open-zipfile underlying-stream)))
  209. (do-zipfile-entries (name entry zip-handle)
  210. (update-zip-file root (split-path name) entry))))
  211. ; Now remove the nodes we have not visited during the update.
  212. (iterate-entries-deep (root translator)
  213. (lambda (name node)
  214. (cond
  215. ((dirty node) t) ; Keep going down there
  216. (t
  217. (remove-dir-entry (parent node)
  218. name)
  219. nil))))
  220. (setf (timestamp translator) new-timestamp)))))
  221. (define-callback report-no-users zip-translator
  222. ((node zip-entry))
  223. (unless (to-write-p node)
  224. ; We don't need this anymore
  225. (when (or (data node)
  226. (<= (number-reads node)))
  227. (setf (data node) nil)
  228. (setf (number-reads node) +seq-cache-size+))))
  229. (defun %create-zip-file (parent entry name)
  230. "Create a new zip entry."
  231. (let ((stat (make-stat (stat parent)
  232. :size (zipfile-entry-size entry)
  233. :type :reg)))
  234. (clear-perms stat :exec)
  235. (make-instance 'zip-entry
  236. :stat stat
  237. :parent parent
  238. :name name
  239. :entry entry)))
  240. (defun %create-zip-dir (parent name)
  241. "Create a new zip directory."
  242. (make-instance 'zip-dir-entry
  243. :stat (make-stat (stat parent))
  244. :name name
  245. :parent parent))
  246. (defun %update-file (node zip-entry)
  247. ; Reset any extracted data.
  248. (unless (to-write-p node)
  249. (setf (data node) nil
  250. (stat-get (stat node) 'st-size) (zipfile-entry-size zip-entry)
  251. (entry node) zip-entry
  252. (number-reads node) +seq-cache-size+)))
  253. (defun update-zip-file (node name zip-entry)
  254. (let* ((name-rest (rest name))
  255. (this-name (first name))
  256. (final-p (null name-rest)))
  257. (if (string= this-name "")
  258. (return-from update-zip-file nil))
  259. (let ((entry (get-entry node this-name)))
  260. (cond
  261. (entry
  262. (cond
  263. (final-p
  264. (cond
  265. ((typep entry 'zip-dir-entry)
  266. (remove-dir-entry node this-name)
  267. (setf entry (add-entry node
  268. (%create-zip-file node zip-entry this-name)
  269. this-name)))
  270. (t
  271. (%update-file entry zip-entry))))
  272. (t
  273. (when (typep entry 'zip-entry)
  274. (remove-dir-entry node this-name)
  275. (setf entry (%create-zip-dir node this-name)))
  276. (update-zip-file entry name-rest zip-entry))))
  277. (t
  278. (setf entry (add-entry node
  279. (if final-p
  280. (%create-zip-file node zip-entry this-name)
  281. (%create-zip-dir node this-name))
  282. this-name))
  283. (unless final-p
  284. (update-zip-file entry name-rest zip-entry))))
  285. (setf (dirty entry) t))))
  286. (defun add-zip-file (node name zip-entry)
  287. "Recursively using name as a path list add into 'node' a new 'zip-entry'."
  288. (let* ((name-rest (rest name))
  289. (this-name (first name))
  290. (final-p (null name-rest)))
  291. (if (string= this-name "")
  292. ;; Last node was a directory and it is already created.
  293. (return-from add-zip-file nil))
  294. (let ((entry (get-entry node this-name)))
  295. (cond
  296. (entry
  297. (unless final-p
  298. (add-zip-file entry name-rest zip-entry)))
  299. (t
  300. (let ((new-dir (add-entry node
  301. (if final-p
  302. (%create-zip-file node zip-entry this-name)
  303. (%create-zip-dir node this-name))
  304. this-name)))
  305. (unless final-p
  306. (add-zip-file new-dir name-rest zip-entry))))))))
  307. (defmethod zip-stream-file-length ((stream hurd-input-stream))
  308. (hurd-stream-file-length stream))
  309. (defconstant +unix-to-universal-time+ 2208988800)
  310. (defun unix-to-universal-time (secs)
  311. (+ secs +unix-to-universal-time+))
  312. (defun get-full-path (node)
  313. (let ((my-name (name node)))
  314. (when my-name
  315. (let* ((parent (parent node))
  316. (parent-path (get-full-path parent)))
  317. (if parent-path
  318. (concatenate-string parent-path "/" my-name)
  319. my-name)))))
  320. (defun get-write-date (node)
  321. (unix-to-universal-time (time-value-seconds (stat-get (stat node) 'st-mtime))))
  322. (defgeneric write-zip-node (node writer))
  323. (defmethod write-zip-node ((node zip-entry) writer)
  324. (let ((path (get-full-path node))
  325. (node-stream (make-in-memory-input-stream (data node))))
  326. (write-zipentry writer path node-stream
  327. :file-write-date (get-write-date node))))
  328. (defmethod write-zip-node ((node zip-dir-entry) writer)
  329. (let ((path (concatenate-string (get-full-path node) "/")))
  330. (write-zipentry writer
  331. path
  332. (make-concatenated-stream)
  333. :file-write-date (get-write-date node))))
  334. (define-callback shutdown zip-translator
  335. ()
  336. (when *must-save*
  337. (warn "Saving zip file...")
  338. ; Extract everything first
  339. (iterate-entries-deep (root translator)
  340. (lambda (name node)
  341. (declare (ignore name))
  342. (when (typep node 'zip-entry)
  343. (ensure-write-data node))
  344. t))
  345. (let ((s (make-hurd-output-stream (underlying-node translator))))
  346. (file-set-size (underlying-node translator) 0)
  347. (let ((writer (make-zipfile-writer s)))
  348. (iterate-entries-deep (root translator)
  349. (lambda (name node)
  350. (declare (ignore name))
  351. (write-zip-node node writer)
  352. t))
  353. (zip-write-central-directory writer)
  354. (force-output s)))))
  355. (define-callback fill-root-node zip-translator
  356. ((node dir-entry))
  357. "Add all entries found on the zip file."
  358. (let ((zip-handle (open-zipfile (underlying-stream translator))))
  359. (do-zipfile-entries (name entry zip-handle)
  360. (add-zip-file node (split-path name) entry))))
  361. (define-callback make-root-node zip-translator
  362. (underlying-node underlying-stat)
  363. (setf (timestamp translator) (stat-get underlying-stat 'st-mtime)
  364. (underlying-stream translator) (make-hurd-input-stream underlying-node))
  365. (call-next-method))
  366. (defun main ()
  367. (run-translator (make-instance 'zip-translator
  368. :name "zip-translator"
  369. :version (list 0 1 0))
  370. :flags '(:notrans :read :write)))
  371. (main)