writer.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  1. // Copyright 2011 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package zip
  5. import (
  6. "bufio"
  7. "encoding/binary"
  8. "errors"
  9. "hash"
  10. "hash/crc32"
  11. "io"
  12. "strings"
  13. "unicode/utf8"
  14. )
  15. var (
  16. errLongName = errors.New("zip: FileHeader.Name too long")
  17. errLongExtra = errors.New("zip: FileHeader.Extra too long")
  18. )
  19. type lastWriter interface {
  20. Close() error
  21. Closed() bool
  22. }
  23. // Writer implements a zip file writer.
  24. type Writer struct {
  25. cw *countWriter
  26. dir []*header
  27. last lastWriter
  28. closed bool
  29. compressors map[uint16]Compressor
  30. comment string
  31. // testHookCloseSizeOffset if non-nil is called with the size
  32. // of offset of the central directory at Close.
  33. testHookCloseSizeOffset func(size, offset uint64)
  34. }
  35. type header struct {
  36. *FileHeader
  37. offset uint64
  38. }
  39. // NewWriter returns a new Writer writing a zip file to w.
  40. func NewWriter(w io.Writer) *Writer {
  41. return &Writer{cw: &countWriter{w: bufio.NewWriter(w)}}
  42. }
  43. // SetOffset sets the offset of the beginning of the zip data within the
  44. // underlying writer. It should be used when the zip data is appended to an
  45. // existing file, such as a binary executable.
  46. // It must be called before any data is written.
  47. func (w *Writer) SetOffset(n int64) {
  48. if w.cw.count != 0 {
  49. panic("zip: SetOffset called after data was written")
  50. }
  51. w.cw.count = n
  52. }
  53. // Flush flushes any buffered data to the underlying writer.
  54. // Calling Flush is not normally necessary; calling Close is sufficient.
  55. func (w *Writer) Flush() error {
  56. return w.cw.w.(*bufio.Writer).Flush()
  57. }
  58. // SetComment sets the end-of-central-directory comment field.
  59. // It can only be called before Close.
  60. func (w *Writer) SetComment(comment string) error {
  61. if len(comment) > uint16max {
  62. return errors.New("zip: Writer.Comment too long")
  63. }
  64. w.comment = comment
  65. return nil
  66. }
  67. // Close finishes writing the zip file by writing the central directory.
  68. // It does not Close the underlying writer.
  69. func (w *Writer) Close() error {
  70. if w.last != nil && !w.last.Closed() {
  71. if err := w.last.Close(); err != nil {
  72. return err
  73. }
  74. w.last = nil
  75. }
  76. if w.closed {
  77. return errors.New("zip: writer closed twice")
  78. }
  79. w.closed = true
  80. // write central directory
  81. start := w.cw.count
  82. for _, h := range w.dir {
  83. var buf [directoryHeaderLen]byte
  84. b := writeBuf(buf[:])
  85. b.uint32(uint32(directoryHeaderSignature))
  86. b.uint16(h.CreatorVersion)
  87. b.uint16(h.ReaderVersion)
  88. b.uint16(h.Flags)
  89. b.uint16(h.Method)
  90. b.uint16(h.ModifiedTime)
  91. b.uint16(h.ModifiedDate)
  92. b.uint32(h.CRC32)
  93. if h.isZip64() || h.offset >= uint32max {
  94. // the file needs a zip64 header. store maxint in both
  95. // 32 bit size fields (and offset later) to signal that the
  96. // zip64 extra header should be used.
  97. b.uint32(uint32max) // compressed size
  98. b.uint32(uint32max) // uncompressed size
  99. // append a zip64 extra block to Extra
  100. var buf [28]byte // 2x uint16 + 3x uint64
  101. eb := writeBuf(buf[:])
  102. eb.uint16(zip64ExtraID)
  103. eb.uint16(24) // size = 3x uint64
  104. eb.uint64(h.UncompressedSize64)
  105. eb.uint64(h.CompressedSize64)
  106. eb.uint64(h.offset)
  107. h.Extra = append(h.Extra, buf[:]...)
  108. } else {
  109. b.uint32(h.CompressedSize)
  110. b.uint32(h.UncompressedSize)
  111. }
  112. b.uint16(uint16(len(h.Name)))
  113. b.uint16(uint16(len(h.Extra)))
  114. b.uint16(uint16(len(h.Comment)))
  115. b = b[4:] // skip disk number start and internal file attr (2x uint16)
  116. b.uint32(h.ExternalAttrs)
  117. if h.offset > uint32max {
  118. b.uint32(uint32max)
  119. } else {
  120. b.uint32(uint32(h.offset))
  121. }
  122. if _, err := w.cw.Write(buf[:]); err != nil {
  123. return err
  124. }
  125. if _, err := io.WriteString(w.cw, h.Name); err != nil {
  126. return err
  127. }
  128. if _, err := w.cw.Write(h.Extra); err != nil {
  129. return err
  130. }
  131. if _, err := io.WriteString(w.cw, h.Comment); err != nil {
  132. return err
  133. }
  134. }
  135. end := w.cw.count
  136. records := uint64(len(w.dir))
  137. size := uint64(end - start)
  138. offset := uint64(start)
  139. if f := w.testHookCloseSizeOffset; f != nil {
  140. f(size, offset)
  141. }
  142. if records >= uint16max || size >= uint32max || offset >= uint32max {
  143. var buf [directory64EndLen + directory64LocLen]byte
  144. b := writeBuf(buf[:])
  145. // zip64 end of central directory record
  146. b.uint32(directory64EndSignature)
  147. b.uint64(directory64EndLen - 12) // length minus signature (uint32) and length fields (uint64)
  148. b.uint16(zipVersion45) // version made by
  149. b.uint16(zipVersion45) // version needed to extract
  150. b.uint32(0) // number of this disk
  151. b.uint32(0) // number of the disk with the start of the central directory
  152. b.uint64(records) // total number of entries in the central directory on this disk
  153. b.uint64(records) // total number of entries in the central directory
  154. b.uint64(size) // size of the central directory
  155. b.uint64(offset) // offset of start of central directory with respect to the starting disk number
  156. // zip64 end of central directory locator
  157. b.uint32(directory64LocSignature)
  158. b.uint32(0) // number of the disk with the start of the zip64 end of central directory
  159. b.uint64(uint64(end)) // relative offset of the zip64 end of central directory record
  160. b.uint32(1) // total number of disks
  161. if _, err := w.cw.Write(buf[:]); err != nil {
  162. return err
  163. }
  164. // store max values in the regular end record to signal
  165. // that the zip64 values should be used instead
  166. records = uint16max
  167. size = uint32max
  168. offset = uint32max
  169. }
  170. // write end record
  171. var buf [directoryEndLen]byte
  172. b := writeBuf(buf[:])
  173. b.uint32(uint32(directoryEndSignature))
  174. b = b[4:] // skip over disk number and first disk number (2x uint16)
  175. b.uint16(uint16(records)) // number of entries this disk
  176. b.uint16(uint16(records)) // number of entries total
  177. b.uint32(uint32(size)) // size of directory
  178. b.uint32(uint32(offset)) // start of directory
  179. b.uint16(uint16(len(w.comment))) // byte size of EOCD comment
  180. if _, err := w.cw.Write(buf[:]); err != nil {
  181. return err
  182. }
  183. if _, err := io.WriteString(w.cw, w.comment); err != nil {
  184. return err
  185. }
  186. return w.cw.w.(*bufio.Writer).Flush()
  187. }
  188. // Create adds a file to the zip file using the provided name.
  189. // It returns a Writer to which the file contents should be written.
  190. // The file contents will be compressed using the Deflate method.
  191. // The name must be a relative path: it must not start with a drive
  192. // letter (e.g. C:) or leading slash, and only forward slashes are
  193. // allowed. To create a directory instead of a file, add a trailing
  194. // slash to the name.
  195. // The file's contents must be written to the io.Writer before the next
  196. // call to Create, CreateHeader, or Close.
  197. func (w *Writer) Create(name string) (io.Writer, error) {
  198. header := &FileHeader{
  199. Name: name,
  200. Method: Deflate,
  201. }
  202. return w.CreateHeader(header)
  203. }
  204. // Copy will copy raw content from input file.
  205. // Optionally a different name can be given to the new file.
  206. func (w *Writer) Copy(name string, src *File) error {
  207. header := src.FileHeader
  208. if name != "" {
  209. header.Name = name
  210. }
  211. raw, err := src.OpenRaw()
  212. if err != nil {
  213. return err
  214. }
  215. wr, err := w.CreateHeaderRaw(&header)
  216. if err != nil {
  217. return err
  218. }
  219. _, err = io.Copy(wr, raw)
  220. return err
  221. }
  222. // detectUTF8 reports whether s is a valid UTF-8 string, and whether the string
  223. // must be considered UTF-8 encoding (i.e., not compatible with CP-437, ASCII,
  224. // or any other common encoding).
  225. func detectUTF8(s string) (valid, require bool) {
  226. for i := 0; i < len(s); {
  227. r, size := utf8.DecodeRuneInString(s[i:])
  228. i += size
  229. // Officially, ZIP uses CP-437, but many readers use the system's
  230. // local character encoding. Most encoding are compatible with a large
  231. // subset of CP-437, which itself is ASCII-like.
  232. //
  233. // Forbid 0x7e and 0x5c since EUC-KR and Shift-JIS replace those
  234. // characters with localized currency and overline characters.
  235. if r < 0x20 || r > 0x7d || r == 0x5c {
  236. if !utf8.ValidRune(r) || (r == utf8.RuneError && size == 1) {
  237. return false, false
  238. }
  239. require = true
  240. }
  241. }
  242. return true, require
  243. }
  244. // CreateHeader adds a file to the zip archive using the provided FileHeader
  245. // for the file metadata. Writer takes ownership of fh and may mutate
  246. // its fields. The caller must not modify fh after calling CreateHeader.
  247. //
  248. // This returns a Writer to which the file contents should be written.
  249. // The file's contents must be written to the io.Writer before the next
  250. // call to Create, Copy, CreateHeader, CreateHeaderRaw or Close.
  251. func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
  252. if w.last != nil && !w.last.Closed() {
  253. if err := w.last.Close(); err != nil {
  254. return nil, err
  255. }
  256. }
  257. if len(w.dir) > 0 && w.dir[len(w.dir)-1].FileHeader == fh {
  258. // See https://golang.org/issue/11144 confusion.
  259. return nil, errors.New("archive/zip: invalid duplicate FileHeader")
  260. }
  261. // The ZIP format has a sad state of affairs regarding character encoding.
  262. // Officially, the name and comment fields are supposed to be encoded
  263. // in CP-437 (which is mostly compatible with ASCII), unless the UTF-8
  264. // flag bit is set. However, there are several problems:
  265. //
  266. // * Many ZIP readers still do not support UTF-8.
  267. // * If the UTF-8 flag is cleared, several readers simply interpret the
  268. // name and comment fields as whatever the local system encoding is.
  269. //
  270. // In order to avoid breaking readers without UTF-8 support,
  271. // we avoid setting the UTF-8 flag if the strings are CP-437 compatible.
  272. // However, if the strings require multibyte UTF-8 encoding and is a
  273. // valid UTF-8 string, then we set the UTF-8 bit.
  274. //
  275. // For the case, where the user explicitly wants to specify the encoding
  276. // as UTF-8, they will need to set the flag bit themselves.
  277. utf8Valid1, utf8Require1 := detectUTF8(fh.Name)
  278. utf8Valid2, utf8Require2 := detectUTF8(fh.Comment)
  279. switch {
  280. case fh.NonUTF8:
  281. fh.Flags &^= 0x800
  282. case (utf8Require1 || utf8Require2) && (utf8Valid1 && utf8Valid2):
  283. fh.Flags |= 0x800
  284. }
  285. fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte
  286. fh.ReaderVersion = zipVersion20
  287. // If Modified is set, this takes precedence over MS-DOS timestamp fields.
  288. if !fh.Modified.IsZero() {
  289. // Contrary to the FileHeader.SetModTime method, we intentionally
  290. // do not convert to UTC, because we assume the user intends to encode
  291. // the date using the specified timezone. A user may want this control
  292. // because many legacy ZIP readers interpret the timestamp according
  293. // to the local timezone.
  294. //
  295. // The timezone is only non-UTC if a user directly sets the Modified
  296. // field directly themselves. All other approaches sets UTC.
  297. fh.ModifiedDate, fh.ModifiedTime = timeToMsDosTime(fh.Modified)
  298. // Use "extended timestamp" format since this is what Info-ZIP uses.
  299. // Nearly every major ZIP implementation uses a different format,
  300. // but at least most seem to be able to understand the other formats.
  301. //
  302. // This format happens to be identical for both local and central header
  303. // if modification time is the only timestamp being encoded.
  304. var mbuf [9]byte // 2*SizeOf(uint16) + SizeOf(uint8) + SizeOf(uint32)
  305. mt := uint32(fh.Modified.Unix())
  306. eb := writeBuf(mbuf[:])
  307. eb.uint16(extTimeExtraID)
  308. eb.uint16(5) // Size: SizeOf(uint8) + SizeOf(uint32)
  309. eb.uint8(1) // Flags: ModTime
  310. eb.uint32(mt) // ModTime
  311. fh.Extra = append(fh.Extra, mbuf[:]...)
  312. }
  313. var ow io.Writer
  314. h := &header{
  315. FileHeader: fh,
  316. offset: uint64(w.cw.count),
  317. }
  318. if strings.HasSuffix(fh.Name, "/") {
  319. // Set the compression method to Store to ensure data length is truly zero,
  320. // which the writeHeader method always encodes for the size fields.
  321. // This is necessary as most compression formats have non-zero lengths
  322. // even when compressing an empty string.
  323. fh.Method = Store
  324. fh.Flags &^= 0x8 // we will not write a data descriptor
  325. // Explicitly clear sizes as they have no meaning for directories.
  326. fh.CompressedSize = 0
  327. fh.CompressedSize64 = 0
  328. fh.UncompressedSize = 0
  329. fh.UncompressedSize64 = 0
  330. ow = dirWriter{}
  331. w.last = nil
  332. } else {
  333. fh.Flags |= 0x8 // we will write a data descriptor
  334. fw := &fileWriter{
  335. zipw: w.cw,
  336. compCount: &countWriter{w: w.cw},
  337. crc32: crc32.NewIEEE(),
  338. }
  339. comp := w.compressor(fh.Method)
  340. if comp == nil {
  341. return nil, ErrAlgorithm
  342. }
  343. var err error
  344. fw.comp, err = comp(fw.compCount)
  345. if err != nil {
  346. return nil, err
  347. }
  348. fw.rawCount = &countWriter{w: fw.comp}
  349. fw.header = h
  350. ow = fw
  351. w.last = fw
  352. }
  353. w.dir = append(w.dir, h)
  354. if err := writeHeader(w.cw, fh); err != nil {
  355. return nil, err
  356. }
  357. // If we're creating a directory, fw is nil.
  358. return ow, nil
  359. }
  360. // CreateHeaderRaw adds a file to the zip archive using the provided FileHeader
  361. // for the file metadata. Writer takes ownership of fh and may mutate
  362. // its fields. The caller must not modify fh after calling CreateHeaderRaw.
  363. //
  364. // This returns a Writer to which the compressed file contents should be written.
  365. // The file's contents must be written to the io.Writer before the next
  366. // call to Create, Copy, CreateHeader, CreateHeaderRaw or Close.
  367. //
  368. // Using this requires knowledge of populating the FileHeader correctly.
  369. // Generally using the Copy() function is recommended.
  370. func (w *Writer) CreateHeaderRaw(fh *FileHeader) (io.Writer, error) {
  371. if w.last != nil && !w.last.Closed() {
  372. if err := w.last.Close(); err != nil {
  373. return nil, err
  374. }
  375. }
  376. if len(w.dir) > 0 && w.dir[len(w.dir)-1].FileHeader == fh {
  377. // See https://golang.org/issue/11144 confusion.
  378. return nil, errors.New("archive/zip: invalid duplicate FileHeader")
  379. }
  380. // The ZIP format has a sad state of affairs regarding character encoding.
  381. // Officially, the name and comment fields are supposed to be encoded
  382. // in CP-437 (which is mostly compatible with ASCII), unless the UTF-8
  383. // flag bit is set. However, there are several problems:
  384. //
  385. // * Many ZIP readers still do not support UTF-8.
  386. // * If the UTF-8 flag is cleared, several readers simply interpret the
  387. // name and comment fields as whatever the local system encoding is.
  388. //
  389. // In order to avoid breaking readers without UTF-8 support,
  390. // we avoid setting the UTF-8 flag if the strings are CP-437 compatible.
  391. // However, if the strings require multibyte UTF-8 encoding and is a
  392. // valid UTF-8 string, then we set the UTF-8 bit.
  393. //
  394. // For the case, where the user explicitly wants to specify the encoding
  395. // as UTF-8, they will need to set the flag bit themselves.
  396. utf8Valid1, utf8Require1 := detectUTF8(fh.Name)
  397. utf8Valid2, utf8Require2 := detectUTF8(fh.Comment)
  398. switch {
  399. case fh.NonUTF8:
  400. fh.Flags &^= 0x800
  401. case (utf8Require1 || utf8Require2) && (utf8Valid1 && utf8Valid2):
  402. fh.Flags |= 0x800
  403. }
  404. fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte
  405. fh.ReaderVersion = zipVersion20
  406. // If Modified is set, this takes precedence over MS-DOS timestamp fields.
  407. if !fh.Modified.IsZero() {
  408. // Contrary to the FileHeader.SetModTime method, we intentionally
  409. // do not convert to UTC, because we assume the user intends to encode
  410. // the date using the specified timezone. A user may want this control
  411. // because many legacy ZIP readers interpret the timestamp according
  412. // to the local timezone.
  413. //
  414. // The timezone is only non-UTC if a user directly sets the Modified
  415. // field directly themselves. All other approaches sets UTC.
  416. fh.ModifiedDate, fh.ModifiedTime = timeToMsDosTime(fh.Modified)
  417. // Use "extended timestamp" format since this is what Info-ZIP uses.
  418. // Nearly every major ZIP implementation uses a different format,
  419. // but at least most seem to be able to understand the other formats.
  420. //
  421. // This format happens to be identical for both local and central header
  422. // if modification time is the only timestamp being encoded.
  423. var mbuf [9]byte // 2*SizeOf(uint16) + SizeOf(uint8) + SizeOf(uint32)
  424. mt := uint32(fh.Modified.Unix())
  425. eb := writeBuf(mbuf[:])
  426. eb.uint16(extTimeExtraID)
  427. eb.uint16(5) // Size: SizeOf(uint8) + SizeOf(uint32)
  428. eb.uint8(1) // Flags: ModTime
  429. eb.uint32(mt) // ModTime
  430. fh.Extra = append(fh.Extra, mbuf[:]...)
  431. }
  432. var ow io.Writer
  433. h := &header{
  434. FileHeader: fh,
  435. offset: uint64(w.cw.count),
  436. }
  437. if strings.HasSuffix(fh.Name, "/") {
  438. // Set the compression method to Store to ensure data length is truly zero,
  439. // which the writeHeader method always encodes for the size fields.
  440. // This is necessary as most compression formats have non-zero lengths
  441. // even when compressing an empty string.
  442. fh.Method = Store
  443. fh.Flags &^= 0x8 // we will not write a data descriptor
  444. // Explicitly clear sizes as they have no meaning for directories.
  445. fh.CompressedSize = 0
  446. fh.CompressedSize64 = 0
  447. fh.UncompressedSize = 0
  448. fh.UncompressedSize64 = 0
  449. ow = dirWriter{}
  450. w.last = nil
  451. } else {
  452. fh.Flags |= 0x8 // we will write a data descriptor
  453. fw := &rawWriter{
  454. header: h,
  455. zipw: w.cw,
  456. rawCount: &countWriter{w: w.cw},
  457. }
  458. ow = fw
  459. w.last = fw
  460. }
  461. w.dir = append(w.dir, h)
  462. if err := writeHeader(w.cw, fh); err != nil {
  463. return nil, err
  464. }
  465. // If we're creating a directory, fw is nil.
  466. return ow, nil
  467. }
  468. func writeHeader(w io.Writer, h *FileHeader) error {
  469. const maxUint16 = 1<<16 - 1
  470. if len(h.Name) > maxUint16 {
  471. return errLongName
  472. }
  473. if len(h.Extra) > maxUint16 {
  474. return errLongExtra
  475. }
  476. var buf [fileHeaderLen]byte
  477. b := writeBuf(buf[:])
  478. b.uint32(uint32(fileHeaderSignature))
  479. b.uint16(h.ReaderVersion)
  480. b.uint16(h.Flags)
  481. b.uint16(h.Method)
  482. b.uint16(h.ModifiedTime)
  483. b.uint16(h.ModifiedDate)
  484. b.uint32(0) // since we are writing a data descriptor crc32,
  485. b.uint32(0) // compressed size,
  486. b.uint32(0) // and uncompressed size should be zero
  487. b.uint16(uint16(len(h.Name)))
  488. b.uint16(uint16(len(h.Extra)))
  489. if _, err := w.Write(buf[:]); err != nil {
  490. return err
  491. }
  492. if _, err := io.WriteString(w, h.Name); err != nil {
  493. return err
  494. }
  495. _, err := w.Write(h.Extra)
  496. return err
  497. }
  498. // RegisterCompressor registers or overrides a custom compressor for a specific
  499. // method ID. If a compressor for a given method is not found, Writer will
  500. // default to looking up the compressor at the package level.
  501. func (w *Writer) RegisterCompressor(method uint16, comp Compressor) {
  502. if w.compressors == nil {
  503. w.compressors = make(map[uint16]Compressor)
  504. }
  505. w.compressors[method] = comp
  506. }
  507. func (w *Writer) compressor(method uint16) Compressor {
  508. comp := w.compressors[method]
  509. if comp == nil {
  510. comp = compressor(method)
  511. }
  512. return comp
  513. }
  514. type dirWriter struct{}
  515. func (dirWriter) Write(b []byte) (int, error) {
  516. if len(b) == 0 {
  517. return 0, nil
  518. }
  519. return 0, errors.New("zip: write to directory")
  520. }
  521. type fileWriter struct {
  522. *header
  523. zipw io.Writer
  524. rawCount *countWriter
  525. comp io.WriteCloser
  526. compCount *countWriter
  527. crc32 hash.Hash32
  528. closed bool
  529. }
  530. func (w *fileWriter) Write(p []byte) (int, error) {
  531. if w.closed {
  532. return 0, errors.New("zip: write to closed file")
  533. }
  534. w.crc32.Write(p)
  535. return w.rawCount.Write(p)
  536. }
  537. func (w *fileWriter) Closed() bool {
  538. return w.closed
  539. }
  540. func (w *fileWriter) Close() error {
  541. if w.closed {
  542. return errors.New("zip: file closed twice")
  543. }
  544. w.closed = true
  545. if err := w.comp.Close(); err != nil {
  546. return err
  547. }
  548. // update FileHeader
  549. fh := w.header.FileHeader
  550. fh.CRC32 = w.crc32.Sum32()
  551. fh.CompressedSize64 = uint64(w.compCount.count)
  552. fh.UncompressedSize64 = uint64(w.rawCount.count)
  553. if fh.isZip64() {
  554. fh.CompressedSize = uint32max
  555. fh.UncompressedSize = uint32max
  556. fh.ReaderVersion = zipVersion45 // requires 4.5 - File uses ZIP64 format extensions
  557. } else {
  558. fh.CompressedSize = uint32(fh.CompressedSize64)
  559. fh.UncompressedSize = uint32(fh.UncompressedSize64)
  560. }
  561. // Write data descriptor. This is more complicated than one would
  562. // think, see e.g. comments in zipfile.c:putextended() and
  563. // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588.
  564. // The approach here is to write 8 byte sizes if needed without
  565. // adding a zip64 extra in the local header (too late anyway).
  566. var buf []byte
  567. if fh.isZip64() {
  568. buf = make([]byte, dataDescriptor64Len)
  569. } else {
  570. buf = make([]byte, dataDescriptorLen)
  571. }
  572. b := writeBuf(buf)
  573. b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X
  574. b.uint32(fh.CRC32)
  575. if fh.isZip64() {
  576. b.uint64(fh.CompressedSize64)
  577. b.uint64(fh.UncompressedSize64)
  578. } else {
  579. b.uint32(fh.CompressedSize)
  580. b.uint32(fh.UncompressedSize)
  581. }
  582. _, err := w.zipw.Write(buf)
  583. return err
  584. }
  585. type rawWriter struct {
  586. *header
  587. zipw io.Writer
  588. rawCount *countWriter
  589. closed bool
  590. }
  591. func (w *rawWriter) Write(p []byte) (int, error) {
  592. if w.closed {
  593. return 0, errors.New("zip: write to closed file")
  594. }
  595. return w.rawCount.Write(p)
  596. }
  597. func (w *rawWriter) Closed() bool {
  598. return w.closed
  599. }
  600. func (w *rawWriter) Close() error {
  601. if w.closed {
  602. return errors.New("zip: file closed twice")
  603. }
  604. w.closed = true
  605. fh := w.FileHeader
  606. fh.CompressedSize64 = uint64(w.rawCount.count)
  607. // Write data descriptor. This is more complicated than one would
  608. // think, see e.g. comments in zipfile.c:putextended() and
  609. // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588.
  610. // The approach here is to write 8 byte sizes if needed without
  611. // adding a zip64 extra in the local header (too late anyway).
  612. var buf []byte
  613. if fh.isZip64() {
  614. buf = make([]byte, dataDescriptor64Len)
  615. } else {
  616. buf = make([]byte, dataDescriptorLen)
  617. }
  618. b := writeBuf(buf)
  619. b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X
  620. b.uint32(fh.CRC32)
  621. if fh.isZip64() {
  622. b.uint64(fh.CompressedSize64)
  623. b.uint64(fh.UncompressedSize64)
  624. } else {
  625. b.uint32(fh.CompressedSize)
  626. b.uint32(fh.UncompressedSize)
  627. }
  628. _, err := w.zipw.Write(buf)
  629. return err
  630. }
  631. type countWriter struct {
  632. w io.Writer
  633. count int64
  634. }
  635. func (w *countWriter) Write(p []byte) (int, error) {
  636. n, err := w.w.Write(p)
  637. w.count += int64(n)
  638. return n, err
  639. }
  640. type nopCloser struct {
  641. io.Writer
  642. }
  643. func (w nopCloser) Close() error {
  644. return nil
  645. }
  646. type writeBuf []byte
  647. func (b *writeBuf) uint8(v uint8) {
  648. (*b)[0] = v
  649. *b = (*b)[1:]
  650. }
  651. func (b *writeBuf) uint16(v uint16) {
  652. binary.LittleEndian.PutUint16(*b, v)
  653. *b = (*b)[2:]
  654. }
  655. func (b *writeBuf) uint32(v uint32) {
  656. binary.LittleEndian.PutUint32(*b, v)
  657. *b = (*b)[4:]
  658. }
  659. func (b *writeBuf) uint64(v uint64) {
  660. binary.LittleEndian.PutUint64(*b, v)
  661. *b = (*b)[8:]
  662. }