mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
archive/zip: make zip understand os.FileMode.
Fixes implicit dependency on underlying os file modes. R=rsc, r, n13m3y3r, gustavo, adg CC=golang-dev https://golang.org/cl/5440130
This commit is contained in:
parent
68ec347c16
commit
2cb1aa4681
5 changed files with 153 additions and 23 deletions
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
@ -25,7 +26,7 @@ type ZipTestFile struct {
|
||||||
Content []byte // if blank, will attempt to compare against File
|
Content []byte // if blank, will attempt to compare against File
|
||||||
File string // name of file to compare to (relative to testdata/)
|
File string // name of file to compare to (relative to testdata/)
|
||||||
Mtime string // modified time in format "mm-dd-yy hh:mm:ss"
|
Mtime string // modified time in format "mm-dd-yy hh:mm:ss"
|
||||||
Mode uint32
|
Mode os.FileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
// Caution: The Mtime values found for the test files should correspond to
|
// Caution: The Mtime values found for the test files should correspond to
|
||||||
|
|
@ -47,13 +48,13 @@ var tests = []ZipTest{
|
||||||
Name: "test.txt",
|
Name: "test.txt",
|
||||||
Content: []byte("This is a test text file.\n"),
|
Content: []byte("This is a test text file.\n"),
|
||||||
Mtime: "09-05-10 12:12:02",
|
Mtime: "09-05-10 12:12:02",
|
||||||
Mode: 0x81a4,
|
Mode: 0644,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "gophercolor16x16.png",
|
Name: "gophercolor16x16.png",
|
||||||
File: "gophercolor16x16.png",
|
File: "gophercolor16x16.png",
|
||||||
Mtime: "09-05-10 15:52:58",
|
Mtime: "09-05-10 15:52:58",
|
||||||
Mode: 0x81a4,
|
Mode: 0644,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -64,6 +65,7 @@ var tests = []ZipTest{
|
||||||
Name: "r/r.zip",
|
Name: "r/r.zip",
|
||||||
File: "r.zip",
|
File: "r.zip",
|
||||||
Mtime: "03-04-10 00:24:16",
|
Mtime: "03-04-10 00:24:16",
|
||||||
|
Mode: 0666,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -76,9 +78,43 @@ var tests = []ZipTest{
|
||||||
Name: "filename",
|
Name: "filename",
|
||||||
Content: []byte("This is a test textfile.\n"),
|
Content: []byte("This is a test textfile.\n"),
|
||||||
Mtime: "02-02-11 13:06:20",
|
Mtime: "02-02-11 13:06:20",
|
||||||
|
Mode: 0666,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// created in windows XP file manager.
|
||||||
|
Name: "winxp.zip",
|
||||||
|
File: crossPlatform,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// created by Zip 3.0 under Linux
|
||||||
|
Name: "unix.zip",
|
||||||
|
File: crossPlatform,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var crossPlatform = []ZipTestFile{
|
||||||
|
{
|
||||||
|
Name: "hello",
|
||||||
|
Content: []byte("world \r\n"),
|
||||||
|
Mode: 0666,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "dir/bar",
|
||||||
|
Content: []byte("foo \r\n"),
|
||||||
|
Mode: 0666,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "dir/empty/",
|
||||||
|
Content: []byte{},
|
||||||
|
Mode: os.ModeDir | 0777,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "readonly",
|
||||||
|
Content: []byte("important \r\n"),
|
||||||
|
Mode: 0444,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReader(t *testing.T) {
|
func TestReader(t *testing.T) {
|
||||||
|
|
@ -159,6 +195,7 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
|
||||||
t.Errorf("name=%q, want %q", f.Name, ft.Name)
|
t.Errorf("name=%q, want %q", f.Name, ft.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ft.Mtime != "" {
|
||||||
mtime, err := time.Parse("01-02-06 15:04:05", ft.Mtime)
|
mtime, err := time.Parse("01-02-06 15:04:05", ft.Mtime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -167,6 +204,7 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
|
||||||
if ft := f.ModTime(); !ft.Equal(mtime) {
|
if ft := f.ModTime(); !ft.Equal(mtime) {
|
||||||
t.Errorf("%s: mtime=%s, want %s", f.Name, ft, mtime)
|
t.Errorf("%s: mtime=%s, want %s", f.Name, ft, mtime)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
testFileMode(t, f, ft.Mode)
|
testFileMode(t, f, ft.Mode)
|
||||||
|
|
||||||
|
|
@ -191,7 +229,7 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
|
||||||
r.Close()
|
r.Close()
|
||||||
|
|
||||||
var c []byte
|
var c []byte
|
||||||
if len(ft.Content) != 0 {
|
if ft.Content != nil {
|
||||||
c = ft.Content
|
c = ft.Content
|
||||||
} else if c, err = ioutil.ReadFile("testdata/" + ft.File); err != nil {
|
} else if c, err = ioutil.ReadFile("testdata/" + ft.File); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -211,7 +249,7 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testFileMode(t *testing.T, f *File, want uint32) {
|
func testFileMode(t *testing.T, f *File, want os.FileMode) {
|
||||||
mode, err := f.Mode()
|
mode, err := f.Mode()
|
||||||
if want == 0 {
|
if want == 0 {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
@ -220,7 +258,7 @@ func testFileMode(t *testing.T, f *File, want uint32) {
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
t.Errorf("%s mode: %s", f.Name, err)
|
t.Errorf("%s mode: %s", f.Name, err)
|
||||||
} else if mode != want {
|
} else if mode != want {
|
||||||
t.Errorf("%s mode: want 0x%x, got 0x%x", f.Name, want, mode)
|
t.Errorf("%s mode: want %v, got %v", f.Name, want, mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ This package does not support ZIP64 or disk spanning.
|
||||||
package zip
|
package zip
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -32,7 +32,11 @@ const (
|
||||||
dataDescriptorLen = 12
|
dataDescriptorLen = 12
|
||||||
|
|
||||||
// Constants for the first byte in CreatorVersion
|
// Constants for the first byte in CreatorVersion
|
||||||
|
creatorFAT = 0
|
||||||
creatorUnix = 3
|
creatorUnix = 3
|
||||||
|
creatorNTFS = 11
|
||||||
|
creatorVFAT = 14
|
||||||
|
creatorMacOSX = 19
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileHeader struct {
|
type FileHeader struct {
|
||||||
|
|
@ -98,17 +102,85 @@ func (h *FileHeader) ModTime() time.Time {
|
||||||
return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
|
return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// traditional names for Unix constants
|
||||||
|
const (
|
||||||
|
s_IFMT = 0xf000
|
||||||
|
s_IFDIR = 0x4000
|
||||||
|
s_IFREG = 0x8000
|
||||||
|
s_ISUID = 0x800
|
||||||
|
s_ISGID = 0x400
|
||||||
|
|
||||||
|
msdosDir = 0x10
|
||||||
|
msdosReadOnly = 0x01
|
||||||
|
)
|
||||||
|
|
||||||
// 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.
|
// An error is returned in case the information is not available.
|
||||||
func (h *FileHeader) Mode() (mode uint32, err error) {
|
func (h *FileHeader) Mode() (mode os.FileMode, err error) {
|
||||||
if h.CreatorVersion>>8 == creatorUnix {
|
switch h.CreatorVersion >> 8 {
|
||||||
return h.ExternalAttrs >> 16, nil
|
case creatorUnix, creatorMacOSX:
|
||||||
|
mode = unixModeToFileMode(h.ExternalAttrs >> 16)
|
||||||
|
case creatorNTFS, creatorVFAT, creatorFAT:
|
||||||
|
mode = msdosModeToFileMode(h.ExternalAttrs)
|
||||||
}
|
}
|
||||||
return 0, errors.New("file mode not available")
|
if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' {
|
||||||
|
mode |= os.ModeDir
|
||||||
|
}
|
||||||
|
return mode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMode changes the permission and mode bits for the FileHeader.
|
// SetMode changes the permission and mode bits for the FileHeader.
|
||||||
func (h *FileHeader) SetMode(mode uint32) {
|
func (h *FileHeader) SetMode(mode os.FileMode) {
|
||||||
h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
|
h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
|
||||||
h.ExternalAttrs = mode << 16
|
h.ExternalAttrs = fileModeToUnixMode(mode) << 16
|
||||||
|
|
||||||
|
// set MSDOS attributes too, as the original zip does.
|
||||||
|
if mode&os.ModeDir != 0 {
|
||||||
|
h.ExternalAttrs |= msdosDir
|
||||||
|
}
|
||||||
|
if mode&0200 == 0 {
|
||||||
|
h.ExternalAttrs |= msdosReadOnly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func msdosModeToFileMode(m uint32) (mode os.FileMode) {
|
||||||
|
if m&msdosDir != 0 {
|
||||||
|
mode = os.ModeDir | 0777
|
||||||
|
} else {
|
||||||
|
mode = 0666
|
||||||
|
}
|
||||||
|
if m&msdosReadOnly != 0 {
|
||||||
|
mode &^= 0222
|
||||||
|
}
|
||||||
|
return mode
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileModeToUnixMode(mode os.FileMode) uint32 {
|
||||||
|
var m uint32
|
||||||
|
if mode&os.ModeDir != 0 {
|
||||||
|
m = s_IFDIR
|
||||||
|
} else {
|
||||||
|
m = s_IFREG
|
||||||
|
}
|
||||||
|
if mode&os.ModeSetuid != 0 {
|
||||||
|
m |= s_ISUID
|
||||||
|
}
|
||||||
|
if mode&os.ModeSetgid != 0 {
|
||||||
|
m |= s_ISGID
|
||||||
|
}
|
||||||
|
return m | uint32(mode&0777)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unixModeToFileMode(m uint32) os.FileMode {
|
||||||
|
var mode os.FileMode
|
||||||
|
if m&s_IFMT == s_IFDIR {
|
||||||
|
mode |= os.ModeDir
|
||||||
|
}
|
||||||
|
if m&s_ISGID != 0 {
|
||||||
|
mode |= os.ModeSetgid
|
||||||
|
}
|
||||||
|
if m&s_ISUID != 0 {
|
||||||
|
mode |= os.ModeSetuid
|
||||||
|
}
|
||||||
|
return mode | os.FileMode(m&0777)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
src/pkg/archive/zip/testdata/unix.zip
vendored
Normal file
BIN
src/pkg/archive/zip/testdata/unix.zip
vendored
Normal file
Binary file not shown.
BIN
src/pkg/archive/zip/testdata/winxp.zip
vendored
Normal file
BIN
src/pkg/archive/zip/testdata/winxp.zip
vendored
Normal file
Binary file not shown.
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -17,7 +18,7 @@ type WriteTest struct {
|
||||||
Name string
|
Name string
|
||||||
Data []byte
|
Data []byte
|
||||||
Method uint16
|
Method uint16
|
||||||
Mode uint32
|
Mode os.FileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
var writeTests = []WriteTest{
|
var writeTests = []WriteTest{
|
||||||
|
|
@ -25,12 +26,31 @@ var writeTests = []WriteTest{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Data: []byte("Rabbits, guinea pigs, gophers, marsupial rats, and quolls."),
|
Data: []byte("Rabbits, guinea pigs, gophers, marsupial rats, and quolls."),
|
||||||
Method: Store,
|
Method: Store,
|
||||||
|
Mode: 0666,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
Data: nil, // large data set in the test
|
Data: nil, // large data set in the test
|
||||||
Method: Deflate,
|
Method: Deflate,
|
||||||
Mode: 0x81ed,
|
Mode: 0644,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "setuid",
|
||||||
|
Data: []byte("setuid file"),
|
||||||
|
Method: Deflate,
|
||||||
|
Mode: 0755 | os.ModeSetuid,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "setgid",
|
||||||
|
Data: []byte("setgid file"),
|
||||||
|
Method: Deflate,
|
||||||
|
Mode: 0755 | os.ModeSetgid,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "setgid",
|
||||||
|
Data: []byte("setgid file"),
|
||||||
|
Method: Deflate,
|
||||||
|
Mode: 0755 | os.ModeSetgid,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue