go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cipd/appengine/impl/repo/processing/reader.go (about) 1 // Copyright 2018 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package processing 16 17 import ( 18 "io" 19 "math" 20 21 "github.com/klauspost/compress/zip" 22 23 "go.chromium.org/luci/common/errors" 24 ) 25 26 // TODO(vadimsh): Share code with the client. 27 28 // PackageReader knows how to extract files from CIPD packages. 29 // 30 // CIPD packages are actually zip archives, but we don't want to expose it 31 // everywhere. 32 type PackageReader struct { 33 zr *zip.Reader 34 } 35 36 // NewPackageReader opens the package by reading its directory. 37 func NewPackageReader(r io.ReaderAt, size int64) (*PackageReader, error) { 38 zr, err := zip.NewReader(r, size) 39 if err != nil { 40 // Note: we rely here (and in other places where we return errors) on 41 // zip.Reader NOT wrapping errors from 'r', so they inherit transient tags 42 // in case of transient Google Storage errors. 43 return nil, err 44 } 45 return &PackageReader{zr}, nil 46 } 47 48 // Files returns names of files inside the package. 49 func (p *PackageReader) Files() []string { 50 files := make([]string, len(p.zr.File)) 51 for i, f := range p.zr.File { 52 files[i] = f.Name 53 } 54 return files 55 } 56 57 // Open opens some file inside the package for reading. 58 // 59 // Returns the ReadCloser and the uncompressed file size. 60 func (p *PackageReader) Open(path string) (io.ReadCloser, int64, error) { 61 for _, f := range p.zr.File { 62 if f.Name == path { 63 if f.UncompressedSize64 > math.MaxInt64 { 64 return nil, 0, errors.Reason("the file %q is unbelievably huge (%d bytes)", path, f.UncompressedSize64).Err() 65 } 66 rc, err := f.Open() 67 if err != nil { 68 return nil, 0, err 69 } 70 return rc, int64(f.UncompressedSize64), nil 71 } 72 } 73 return nil, 0, errors.Reason("no file %q inside the package", path).Err() 74 }