From: Brad Fitzpatrick Date: Thu, 26 Jan 2012 23:31:09 +0000 (-0800) Subject: archive/zip: add functions to convert between os.FileInfo & FileHeader X-Git-Tag: weekly.2012-01-27~4 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=b62a5099e4bf2e87525792dd562c20894fff878c;p=gostls13.git archive/zip: add functions to convert between os.FileInfo & FileHeader Fixes #2186 R=golang-dev, gri, adg CC=golang-dev https://golang.org/cl/5579044 --- diff --git a/src/pkg/archive/zip/reader_test.go b/src/pkg/archive/zip/reader_test.go index b34f5bf1ef..9407e35d5c 100644 --- a/src/pkg/archive/zip/reader_test.go +++ b/src/pkg/archive/zip/reader_test.go @@ -250,13 +250,9 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) { } func testFileMode(t *testing.T, f *File, want os.FileMode) { - mode, err := f.Mode() + mode := f.Mode() if want == 0 { - if err == nil { - t.Errorf("%s mode: got %v, want none", f.Name, mode) - } - } else if err != nil { - t.Errorf("%s mode: %s", f.Name, err) + t.Errorf("%s mode: got %v, want none", f.Name, mode) } else if mode != want { t.Errorf("%s mode: want %v, got %v", f.Name, want, mode) } diff --git a/src/pkg/archive/zip/struct.go b/src/pkg/archive/zip/struct.go index 34a87fae5b..67e9658629 100644 --- a/src/pkg/archive/zip/struct.go +++ b/src/pkg/archive/zip/struct.go @@ -12,6 +12,7 @@ This package does not support ZIP64 or disk spanning. package zip import ( + "errors" "os" "time" ) @@ -55,6 +56,38 @@ type FileHeader struct { Comment string } +// FileInfo returns an os.FileInfo for the FileHeader. +func (fh *FileHeader) FileInfo() os.FileInfo { + return headerFileInfo{fh} +} + +// headerFileInfo implements os.FileInfo. +type headerFileInfo struct { + fh *FileHeader +} + +func (fi headerFileInfo) Name() string { return fi.fh.Name } +func (fi headerFileInfo) Size() int64 { return int64(fi.fh.UncompressedSize) } +func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } +func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() } +func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() } + +// FileInfoHeader creates a partially-populated FileHeader from an +// os.FileInfo. +func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) { + size := fi.Size() + if size > (1<<32 - 1) { + return nil, errors.New("zip: file over 4GB") + } + fh := &FileHeader{ + Name: fi.Name(), + UncompressedSize: uint32(size), + } + fh.SetModTime(fi.ModTime()) + fh.SetMode(fi.Mode()) + return fh, nil +} + type directoryEnd struct { diskNbr uint16 // unused dirDiskNbr uint16 // unused @@ -131,8 +164,7 @@ const ( ) // Mode returns the permission and mode bits for the FileHeader. -// An error is returned in case the information is not available. -func (h *FileHeader) Mode() (mode os.FileMode, err error) { +func (h *FileHeader) Mode() (mode os.FileMode) { switch h.CreatorVersion >> 8 { case creatorUnix, creatorMacOSX: mode = unixModeToFileMode(h.ExternalAttrs >> 16) @@ -142,7 +174,7 @@ func (h *FileHeader) Mode() (mode os.FileMode, err error) { if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' { mode |= os.ModeDir } - return mode, nil + return mode } // SetMode changes the permission and mode bits for the FileHeader. diff --git a/src/pkg/archive/zip/zip_test.go b/src/pkg/archive/zip/zip_test.go index 8aab2b6812..acd3d93821 100644 --- a/src/pkg/archive/zip/zip_test.go +++ b/src/pkg/archive/zip/zip_test.go @@ -10,6 +10,7 @@ import ( "bytes" "fmt" "io" + "reflect" "testing" "time" ) @@ -66,3 +67,22 @@ func TestModTime(t *testing.T) { t.Errorf("times don't match: got %s, want %s", outTime, testTime) } } + +func TestFileHeaderRoundTrip(t *testing.T) { + fh := &FileHeader{ + Name: "foo.txt", + UncompressedSize: 987654321, + ModifiedTime: 1234, + ModifiedDate: 5678, + } + fi := fh.FileInfo() + fh2, err := FileInfoHeader(fi) + + // Ignore these fields: + fh2.CreatorVersion = 0 + fh2.ExternalAttrs = 0 + + if !reflect.DeepEqual(fh, fh2) { + t.Errorf("mismatch\n input=%#v\noutput=%#v\nerr=%v", fh, fh2, err) + } +}