1
0
mirror of https://github.com/golang/go synced 2024-11-25 21:57:57 -07:00

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
This commit is contained in:
Brad Fitzpatrick 2012-01-26 15:31:09 -08:00
parent 2a22f35598
commit b62a5099e4
3 changed files with 57 additions and 9 deletions

View File

@ -250,13 +250,9 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
} }
func testFileMode(t *testing.T, f *File, want os.FileMode) { func testFileMode(t *testing.T, f *File, want os.FileMode) {
mode, err := f.Mode() mode := f.Mode()
if want == 0 { if want == 0 {
if err == nil {
t.Errorf("%s mode: got %v, want none", f.Name, mode) t.Errorf("%s mode: got %v, want none", f.Name, mode)
}
} else if err != nil {
t.Errorf("%s mode: %s", f.Name, err)
} else if mode != want { } else if mode != want {
t.Errorf("%s mode: want %v, got %v", f.Name, want, mode) t.Errorf("%s mode: want %v, got %v", f.Name, want, mode)
} }

View File

@ -12,6 +12,7 @@ This package does not support ZIP64 or disk spanning.
package zip package zip
import ( import (
"errors"
"os" "os"
"time" "time"
) )
@ -55,6 +56,38 @@ type FileHeader struct {
Comment string 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 { type directoryEnd struct {
diskNbr uint16 // unused diskNbr uint16 // unused
dirDiskNbr uint16 // unused dirDiskNbr uint16 // unused
@ -131,8 +164,7 @@ const (
) )
// Mode returns the permission and mode bits for the FileHeader. // 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) {
func (h *FileHeader) Mode() (mode os.FileMode, err error) {
switch h.CreatorVersion >> 8 { switch h.CreatorVersion >> 8 {
case creatorUnix, creatorMacOSX: case creatorUnix, creatorMacOSX:
mode = unixModeToFileMode(h.ExternalAttrs >> 16) 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] == '/' { if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' {
mode |= os.ModeDir mode |= os.ModeDir
} }
return mode, nil return mode
} }
// SetMode changes the permission and mode bits for the FileHeader. // SetMode changes the permission and mode bits for the FileHeader.

View File

@ -10,6 +10,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"reflect"
"testing" "testing"
"time" "time"
) )
@ -66,3 +67,22 @@ func TestModTime(t *testing.T) {
t.Errorf("times don't match: got %s, want %s", outTime, testTime) 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)
}
}