2011-07-10 11:30:16 +10:00
|
|
|
// Copyright 2010 The Go Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Package zip provides support for reading and writing ZIP archives.
|
|
|
|
|
|
2017-01-22 02:46:25 +03:00
|
|
|
See: https://www.pkware.com/appnote
|
2011-07-10 11:30:16 +10:00
|
|
|
|
2012-08-22 11:05:24 +10:00
|
|
|
This package does not support disk spanning.
|
|
|
|
|
|
|
|
|
|
A note about ZIP64:
|
|
|
|
|
|
|
|
|
|
To be backwards compatible the FileHeader has both 32 and 64 bit Size
|
|
|
|
|
fields. The 64 bit fields will always contain the correct value and
|
|
|
|
|
for normal archives both fields will be the same. For files requiring
|
|
|
|
|
the ZIP64 format the 32 bit fields will be 0xffffffff and the 64 bit
|
|
|
|
|
fields must be used instead.
|
2011-07-10 11:30:16 +10:00
|
|
|
*/
|
2010-09-30 11:59:46 +10:00
|
|
|
package zip
|
|
|
|
|
|
2011-11-30 12:01:46 -05:00
|
|
|
import (
|
2020-07-07 13:49:21 -04:00
|
|
|
"io/fs"
|
archive/tar,zip: implement the os.FileInfo interface correctly.
This is potentially an API-breaking change, but it is an important bug fix.
The CL https://golang.org/cl/7305072/ added stuff to make
the tar file look more like a file system internally, including providing an
implementation of os.FileInfo for the file headers within the archive.
But the code is incorrect because FileInfo.Name is supposed to return
the base name only; this implementation returns the full path. A round
trip test added in the same shows this in action, as the slashes are
preserved as we create a header using the local implementation of
FileInfo.
The CL here changes the behavior of the tar (and zip) FileInfo to honor
the Go spec for that interface. It also clarifies that the FileInfoHeader
function, which takes a FileInfo as an argument, will therefore create
a header with only the base name of the file recorded, and that
subsequent adjustment may be necessary.
There may be code out there that depends on the broken behavior.
We can call out the risk in the release notes.
Fixes #6180.
R=golang-dev, dsymonds, adg, bradfitz
CC=golang-dev
https://golang.org/cl/13118043
2013-08-21 08:29:41 +10:00
|
|
|
"path"
|
2011-11-30 12:01:46 -05:00
|
|
|
"time"
|
|
|
|
|
)
|
2011-07-10 11:30:16 +10:00
|
|
|
|
|
|
|
|
// Compression methods.
|
|
|
|
|
const (
|
2017-11-15 10:24:21 +00:00
|
|
|
Store uint16 = 0 // no compression
|
|
|
|
|
Deflate uint16 = 8 // DEFLATE compressed
|
2011-07-10 11:30:16 +10:00
|
|
|
)
|
|
|
|
|
|
2010-09-30 11:59:46 +10:00
|
|
|
const (
|
|
|
|
|
fileHeaderSignature = 0x04034b50
|
|
|
|
|
directoryHeaderSignature = 0x02014b50
|
|
|
|
|
directoryEndSignature = 0x06054b50
|
2012-08-22 11:05:24 +10:00
|
|
|
directory64LocSignature = 0x07064b50
|
|
|
|
|
directory64EndSignature = 0x06064b50
|
2012-03-09 14:12:02 -08:00
|
|
|
dataDescriptorSignature = 0x08074b50 // de-facto standard; required by OS X Finder
|
|
|
|
|
fileHeaderLen = 30 // + filename + extra
|
|
|
|
|
directoryHeaderLen = 46 // + filename + extra + comment
|
|
|
|
|
directoryEndLen = 22 // + comment
|
|
|
|
|
dataDescriptorLen = 16 // four uint32: descriptor signature, crc32, compressed size, size
|
2012-08-22 11:05:24 +10:00
|
|
|
dataDescriptor64Len = 24 // descriptor with 8 byte sizes
|
|
|
|
|
directory64LocLen = 20 //
|
|
|
|
|
directory64EndLen = 56 // + extra
|
2011-09-25 20:48:03 -03:00
|
|
|
|
archive/zip: add FileHeader.Modified field
The ModifiedTime and ModifiedDate fields are not expressive enough
for many of the time extensions that have since been added to ZIP,
nor are they easy to access since they in a legacy MS-DOS format,
and must be set and retrieved via the SetModTime and ModTime methods.
Instead, we add new field Modified of time.Time type that contains
all of the previous information and more.
Support for extended timestamps have been attempted before, but the
change was reverted because it provided no ability for the user to
specify the timezone of the legacy MS-DOS fields.
Technically the old API did not either, but users were manually offsetting
the timestamp to achieve the same effect.
The Writer now writes the legacy timestamps according to the timezone
of the FileHeader.Modified field. When the Modified field is set via
the SetModTime method, it is in UTC, which preserves the old behavior.
The Reader attempts to determine the timezone if both the legacy
and extended timestamps are present since it can compute the delta
between the two values.
Since Modified is a superset of the information in ModifiedTime and ModifiedDate,
we mark ModifiedTime, ModifiedDate, ModTime, and SetModTime as deprecated.
Fixes #18359
Change-Id: I29c6bc0a62908095d02740df3e6902f50d3152f1
Reviewed-on: https://go-review.googlesource.com/74970
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2017-08-28 12:07:58 -07:00
|
|
|
// Constants for the first byte in CreatorVersion.
|
2011-12-12 15:22:55 -05:00
|
|
|
creatorFAT = 0
|
|
|
|
|
creatorUnix = 3
|
|
|
|
|
creatorNTFS = 11
|
|
|
|
|
creatorVFAT = 14
|
|
|
|
|
creatorMacOSX = 19
|
2012-08-22 11:05:24 +10:00
|
|
|
|
archive/zip: add FileHeader.Modified field
The ModifiedTime and ModifiedDate fields are not expressive enough
for many of the time extensions that have since been added to ZIP,
nor are they easy to access since they in a legacy MS-DOS format,
and must be set and retrieved via the SetModTime and ModTime methods.
Instead, we add new field Modified of time.Time type that contains
all of the previous information and more.
Support for extended timestamps have been attempted before, but the
change was reverted because it provided no ability for the user to
specify the timezone of the legacy MS-DOS fields.
Technically the old API did not either, but users were manually offsetting
the timestamp to achieve the same effect.
The Writer now writes the legacy timestamps according to the timezone
of the FileHeader.Modified field. When the Modified field is set via
the SetModTime method, it is in UTC, which preserves the old behavior.
The Reader attempts to determine the timezone if both the legacy
and extended timestamps are present since it can compute the delta
between the two values.
Since Modified is a superset of the information in ModifiedTime and ModifiedDate,
we mark ModifiedTime, ModifiedDate, ModTime, and SetModTime as deprecated.
Fixes #18359
Change-Id: I29c6bc0a62908095d02740df3e6902f50d3152f1
Reviewed-on: https://go-review.googlesource.com/74970
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2017-08-28 12:07:58 -07:00
|
|
|
// Version numbers.
|
2012-08-22 11:05:24 +10:00
|
|
|
zipVersion20 = 20 // 2.0
|
|
|
|
|
zipVersion45 = 45 // 4.5 (reads and writes zip64 archives)
|
|
|
|
|
|
archive/zip: add FileHeader.Modified field
The ModifiedTime and ModifiedDate fields are not expressive enough
for many of the time extensions that have since been added to ZIP,
nor are they easy to access since they in a legacy MS-DOS format,
and must be set and retrieved via the SetModTime and ModTime methods.
Instead, we add new field Modified of time.Time type that contains
all of the previous information and more.
Support for extended timestamps have been attempted before, but the
change was reverted because it provided no ability for the user to
specify the timezone of the legacy MS-DOS fields.
Technically the old API did not either, but users were manually offsetting
the timestamp to achieve the same effect.
The Writer now writes the legacy timestamps according to the timezone
of the FileHeader.Modified field. When the Modified field is set via
the SetModTime method, it is in UTC, which preserves the old behavior.
The Reader attempts to determine the timezone if both the legacy
and extended timestamps are present since it can compute the delta
between the two values.
Since Modified is a superset of the information in ModifiedTime and ModifiedDate,
we mark ModifiedTime, ModifiedDate, ModTime, and SetModTime as deprecated.
Fixes #18359
Change-Id: I29c6bc0a62908095d02740df3e6902f50d3152f1
Reviewed-on: https://go-review.googlesource.com/74970
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2017-08-28 12:07:58 -07:00
|
|
|
// Limits for non zip64 files.
|
2012-08-22 11:05:24 +10:00
|
|
|
uint16max = (1 << 16) - 1
|
|
|
|
|
uint32max = (1 << 32) - 1
|
|
|
|
|
|
archive/zip: add FileHeader.Modified field
The ModifiedTime and ModifiedDate fields are not expressive enough
for many of the time extensions that have since been added to ZIP,
nor are they easy to access since they in a legacy MS-DOS format,
and must be set and retrieved via the SetModTime and ModTime methods.
Instead, we add new field Modified of time.Time type that contains
all of the previous information and more.
Support for extended timestamps have been attempted before, but the
change was reverted because it provided no ability for the user to
specify the timezone of the legacy MS-DOS fields.
Technically the old API did not either, but users were manually offsetting
the timestamp to achieve the same effect.
The Writer now writes the legacy timestamps according to the timezone
of the FileHeader.Modified field. When the Modified field is set via
the SetModTime method, it is in UTC, which preserves the old behavior.
The Reader attempts to determine the timezone if both the legacy
and extended timestamps are present since it can compute the delta
between the two values.
Since Modified is a superset of the information in ModifiedTime and ModifiedDate,
we mark ModifiedTime, ModifiedDate, ModTime, and SetModTime as deprecated.
Fixes #18359
Change-Id: I29c6bc0a62908095d02740df3e6902f50d3152f1
Reviewed-on: https://go-review.googlesource.com/74970
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2017-08-28 12:07:58 -07:00
|
|
|
// Extra header IDs.
|
|
|
|
|
//
|
|
|
|
|
// IDs 0..31 are reserved for official use by PKWARE.
|
|
|
|
|
// IDs above that range are defined by third-party vendors.
|
|
|
|
|
// Since ZIP lacked high precision timestamps (nor a official specification
|
|
|
|
|
// of the timezone used for the date fields), many competing extra fields
|
|
|
|
|
// have been invented. Pervasive use effectively makes them "official".
|
|
|
|
|
//
|
|
|
|
|
// See http://mdfs.net/Docs/Comp/Archiving/Zip/ExtraField
|
|
|
|
|
zip64ExtraID = 0x0001 // Zip64 extended information
|
|
|
|
|
ntfsExtraID = 0x000a // NTFS
|
|
|
|
|
unixExtraID = 0x000d // UNIX
|
|
|
|
|
extTimeExtraID = 0x5455 // Extended timestamp
|
|
|
|
|
infoZipUnixExtraID = 0x5855 // Info-ZIP Unix extension
|
2010-09-30 11:59:46 +10:00
|
|
|
)
|
|
|
|
|
|
2013-04-17 13:25:12 -07:00
|
|
|
// FileHeader describes a file within a zip file.
|
|
|
|
|
// See the zip spec for details.
|
2010-09-30 11:59:46 +10:00
|
|
|
type FileHeader struct {
|
2013-04-17 13:25:12 -07:00
|
|
|
// Name is the name of the file.
|
2018-06-12 19:33:23 +00:00
|
|
|
//
|
|
|
|
|
// It must be a relative path, not start with a drive letter (such as "C:"),
|
2018-02-25 16:34:35 +02:00
|
|
|
// and must use forward slashes instead of back slashes. A trailing slash
|
|
|
|
|
// indicates that this file is a directory and should have no data.
|
2018-06-12 19:33:23 +00:00
|
|
|
//
|
|
|
|
|
// When reading zip files, the Name field is populated from
|
|
|
|
|
// the zip file directly and is not validated for correctness.
|
|
|
|
|
// It is the caller's responsibility to sanitize it as
|
|
|
|
|
// appropriate, including canonicalizing slash directions,
|
|
|
|
|
// validating that paths are relative, and preventing path
|
|
|
|
|
// traversal through filenames ("../../../").
|
2013-04-17 13:25:12 -07:00
|
|
|
Name string
|
|
|
|
|
|
archive/zip: add FileHeader.NonUTF8 field
The NonUTF8 field provides users with a way to explictly tell the
ZIP writer to avoid setting the UTF-8 flag.
This is necessary because many readers:
1) (Still) do not support UTF-8
2) And use the local system encoding instead
Thus, even though character encodings other than CP-437 and UTF-8
are not officially supported by the ZIP specification, pragmatically
the world has permitted use of them.
When a non-standard encoding is used, it is the user's responsibility
to ensure that the target system is expecting the encoding used
(e.g., producing a ZIP file you know is used on a Chinese version of Windows).
We adjust the detectUTF8 function to account for Shift-JIS and EUC-KR
not being identical to ASCII for two characters.
We don't need an API for users to explicitly specify that they are encoding
with UTF-8 since all single byte characters are compatible with all other
common encodings (Windows-1256, Windows-1252, Windows-1251, Windows-1250,
IEC-8859, EUC-KR, KOI8-R, Latin-1, Shift-JIS, GB-2312, GBK) except for
the non-printable characters and the backslash character (all of which
are invalid characters in a path name anyways).
Fixes #10741
Change-Id: I9004542d1d522c9137973f1b6e2b623fa54dfd66
Reviewed-on: https://go-review.googlesource.com/75592
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2017-11-02 13:53:16 -07:00
|
|
|
// Comment is any arbitrary user-defined string shorter than 64KiB.
|
|
|
|
|
Comment string
|
|
|
|
|
|
|
|
|
|
// NonUTF8 indicates that Name and Comment are not encoded in UTF-8.
|
|
|
|
|
//
|
|
|
|
|
// By specification, the only other encoding permitted should be CP-437,
|
|
|
|
|
// but historically many ZIP readers interpret Name and Comment as whatever
|
|
|
|
|
// the system's local character encoding happens to be.
|
|
|
|
|
//
|
|
|
|
|
// This flag should only be set if the user intends to encode a non-portable
|
|
|
|
|
// ZIP file for a specific localized region. Otherwise, the Writer
|
|
|
|
|
// automatically sets the ZIP format's UTF-8 flag for valid UTF-8 strings.
|
|
|
|
|
NonUTF8 bool
|
|
|
|
|
|
archive/zip: add FileHeader.Modified field
The ModifiedTime and ModifiedDate fields are not expressive enough
for many of the time extensions that have since been added to ZIP,
nor are they easy to access since they in a legacy MS-DOS format,
and must be set and retrieved via the SetModTime and ModTime methods.
Instead, we add new field Modified of time.Time type that contains
all of the previous information and more.
Support for extended timestamps have been attempted before, but the
change was reverted because it provided no ability for the user to
specify the timezone of the legacy MS-DOS fields.
Technically the old API did not either, but users were manually offsetting
the timestamp to achieve the same effect.
The Writer now writes the legacy timestamps according to the timezone
of the FileHeader.Modified field. When the Modified field is set via
the SetModTime method, it is in UTC, which preserves the old behavior.
The Reader attempts to determine the timezone if both the legacy
and extended timestamps are present since it can compute the delta
between the two values.
Since Modified is a superset of the information in ModifiedTime and ModifiedDate,
we mark ModifiedTime, ModifiedDate, ModTime, and SetModTime as deprecated.
Fixes #18359
Change-Id: I29c6bc0a62908095d02740df3e6902f50d3152f1
Reviewed-on: https://go-review.googlesource.com/74970
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2017-08-28 12:07:58 -07:00
|
|
|
CreatorVersion uint16
|
|
|
|
|
ReaderVersion uint16
|
|
|
|
|
Flags uint16
|
2017-11-15 10:24:21 +00:00
|
|
|
|
|
|
|
|
// Method is the compression method. If zero, Store is used.
|
|
|
|
|
Method uint16
|
archive/zip: add FileHeader.Modified field
The ModifiedTime and ModifiedDate fields are not expressive enough
for many of the time extensions that have since been added to ZIP,
nor are they easy to access since they in a legacy MS-DOS format,
and must be set and retrieved via the SetModTime and ModTime methods.
Instead, we add new field Modified of time.Time type that contains
all of the previous information and more.
Support for extended timestamps have been attempted before, but the
change was reverted because it provided no ability for the user to
specify the timezone of the legacy MS-DOS fields.
Technically the old API did not either, but users were manually offsetting
the timestamp to achieve the same effect.
The Writer now writes the legacy timestamps according to the timezone
of the FileHeader.Modified field. When the Modified field is set via
the SetModTime method, it is in UTC, which preserves the old behavior.
The Reader attempts to determine the timezone if both the legacy
and extended timestamps are present since it can compute the delta
between the two values.
Since Modified is a superset of the information in ModifiedTime and ModifiedDate,
we mark ModifiedTime, ModifiedDate, ModTime, and SetModTime as deprecated.
Fixes #18359
Change-Id: I29c6bc0a62908095d02740df3e6902f50d3152f1
Reviewed-on: https://go-review.googlesource.com/74970
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2017-08-28 12:07:58 -07:00
|
|
|
|
|
|
|
|
// Modified is the modified time of the file.
|
|
|
|
|
//
|
|
|
|
|
// When reading, an extended timestamp is preferred over the legacy MS-DOS
|
|
|
|
|
// date field, and the offset between the times is used as the timezone.
|
|
|
|
|
// If only the MS-DOS date is present, the timezone is assumed to be UTC.
|
|
|
|
|
//
|
|
|
|
|
// When writing, an extended timestamp (which is timezone-agnostic) is
|
|
|
|
|
// always emitted. The legacy MS-DOS date field is encoded according to the
|
|
|
|
|
// location of the Modified time.
|
|
|
|
|
Modified time.Time
|
|
|
|
|
ModifiedTime uint16 // Deprecated: Legacy MS-DOS date; use Modified instead.
|
|
|
|
|
ModifiedDate uint16 // Deprecated: Legacy MS-DOS time; use Modified instead.
|
|
|
|
|
|
2012-08-22 11:05:24 +10:00
|
|
|
CRC32 uint32
|
2015-05-18 15:50:00 -04:00
|
|
|
CompressedSize uint32 // Deprecated: Use CompressedSize64 instead.
|
|
|
|
|
UncompressedSize uint32 // Deprecated: Use UncompressedSize64 instead.
|
2012-08-22 11:05:24 +10:00
|
|
|
CompressedSize64 uint64
|
|
|
|
|
UncompressedSize64 uint64
|
|
|
|
|
Extra []byte
|
|
|
|
|
ExternalAttrs uint32 // Meaning depends on CreatorVersion
|
2010-09-30 11:59:46 +10:00
|
|
|
}
|
|
|
|
|
|
2020-07-07 13:49:21 -04:00
|
|
|
// FileInfo returns an fs.FileInfo for the FileHeader.
|
|
|
|
|
func (h *FileHeader) FileInfo() fs.FileInfo {
|
2012-01-30 11:58:49 -08:00
|
|
|
return headerFileInfo{h}
|
2012-01-26 15:31:09 -08:00
|
|
|
}
|
|
|
|
|
|
2020-07-07 13:49:21 -04:00
|
|
|
// headerFileInfo implements fs.FileInfo.
|
2012-01-26 15:31:09 -08:00
|
|
|
type headerFileInfo struct {
|
|
|
|
|
fh *FileHeader
|
|
|
|
|
}
|
|
|
|
|
|
archive/tar,zip: implement the os.FileInfo interface correctly.
This is potentially an API-breaking change, but it is an important bug fix.
The CL https://golang.org/cl/7305072/ added stuff to make
the tar file look more like a file system internally, including providing an
implementation of os.FileInfo for the file headers within the archive.
But the code is incorrect because FileInfo.Name is supposed to return
the base name only; this implementation returns the full path. A round
trip test added in the same shows this in action, as the slashes are
preserved as we create a header using the local implementation of
FileInfo.
The CL here changes the behavior of the tar (and zip) FileInfo to honor
the Go spec for that interface. It also clarifies that the FileInfoHeader
function, which takes a FileInfo as an argument, will therefore create
a header with only the base name of the file recorded, and that
subsequent adjustment may be necessary.
There may be code out there that depends on the broken behavior.
We can call out the risk in the release notes.
Fixes #6180.
R=golang-dev, dsymonds, adg, bradfitz
CC=golang-dev
https://golang.org/cl/13118043
2013-08-21 08:29:41 +10:00
|
|
|
func (fi headerFileInfo) Name() string { return path.Base(fi.fh.Name) }
|
2012-08-22 11:05:24 +10:00
|
|
|
func (fi headerFileInfo) Size() int64 {
|
|
|
|
|
if fi.fh.UncompressedSize64 > 0 {
|
|
|
|
|
return int64(fi.fh.UncompressedSize64)
|
|
|
|
|
}
|
|
|
|
|
return int64(fi.fh.UncompressedSize)
|
|
|
|
|
}
|
2019-04-08 07:31:59 +00:00
|
|
|
func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
|
|
|
|
|
func (fi headerFileInfo) ModTime() time.Time {
|
|
|
|
|
if fi.fh.Modified.IsZero() {
|
|
|
|
|
return fi.fh.ModTime()
|
|
|
|
|
}
|
|
|
|
|
return fi.fh.Modified.UTC()
|
|
|
|
|
}
|
2020-07-07 13:49:21 -04:00
|
|
|
func (fi headerFileInfo) Mode() fs.FileMode { return fi.fh.Mode() }
|
2020-07-06 11:56:19 -04:00
|
|
|
func (fi headerFileInfo) Type() fs.FileMode { return fi.fh.Mode().Type() }
|
2019-04-08 07:31:59 +00:00
|
|
|
func (fi headerFileInfo) Sys() interface{} { return fi.fh }
|
2012-01-26 15:31:09 -08:00
|
|
|
|
2020-07-06 11:56:19 -04:00
|
|
|
func (fi headerFileInfo) Info() (fs.FileInfo, error) { return fi, nil }
|
|
|
|
|
|
2012-01-26 15:31:09 -08:00
|
|
|
// FileInfoHeader creates a partially-populated FileHeader from an
|
2020-07-07 13:49:21 -04:00
|
|
|
// fs.FileInfo.
|
|
|
|
|
// Because fs.FileInfo's Name method returns only the base name of
|
archive/tar,zip: implement the os.FileInfo interface correctly.
This is potentially an API-breaking change, but it is an important bug fix.
The CL https://golang.org/cl/7305072/ added stuff to make
the tar file look more like a file system internally, including providing an
implementation of os.FileInfo for the file headers within the archive.
But the code is incorrect because FileInfo.Name is supposed to return
the base name only; this implementation returns the full path. A round
trip test added in the same shows this in action, as the slashes are
preserved as we create a header using the local implementation of
FileInfo.
The CL here changes the behavior of the tar (and zip) FileInfo to honor
the Go spec for that interface. It also clarifies that the FileInfoHeader
function, which takes a FileInfo as an argument, will therefore create
a header with only the base name of the file recorded, and that
subsequent adjustment may be necessary.
There may be code out there that depends on the broken behavior.
We can call out the risk in the release notes.
Fixes #6180.
R=golang-dev, dsymonds, adg, bradfitz
CC=golang-dev
https://golang.org/cl/13118043
2013-08-21 08:29:41 +10:00
|
|
|
// the file it describes, it may be necessary to modify the Name field
|
|
|
|
|
// of the returned header to provide the full path name of the file.
|
2017-11-15 10:24:21 +00:00
|
|
|
// If compression is desired, callers should set the FileHeader.Method
|
|
|
|
|
// field; it is unset by default.
|
2020-07-07 13:49:21 -04:00
|
|
|
func FileInfoHeader(fi fs.FileInfo) (*FileHeader, error) {
|
2012-01-26 15:31:09 -08:00
|
|
|
size := fi.Size()
|
|
|
|
|
fh := &FileHeader{
|
2012-08-22 11:05:24 +10:00
|
|
|
Name: fi.Name(),
|
|
|
|
|
UncompressedSize64: uint64(size),
|
2012-01-26 15:31:09 -08:00
|
|
|
}
|
|
|
|
|
fh.SetModTime(fi.ModTime())
|
|
|
|
|
fh.SetMode(fi.Mode())
|
2012-08-22 11:05:24 +10:00
|
|
|
if fh.UncompressedSize64 > uint32max {
|
|
|
|
|
fh.UncompressedSize = uint32max
|
|
|
|
|
} else {
|
|
|
|
|
fh.UncompressedSize = uint32(fh.UncompressedSize64)
|
|
|
|
|
}
|
2012-01-26 15:31:09 -08:00
|
|
|
return fh, nil
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-30 11:59:46 +10:00
|
|
|
type directoryEnd struct {
|
2012-08-22 11:05:24 +10:00
|
|
|
diskNbr uint32 // unused
|
|
|
|
|
dirDiskNbr uint32 // unused
|
|
|
|
|
dirRecordsThisDisk uint64 // unused
|
|
|
|
|
directoryRecords uint64
|
|
|
|
|
directorySize uint64
|
|
|
|
|
directoryOffset uint64 // relative to file
|
2010-09-30 11:59:46 +10:00
|
|
|
commentLen uint16
|
|
|
|
|
comment string
|
|
|
|
|
}
|
2011-07-10 11:30:16 +10:00
|
|
|
|
archive/zip: add FileHeader.Modified field
The ModifiedTime and ModifiedDate fields are not expressive enough
for many of the time extensions that have since been added to ZIP,
nor are they easy to access since they in a legacy MS-DOS format,
and must be set and retrieved via the SetModTime and ModTime methods.
Instead, we add new field Modified of time.Time type that contains
all of the previous information and more.
Support for extended timestamps have been attempted before, but the
change was reverted because it provided no ability for the user to
specify the timezone of the legacy MS-DOS fields.
Technically the old API did not either, but users were manually offsetting
the timestamp to achieve the same effect.
The Writer now writes the legacy timestamps according to the timezone
of the FileHeader.Modified field. When the Modified field is set via
the SetModTime method, it is in UTC, which preserves the old behavior.
The Reader attempts to determine the timezone if both the legacy
and extended timestamps are present since it can compute the delta
between the two values.
Since Modified is a superset of the information in ModifiedTime and ModifiedDate,
we mark ModifiedTime, ModifiedDate, ModTime, and SetModTime as deprecated.
Fixes #18359
Change-Id: I29c6bc0a62908095d02740df3e6902f50d3152f1
Reviewed-on: https://go-review.googlesource.com/74970
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2017-08-28 12:07:58 -07:00
|
|
|
// timeZone returns a *time.Location based on the provided offset.
|
|
|
|
|
// If the offset is non-sensible, then this uses an offset of zero.
|
|
|
|
|
func timeZone(offset time.Duration) *time.Location {
|
|
|
|
|
const (
|
|
|
|
|
minOffset = -12 * time.Hour // E.g., Baker island at -12:00
|
|
|
|
|
maxOffset = +14 * time.Hour // E.g., Line island at +14:00
|
|
|
|
|
offsetAlias = 15 * time.Minute // E.g., Nepal at +5:45
|
|
|
|
|
)
|
|
|
|
|
offset = offset.Round(offsetAlias)
|
|
|
|
|
if offset < minOffset || maxOffset < offset {
|
|
|
|
|
offset = 0
|
|
|
|
|
}
|
|
|
|
|
return time.FixedZone("", int(offset/time.Second))
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-18 20:30:44 -07:00
|
|
|
// msDosTimeToTime converts an MS-DOS date and time into a time.Time.
|
|
|
|
|
// The resolution is 2s.
|
2018-06-01 17:29:59 -03:00
|
|
|
// See: https://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
|
2011-07-18 20:30:44 -07:00
|
|
|
func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
|
2011-11-30 12:01:46 -05:00
|
|
|
return time.Date(
|
2011-07-18 20:30:44 -07:00
|
|
|
// date bits 0-4: day of month; 5-8: month; 9-15: years since 1980
|
2011-11-30 12:01:46 -05:00
|
|
|
int(dosDate>>9+1980),
|
|
|
|
|
time.Month(dosDate>>5&0xf),
|
|
|
|
|
int(dosDate&0x1f),
|
2011-07-18 20:30:44 -07:00
|
|
|
|
|
|
|
|
// time bits 0-4: second/2; 5-10: minute; 11-15: hour
|
2011-11-30 12:01:46 -05:00
|
|
|
int(dosTime>>11),
|
|
|
|
|
int(dosTime>>5&0x3f),
|
|
|
|
|
int(dosTime&0x1f*2),
|
|
|
|
|
0, // nanoseconds
|
|
|
|
|
|
|
|
|
|
time.UTC,
|
|
|
|
|
)
|
2011-07-18 20:30:44 -07:00
|
|
|
}
|
|
|
|
|
|
2011-12-19 14:59:41 +11:00
|
|
|
// timeToMsDosTime converts a time.Time to an MS-DOS date and time.
|
|
|
|
|
// The resolution is 2s.
|
2018-06-01 17:29:59 -03:00
|
|
|
// See: https://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx
|
2011-12-19 14:59:41 +11:00
|
|
|
func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
|
|
|
|
|
fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
|
|
|
|
|
fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-15 12:38:26 -08:00
|
|
|
// ModTime returns the modification time in UTC using the legacy
|
|
|
|
|
// ModifiedDate and ModifiedTime fields.
|
archive/zip: add FileHeader.Modified field
The ModifiedTime and ModifiedDate fields are not expressive enough
for many of the time extensions that have since been added to ZIP,
nor are they easy to access since they in a legacy MS-DOS format,
and must be set and retrieved via the SetModTime and ModTime methods.
Instead, we add new field Modified of time.Time type that contains
all of the previous information and more.
Support for extended timestamps have been attempted before, but the
change was reverted because it provided no ability for the user to
specify the timezone of the legacy MS-DOS fields.
Technically the old API did not either, but users were manually offsetting
the timestamp to achieve the same effect.
The Writer now writes the legacy timestamps according to the timezone
of the FileHeader.Modified field. When the Modified field is set via
the SetModTime method, it is in UTC, which preserves the old behavior.
The Reader attempts to determine the timezone if both the legacy
and extended timestamps are present since it can compute the delta
between the two values.
Since Modified is a superset of the information in ModifiedTime and ModifiedDate,
we mark ModifiedTime, ModifiedDate, ModTime, and SetModTime as deprecated.
Fixes #18359
Change-Id: I29c6bc0a62908095d02740df3e6902f50d3152f1
Reviewed-on: https://go-review.googlesource.com/74970
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2017-08-28 12:07:58 -07:00
|
|
|
//
|
|
|
|
|
// Deprecated: Use Modified instead.
|
2011-11-30 12:01:46 -05:00
|
|
|
func (h *FileHeader) ModTime() time.Time {
|
|
|
|
|
return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
|
2011-07-18 20:30:44 -07:00
|
|
|
}
|
2011-09-25 20:48:03 -03:00
|
|
|
|
archive/zip: add FileHeader.Modified field
The ModifiedTime and ModifiedDate fields are not expressive enough
for many of the time extensions that have since been added to ZIP,
nor are they easy to access since they in a legacy MS-DOS format,
and must be set and retrieved via the SetModTime and ModTime methods.
Instead, we add new field Modified of time.Time type that contains
all of the previous information and more.
Support for extended timestamps have been attempted before, but the
change was reverted because it provided no ability for the user to
specify the timezone of the legacy MS-DOS fields.
Technically the old API did not either, but users were manually offsetting
the timestamp to achieve the same effect.
The Writer now writes the legacy timestamps according to the timezone
of the FileHeader.Modified field. When the Modified field is set via
the SetModTime method, it is in UTC, which preserves the old behavior.
The Reader attempts to determine the timezone if both the legacy
and extended timestamps are present since it can compute the delta
between the two values.
Since Modified is a superset of the information in ModifiedTime and ModifiedDate,
we mark ModifiedTime, ModifiedDate, ModTime, and SetModTime as deprecated.
Fixes #18359
Change-Id: I29c6bc0a62908095d02740df3e6902f50d3152f1
Reviewed-on: https://go-review.googlesource.com/74970
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2017-08-28 12:07:58 -07:00
|
|
|
// SetModTime sets the Modified, ModifiedTime, and ModifiedDate fields
|
|
|
|
|
// to the given time in UTC.
|
|
|
|
|
//
|
|
|
|
|
// Deprecated: Use Modified instead.
|
2011-12-19 14:59:41 +11:00
|
|
|
func (h *FileHeader) SetModTime(t time.Time) {
|
2017-11-09 16:07:29 -08:00
|
|
|
t = t.UTC() // Convert to UTC for compatibility
|
archive/zip: add FileHeader.Modified field
The ModifiedTime and ModifiedDate fields are not expressive enough
for many of the time extensions that have since been added to ZIP,
nor are they easy to access since they in a legacy MS-DOS format,
and must be set and retrieved via the SetModTime and ModTime methods.
Instead, we add new field Modified of time.Time type that contains
all of the previous information and more.
Support for extended timestamps have been attempted before, but the
change was reverted because it provided no ability for the user to
specify the timezone of the legacy MS-DOS fields.
Technically the old API did not either, but users were manually offsetting
the timestamp to achieve the same effect.
The Writer now writes the legacy timestamps according to the timezone
of the FileHeader.Modified field. When the Modified field is set via
the SetModTime method, it is in UTC, which preserves the old behavior.
The Reader attempts to determine the timezone if both the legacy
and extended timestamps are present since it can compute the delta
between the two values.
Since Modified is a superset of the information in ModifiedTime and ModifiedDate,
we mark ModifiedTime, ModifiedDate, ModTime, and SetModTime as deprecated.
Fixes #18359
Change-Id: I29c6bc0a62908095d02740df3e6902f50d3152f1
Reviewed-on: https://go-review.googlesource.com/74970
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2017-08-28 12:07:58 -07:00
|
|
|
h.Modified = t
|
2011-12-19 14:59:41 +11:00
|
|
|
h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t)
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-12 15:22:55 -05:00
|
|
|
const (
|
2012-02-06 11:58:32 -02:00
|
|
|
// Unix constants. The specification doesn't mention them,
|
|
|
|
|
// but these seem to be the values agreed on by tools.
|
|
|
|
|
s_IFMT = 0xf000
|
|
|
|
|
s_IFSOCK = 0xc000
|
|
|
|
|
s_IFLNK = 0xa000
|
|
|
|
|
s_IFREG = 0x8000
|
|
|
|
|
s_IFBLK = 0x6000
|
|
|
|
|
s_IFDIR = 0x4000
|
|
|
|
|
s_IFCHR = 0x2000
|
|
|
|
|
s_IFIFO = 0x1000
|
|
|
|
|
s_ISUID = 0x800
|
|
|
|
|
s_ISGID = 0x400
|
|
|
|
|
s_ISVTX = 0x200
|
2011-12-12 15:22:55 -05:00
|
|
|
|
|
|
|
|
msdosDir = 0x10
|
|
|
|
|
msdosReadOnly = 0x01
|
|
|
|
|
)
|
|
|
|
|
|
2011-09-25 20:48:03 -03:00
|
|
|
// Mode returns the permission and mode bits for the FileHeader.
|
2020-07-07 13:49:21 -04:00
|
|
|
func (h *FileHeader) Mode() (mode fs.FileMode) {
|
2011-12-12 15:22:55 -05:00
|
|
|
switch h.CreatorVersion >> 8 {
|
|
|
|
|
case creatorUnix, creatorMacOSX:
|
|
|
|
|
mode = unixModeToFileMode(h.ExternalAttrs >> 16)
|
|
|
|
|
case creatorNTFS, creatorVFAT, creatorFAT:
|
|
|
|
|
mode = msdosModeToFileMode(h.ExternalAttrs)
|
2011-09-25 20:48:03 -03:00
|
|
|
}
|
2011-12-12 15:22:55 -05:00
|
|
|
if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' {
|
2020-07-07 13:49:21 -04:00
|
|
|
mode |= fs.ModeDir
|
2011-12-12 15:22:55 -05:00
|
|
|
}
|
2012-01-26 15:31:09 -08:00
|
|
|
return mode
|
2011-09-25 20:48:03 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetMode changes the permission and mode bits for the FileHeader.
|
2020-07-07 13:49:21 -04:00
|
|
|
func (h *FileHeader) SetMode(mode fs.FileMode) {
|
2011-09-25 20:48:03 -03:00
|
|
|
h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
|
2011-12-12 15:22:55 -05:00
|
|
|
h.ExternalAttrs = fileModeToUnixMode(mode) << 16
|
|
|
|
|
|
|
|
|
|
// set MSDOS attributes too, as the original zip does.
|
2020-07-07 13:49:21 -04:00
|
|
|
if mode&fs.ModeDir != 0 {
|
2011-12-12 15:22:55 -05:00
|
|
|
h.ExternalAttrs |= msdosDir
|
|
|
|
|
}
|
|
|
|
|
if mode&0200 == 0 {
|
|
|
|
|
h.ExternalAttrs |= msdosReadOnly
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 15:44:42 -08:00
|
|
|
// isZip64 reports whether the file size exceeds the 32 bit limit
|
2018-06-22 16:50:31 +05:30
|
|
|
func (h *FileHeader) isZip64() bool {
|
|
|
|
|
return h.CompressedSize64 >= uint32max || h.UncompressedSize64 >= uint32max
|
2012-08-22 11:05:24 +10:00
|
|
|
}
|
|
|
|
|
|
2020-07-07 13:49:21 -04:00
|
|
|
func msdosModeToFileMode(m uint32) (mode fs.FileMode) {
|
2011-12-12 15:22:55 -05:00
|
|
|
if m&msdosDir != 0 {
|
2020-07-07 13:49:21 -04:00
|
|
|
mode = fs.ModeDir | 0777
|
2011-12-12 15:22:55 -05:00
|
|
|
} else {
|
|
|
|
|
mode = 0666
|
|
|
|
|
}
|
|
|
|
|
if m&msdosReadOnly != 0 {
|
|
|
|
|
mode &^= 0222
|
|
|
|
|
}
|
|
|
|
|
return mode
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 13:49:21 -04:00
|
|
|
func fileModeToUnixMode(mode fs.FileMode) uint32 {
|
2011-12-12 15:22:55 -05:00
|
|
|
var m uint32
|
2020-07-07 13:49:21 -04:00
|
|
|
switch mode & fs.ModeType {
|
2012-02-06 11:58:32 -02:00
|
|
|
default:
|
2011-12-12 15:22:55 -05:00
|
|
|
m = s_IFREG
|
2020-07-07 13:49:21 -04:00
|
|
|
case fs.ModeDir:
|
2012-02-06 11:58:32 -02:00
|
|
|
m = s_IFDIR
|
2020-07-07 13:49:21 -04:00
|
|
|
case fs.ModeSymlink:
|
2012-02-06 11:58:32 -02:00
|
|
|
m = s_IFLNK
|
2020-07-07 13:49:21 -04:00
|
|
|
case fs.ModeNamedPipe:
|
2012-02-06 11:58:32 -02:00
|
|
|
m = s_IFIFO
|
2020-07-07 13:49:21 -04:00
|
|
|
case fs.ModeSocket:
|
2012-02-06 11:58:32 -02:00
|
|
|
m = s_IFSOCK
|
2020-07-07 13:49:21 -04:00
|
|
|
case fs.ModeDevice:
|
|
|
|
|
if mode&fs.ModeCharDevice != 0 {
|
2012-02-06 11:58:32 -02:00
|
|
|
m = s_IFCHR
|
|
|
|
|
} else {
|
|
|
|
|
m = s_IFBLK
|
|
|
|
|
}
|
2011-12-12 15:22:55 -05:00
|
|
|
}
|
2020-07-07 13:49:21 -04:00
|
|
|
if mode&fs.ModeSetuid != 0 {
|
2011-12-12 15:22:55 -05:00
|
|
|
m |= s_ISUID
|
|
|
|
|
}
|
2020-07-07 13:49:21 -04:00
|
|
|
if mode&fs.ModeSetgid != 0 {
|
2011-12-12 15:22:55 -05:00
|
|
|
m |= s_ISGID
|
|
|
|
|
}
|
2020-07-07 13:49:21 -04:00
|
|
|
if mode&fs.ModeSticky != 0 {
|
2012-02-06 11:58:32 -02:00
|
|
|
m |= s_ISVTX
|
|
|
|
|
}
|
2011-12-12 15:22:55 -05:00
|
|
|
return m | uint32(mode&0777)
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 13:49:21 -04:00
|
|
|
func unixModeToFileMode(m uint32) fs.FileMode {
|
|
|
|
|
mode := fs.FileMode(m & 0777)
|
2012-02-06 11:58:32 -02:00
|
|
|
switch m & s_IFMT {
|
|
|
|
|
case s_IFBLK:
|
2020-07-07 13:49:21 -04:00
|
|
|
mode |= fs.ModeDevice
|
2012-02-06 11:58:32 -02:00
|
|
|
case s_IFCHR:
|
2020-07-07 13:49:21 -04:00
|
|
|
mode |= fs.ModeDevice | fs.ModeCharDevice
|
2012-02-06 11:58:32 -02:00
|
|
|
case s_IFDIR:
|
2020-07-07 13:49:21 -04:00
|
|
|
mode |= fs.ModeDir
|
2012-02-06 11:58:32 -02:00
|
|
|
case s_IFIFO:
|
2020-07-07 13:49:21 -04:00
|
|
|
mode |= fs.ModeNamedPipe
|
2012-02-06 11:58:32 -02:00
|
|
|
case s_IFLNK:
|
2020-07-07 13:49:21 -04:00
|
|
|
mode |= fs.ModeSymlink
|
2012-02-06 11:58:32 -02:00
|
|
|
case s_IFREG:
|
|
|
|
|
// nothing to do
|
|
|
|
|
case s_IFSOCK:
|
2020-07-07 13:49:21 -04:00
|
|
|
mode |= fs.ModeSocket
|
2011-12-12 15:22:55 -05:00
|
|
|
}
|
|
|
|
|
if m&s_ISGID != 0 {
|
2020-07-07 13:49:21 -04:00
|
|
|
mode |= fs.ModeSetgid
|
2011-12-12 15:22:55 -05:00
|
|
|
}
|
|
|
|
|
if m&s_ISUID != 0 {
|
2020-07-07 13:49:21 -04:00
|
|
|
mode |= fs.ModeSetuid
|
2011-12-12 15:22:55 -05:00
|
|
|
}
|
2012-02-06 11:58:32 -02:00
|
|
|
if m&s_ISVTX != 0 {
|
2020-07-07 13:49:21 -04:00
|
|
|
mode |= fs.ModeSticky
|
2012-02-06 11:58:32 -02:00
|
|
|
}
|
|
|
|
|
return mode
|
2011-09-25 20:48:03 -03:00
|
|
|
}
|