Commit 205486e3 authored by Jacob Vosmaer's avatar Jacob Vosmaer

Merge branch 'fix/metadata-full-structure' into 'master'

Add missing entries in build artifacts archive metadata

We need full directory structure, but since ZIP does not require it, we
need to calculate missing entries for directories when we generate
archive metadata.

Closes gitlab-org/gitlab-ce#12634

cc @jacobvosmaer @ayufan 

See merge request !34
parents 4fe063cf d4ee3a82
...@@ -23,7 +23,7 @@ func main() { ...@@ -23,7 +23,7 @@ func main() {
} }
if len(os.Args) != 2 { if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s FILE.ZIP", progName) fmt.Fprintf(os.Stderr, "Usage: %s FILE.ZIP\n", progName)
os.Exit(1) os.Exit(1)
} }
if err := zipartifacts.GenerateZipMetadataFromFile(os.Args[1], os.Stdout); err != nil { if err := zipartifacts.GenerateZipMetadataFromFile(os.Args[1], os.Stdout); err != nil {
......
...@@ -7,12 +7,14 @@ import ( ...@@ -7,12 +7,14 @@ import (
"encoding/json" "encoding/json"
"io" "io"
"os" "os"
"path"
"sort"
"strconv" "strconv"
) )
type metadata struct { type metadata struct {
Modified int64 `json:"modified"` Modified int64 `json:"modified,omitempty"`
Mode string `json:"mode"` Mode string `json:"mode,omitempty"`
CRC uint32 `json:"crc,omitempty"` CRC uint32 `json:"crc,omitempty"`
Size uint64 `json:"size,omitempty"` Size uint64 `json:"size,omitempty"`
Zipped uint64 `json:"zipped,omitempty"` Zipped uint64 `json:"zipped,omitempty"`
...@@ -23,6 +25,10 @@ const MetadataHeaderPrefix = "\x00\x00\x00&" // length of string below, encoded ...@@ -23,6 +25,10 @@ const MetadataHeaderPrefix = "\x00\x00\x00&" // length of string below, encoded
const MetadataHeader = "GitLab Build Artifacts Metadata 0.0.2\n" const MetadataHeader = "GitLab Build Artifacts Metadata 0.0.2\n"
func newMetadata(file *zip.File) metadata { func newMetadata(file *zip.File) metadata {
if file == nil {
return metadata{}
}
return metadata{ return metadata{
Modified: file.ModTime().Unix(), Modified: file.ModTime().Unix(),
Mode: strconv.FormatUint(uint64(file.Mode().Perm()), 8), Mode: strconv.FormatUint(uint64(file.Mode().Perm()), 8),
...@@ -42,35 +48,53 @@ func (m metadata) writeEncoded(output io.Writer) error { ...@@ -42,35 +48,53 @@ func (m metadata) writeEncoded(output io.Writer) error {
return writeBytes(output, j) return writeBytes(output, j)
} }
func writeZipEntryMetadata(output io.Writer, entry *zip.File) error { func writeZipEntryMetadata(output io.Writer, path string, entry *zip.File) error {
err := writeString(output, entry.Name) if err := writeString(output, path); err != nil {
if err != nil {
return err return err
} }
err = newMetadata(entry).writeEncoded(output) if err := newMetadata(entry).writeEncoded(output); err != nil {
if err != nil {
return err return err
} }
return nil return nil
} }
func generateZipMetadata(output io.Writer, archive *zip.Reader) error { func generateZipMetadata(output io.Writer, archive *zip.Reader) error {
err := writeString(output, MetadataHeader) if err := writeString(output, MetadataHeader); err != nil {
if err != nil {
return err return err
} }
// Write empty error string // Write empty error header that we may need in the future
err = writeString(output, "{}") if err := writeString(output, "{}"); err != nil {
if err != nil {
return err return err
} }
// Write all files // Create map of files in zip archive
zipMap := make(map[string]*zip.File, len(archive.File))
// Add missing entries
for _, entry := range archive.File { for _, entry := range archive.File {
err = writeZipEntryMetadata(output, entry) zipMap[entry.Name] = entry
if err != nil {
for d := path.Dir(entry.Name); d != "." && d != "/"; d = path.Dir(d) {
entryDir := d + "/"
if _, ok := zipMap[entryDir]; !ok {
zipMap[entryDir] = nil
}
}
}
// Sort paths
sortedPaths := make([]string, 0, len(zipMap))
for path, _ := range zipMap {
sortedPaths = append(sortedPaths, path)
}
sort.Strings(sortedPaths)
// Write all files
for _, path := range sortedPaths {
if err := writeZipEntryMetadata(output, path, zipMap[path]); err != nil {
return err return err
} }
} }
......
package zipartifacts
import (
"archive/zip"
"bytes"
"encoding/binary"
"fmt"
"testing"
)
func TestMissingMetadataEntries(t *testing.T) {
var zipBuffer, metaBuffer bytes.Buffer
archive := zip.NewWriter(&zipBuffer)
// non-POSIX paths are here just to test if we never enter infinite loop
files := []string{"file1", "some/file/dir/", "some/file/dir/file2", "../../test12/test",
"/usr/bin/test", `c:\windows\win32.exe`, `c:/windows/win.dll`, "./f/asd", "/"}
for _, file := range files {
archiveFile, err := archive.Create(file)
if err != nil {
t.Fatal(err)
}
fmt.Fprint(archiveFile, file)
}
archive.Close()
zipReader := bytes.NewReader(zipBuffer.Bytes())
zipArchiveReader, _ := zip.NewReader(zipReader, int64(binary.Size(zipBuffer.Bytes())))
if err := generateZipMetadata(&metaBuffer, zipArchiveReader); err != nil {
t.Fatal("zipartifacts: generateZipMetadata failed", err)
}
paths := []string{"file1", "some/", "some/file/", "some/file/dir/", "some/file/dir/file2"}
for _, path := range paths {
if !bytes.Contains(metaBuffer.Bytes(), []byte(path+"\x00")) {
t.Fatal("zipartifacts: metadata for path", path, "not found")
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment