Import files from compare_target_files for use in diff_target_files
Copied from cl/240594925. Bug: 121158314 Test: copied unit tests Change-Id: I2e91126285dcd33171ff8b8dbfcfa5d48501f535
This commit is contained in:
174
cmd/diff_target_files/zip_artifact.go
Normal file
174
cmd/diff_target_files/zip_artifact.go
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright 2019 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"context"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// ZipArtifact represents a zip file that may be local or remote.
|
||||
type ZipArtifact interface {
|
||||
// Files returns the list of files contained in the zip file.
|
||||
Files() ([]*ZipArtifactFile, error)
|
||||
|
||||
// Close closes the zip file artifact.
|
||||
Close()
|
||||
}
|
||||
|
||||
// localZipArtifact is a handle to a local zip file artifact.
|
||||
type localZipArtifact struct {
|
||||
zr *zip.ReadCloser
|
||||
files []*ZipArtifactFile
|
||||
}
|
||||
|
||||
// NewLocalZipArtifact returns a ZipArtifact for a local zip file..
|
||||
func NewLocalZipArtifact(name string) (ZipArtifact, error) {
|
||||
zr, err := zip.OpenReader(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var files []*ZipArtifactFile
|
||||
for _, zf := range zr.File {
|
||||
files = append(files, &ZipArtifactFile{zf})
|
||||
}
|
||||
|
||||
return &localZipArtifact{
|
||||
zr: zr,
|
||||
files: files,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Files returns the list of files contained in the local zip file artifact.
|
||||
func (z *localZipArtifact) Files() ([]*ZipArtifactFile, error) {
|
||||
return z.files, nil
|
||||
}
|
||||
|
||||
// Close closes the buffered reader of the local zip file artifact.
|
||||
func (z *localZipArtifact) Close() {
|
||||
z.zr.Close()
|
||||
}
|
||||
|
||||
// ZipArtifactFile contains a zip.File handle to the data inside the remote *-target_files-*.zip
|
||||
// build artifact.
|
||||
type ZipArtifactFile struct {
|
||||
*zip.File
|
||||
}
|
||||
|
||||
// Extract begins extract a file from inside a ZipArtifact. It returns an
|
||||
// ExtractedZipArtifactFile handle.
|
||||
func (zf *ZipArtifactFile) Extract(ctx context.Context, dir string,
|
||||
limiter chan bool) *ExtractedZipArtifactFile {
|
||||
|
||||
d := &ExtractedZipArtifactFile{
|
||||
initCh: make(chan struct{}),
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer close(d.initCh)
|
||||
limiter <- true
|
||||
defer func() { <-limiter }()
|
||||
|
||||
zr, err := zf.Open()
|
||||
if err != nil {
|
||||
d.err = err
|
||||
return
|
||||
}
|
||||
defer zr.Close()
|
||||
|
||||
crc := crc32.NewIEEE()
|
||||
r := io.TeeReader(zr, crc)
|
||||
|
||||
if filepath.Clean(zf.Name) != zf.Name {
|
||||
d.err = fmt.Errorf("invalid filename %q", zf.Name)
|
||||
return
|
||||
}
|
||||
path := filepath.Join(dir, zf.Name)
|
||||
|
||||
err = os.MkdirAll(filepath.Dir(path), 0777)
|
||||
if err != nil {
|
||||
d.err = err
|
||||
return
|
||||
}
|
||||
|
||||
err = os.Remove(path)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
d.err = err
|
||||
return
|
||||
}
|
||||
|
||||
if zf.Mode().IsRegular() {
|
||||
w, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, zf.Mode())
|
||||
if err != nil {
|
||||
d.err = err
|
||||
return
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
_, err = io.Copy(w, r)
|
||||
if err != nil {
|
||||
d.err = err
|
||||
return
|
||||
}
|
||||
} else if zf.Mode()&os.ModeSymlink != 0 {
|
||||
target, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
d.err = err
|
||||
return
|
||||
}
|
||||
|
||||
err = os.Symlink(string(target), path)
|
||||
if err != nil {
|
||||
d.err = err
|
||||
return
|
||||
}
|
||||
} else {
|
||||
d.err = fmt.Errorf("unknown mode %q", zf.Mode())
|
||||
return
|
||||
}
|
||||
|
||||
if crc.Sum32() != zf.CRC32 {
|
||||
d.err = fmt.Errorf("crc mismatch for %v", zf.Name)
|
||||
return
|
||||
}
|
||||
|
||||
d.path = path
|
||||
}()
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// ExtractedZipArtifactFile is a handle to a downloaded file from a remoteZipArtifact. The download
|
||||
// may still be in progress, and will be complete with Path() returns.
|
||||
type ExtractedZipArtifactFile struct {
|
||||
initCh chan struct{}
|
||||
err error
|
||||
|
||||
path string
|
||||
}
|
||||
|
||||
// Path returns the path to the downloaded file and any errors that occurred during the download.
|
||||
// It will block until the download is complete.
|
||||
func (d *ExtractedZipArtifactFile) Path() (string, error) {
|
||||
<-d.initCh
|
||||
return d.path, d.err
|
||||
}
|
Reference in New Issue
Block a user