]> Cypherpunks repositories - gostls13.git/commitdiff
archive/zip: read and write unix file modes
authorGustavo Niemeyer <gustavo@niemeyer.net>
Sun, 25 Sep 2011 23:48:03 +0000 (20:48 -0300)
committerGustavo Niemeyer <gustavo@niemeyer.net>
Sun, 25 Sep 2011 23:48:03 +0000 (20:48 -0300)
R=golang-dev, rsc, adg
CC=golang-dev
https://golang.org/cl/5124044

src/pkg/archive/zip/reader.go
src/pkg/archive/zip/reader_test.go
src/pkg/archive/zip/struct.go
src/pkg/archive/zip/writer.go
src/pkg/archive/zip/writer_test.go

index f92f9297ada8c65137c3c176a5b5057ff2b7b427..b0a559936bd2a2dcdf23fa34fed252bd949ba1b5 100644 (file)
@@ -238,7 +238,7 @@ func readDirectoryHeader(f *File, r io.Reader) os.Error {
        commentLen := int(c.Uint16(b[32:34]))
        // startDiskNumber := c.Uint16(b[34:36])    // Unused
        // internalAttributes := c.Uint16(b[36:38]) // Unused
-       // externalAttributes := c.Uint32(b[38:42]) // Unused
+       f.ExternalAttrs = c.Uint32(b[38:42])
        f.headerOffset = int64(c.Uint32(b[42:46]))
        d := make([]byte, filenameLen+extraLen+commentLen)
        if _, err := io.ReadFull(r, d); err != nil {
index fd5fed2af05fa12acde9e437a518a0a487d22941..3b7b0dc130441655e4fda4e6ababb8a60e1df613 100644 (file)
@@ -26,6 +26,7 @@ type ZipTestFile struct {
        Content []byte // if blank, will attempt to compare against File
        File    string // name of file to compare to (relative to testdata/)
        Mtime   string // modified time in format "mm-dd-yy hh:mm:ss"
+       Mode    uint32
 }
 
 // Caution: The Mtime values found for the test files should correspond to
@@ -47,11 +48,13 @@ var tests = []ZipTest{
                                Name:    "test.txt",
                                Content: []byte("This is a test text file.\n"),
                                Mtime:   "09-05-10 12:12:02",
+                               Mode:    0x81a4,
                        },
                        {
                                Name:  "gophercolor16x16.png",
                                File:  "gophercolor16x16.png",
                                Mtime: "09-05-10 15:52:58",
+                               Mode:  0x81a4,
                        },
                },
        },
@@ -162,6 +165,8 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
                t.Errorf("%s: mtime=%s (%d); want %s (%d)", f.Name, time.SecondsToUTC(got), got, mtime, want)
        }
 
+       testFileMode(t, f, ft.Mode)
+
        size0 := f.UncompressedSize
 
        var b bytes.Buffer
@@ -203,6 +208,19 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
        }
 }
 
+func testFileMode(t *testing.T, f *File, want uint32) {
+       mode, err := 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)
+       } else if mode != want {
+               t.Errorf("%s mode: want 0x%x, got 0x%x", f.Name, want, mode)
+       }
+}
+
 func TestInvalidFiles(t *testing.T) {
        const size = 1024 * 70 // 70kb
        b := make([]byte, size)
index 1d6e70f105a9299a781acf551a6cfe85e78f52fe..a32de5a9e0d49fa783a599488e435890170eb614 100644 (file)
@@ -28,6 +28,9 @@ const (
        directoryHeaderLen       = 46 // + filename + extra + comment
        directoryEndLen          = 22 // + comment
        dataDescriptorLen        = 12
+
+       // Constants for the first byte in CreatorVersion
+       creatorUnix = 3
 )
 
 type FileHeader struct {
@@ -42,6 +45,7 @@ type FileHeader struct {
        CompressedSize   uint32
        UncompressedSize uint32
        Extra            []byte
+       ExternalAttrs    uint32 // Meaning depends on CreatorVersion
        Comment          string
 }
 
@@ -89,3 +93,18 @@ func (h *FileHeader) Mtime_ns() int64 {
        t := msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
        return t.Seconds() * 1e9
 }
+
+// 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 uint32, err os.Error) {
+       if h.CreatorVersion>>8 == creatorUnix {
+               return h.ExternalAttrs >> 16, nil
+       }
+       return 0, os.NewError("file mode not available")
+}
+
+// SetMode changes the permission and mode bits for the FileHeader.
+func (h *FileHeader) SetMode(mode uint32) {
+       h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
+       h.ExternalAttrs = mode << 16
+}
index 2065b06daac80fd8eebf8dd38de68ecaebd05273..3a6dc38e20fedc78477d8e855a542cec49aeb7e8 100644 (file)
@@ -69,7 +69,7 @@ func (w *Writer) Close() (err os.Error) {
                write(w, uint16(len(h.Comment)))
                write(w, uint16(0)) // disk number start
                write(w, uint16(0)) // internal file attributes
-               write(w, uint32(0)) // external file attributes
+               write(w, h.ExternalAttrs)
                write(w, h.offset)
                writeBytes(w, []byte(h.Name))
                writeBytes(w, h.Extra)
@@ -115,7 +115,7 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, os.Error) {
        }
 
        fh.Flags |= 0x8 // we will write a data descriptor
-       fh.CreatorVersion = 0x14
+       fh.CreatorVersion = fh.CreatorVersion&0xff00 | 0x14
        fh.ReaderVersion = 0x14
 
        fw := &fileWriter{
index eb2a80c3f7027b9710c6611775346a8f4564d626..97f86709479b1b2915928996b4d71e2ed631b052 100644 (file)
@@ -13,19 +13,45 @@ import (
 
 // TODO(adg): a more sophisticated test suite
 
-const testString = "Rabbits, guinea pigs, gophers, marsupial rats, and quolls."
+type WriteTest struct {
+       Name   string
+       Data   []byte
+       Method uint16
+       Mode   uint32
+}
+
+var writeTests = []WriteTest{
+       WriteTest{
+               Name:   "foo",
+               Data:   []byte("Rabbits, guinea pigs, gophers, marsupial rats, and quolls."),
+               Method: Store,
+       },
+       WriteTest{
+               Name:   "bar",
+               Data:   nil, // large data set in the test
+               Method: Deflate,
+               Mode:   0x81ed,
+       },
+}
 
 func TestWriter(t *testing.T) {
        largeData := make([]byte, 1<<17)
        for i := range largeData {
                largeData[i] = byte(rand.Int())
        }
+       writeTests[1].Data = largeData
+       defer func() {
+               writeTests[1].Data = nil
+       }()
 
        // write a zip file
        buf := new(bytes.Buffer)
        w := NewWriter(buf)
-       testCreate(t, w, "foo", []byte(testString), Store)
-       testCreate(t, w, "bar", largeData, Deflate)
+
+       for _, wt := range writeTests {
+               testCreate(t, w, &wt)
+       }
+
        if err := w.Close(); err != nil {
                t.Fatal(err)
        }
@@ -35,26 +61,34 @@ func TestWriter(t *testing.T) {
        if err != nil {
                t.Fatal(err)
        }
-       testReadFile(t, r.File[0], []byte(testString))
-       testReadFile(t, r.File[1], largeData)
+       for i, wt := range writeTests {
+               testReadFile(t, r.File[i], &wt)
+       }
 }
 
-func testCreate(t *testing.T, w *Writer, name string, data []byte, method uint16) {
+func testCreate(t *testing.T, w *Writer, wt *WriteTest) {
        header := &FileHeader{
-               Name:   name,
-               Method: method,
+               Name:   wt.Name,
+               Method: wt.Method,
+       }
+       if wt.Mode != 0 {
+               header.SetMode(wt.Mode)
        }
        f, err := w.CreateHeader(header)
        if err != nil {
                t.Fatal(err)
        }
-       _, err = f.Write(data)
+       _, err = f.Write(wt.Data)
        if err != nil {
                t.Fatal(err)
        }
 }
 
-func testReadFile(t *testing.T, f *File, data []byte) {
+func testReadFile(t *testing.T, f *File, wt *WriteTest) {
+       if f.Name != wt.Name {
+               t.Fatal("File name: got %q, want %q", f.Name, wt.Name)
+       }
+       testFileMode(t, f, wt.Mode)
        rc, err := f.Open()
        if err != nil {
                t.Fatal("opening:", err)
@@ -67,7 +101,7 @@ func testReadFile(t *testing.T, f *File, data []byte) {
        if err != nil {
                t.Fatal("closing:", err)
        }
-       if !bytes.Equal(b, data) {
-               t.Errorf("File contents %q, want %q", b, data)
+       if !bytes.Equal(b, wt.Data) {
+               t.Errorf("File contents %q, want %q", b, wt.Data)
        }
 }