github.com/nikkelma/oras-project_oras-go@v1.1.1-0.20220201001104-a75f6a419090/pkg/content/untar.go (about) 1 /* 2 Copyright The ORAS Authors. 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 16 package content 17 18 import ( 19 "archive/tar" 20 "fmt" 21 "io" 22 23 "github.com/containerd/containerd/content" 24 ) 25 26 // NewUntarWriter wrap a writer with an untar, so that the stream is untarred 27 // 28 // By default, it calculates the hash when writing. If the option `skipHash` is true, 29 // it will skip doing the hash. Skipping the hash is intended to be used only 30 // if you are confident about the validity of the data being passed to the writer, 31 // and wish to save on the hashing time. 32 func NewUntarWriter(writer content.Writer, opts ...WriterOpt) content.Writer { 33 // process opts for default 34 wOpts := DefaultWriterOpts() 35 for _, opt := range opts { 36 if err := opt(&wOpts); err != nil { 37 return nil 38 } 39 } 40 41 return NewPassthroughWriter(writer, func(r io.Reader, w io.Writer, done chan<- error) { 42 tr := tar.NewReader(r) 43 var err error 44 for { 45 _, err := tr.Next() 46 if err == io.EOF { 47 // clear the error, since we do not pass an io.EOF 48 err = nil 49 break // End of archive 50 } 51 if err != nil { 52 // pass the error on 53 err = fmt.Errorf("UntarWriter tar file header read error: %v", err) 54 break 55 } 56 // write out the untarred data 57 // we can handle io.EOF, just go to the next file 58 // any other errors should stop and get reported 59 b := make([]byte, wOpts.Blocksize, wOpts.Blocksize) 60 for { 61 var n int 62 n, err = tr.Read(b) 63 if err != nil && err != io.EOF { 64 err = fmt.Errorf("UntarWriter file data read error: %v\n", err) 65 break 66 } 67 l := n 68 if n > len(b) { 69 l = len(b) 70 } 71 if _, err2 := w.Write(b[:l]); err2 != nil { 72 err = fmt.Errorf("UntarWriter error writing to underlying writer: %v", err2) 73 break 74 } 75 if err == io.EOF { 76 // go to the next file 77 break 78 } 79 } 80 // did we break with a non-nil and non-EOF error? 81 if err != nil && err != io.EOF { 82 break 83 } 84 } 85 done <- err 86 }, opts...) 87 } 88 89 // NewUntarWriterByName wrap multiple writers with an untar, so that the stream is untarred and passed 90 // to the appropriate writer, based on the filename. If a filename is not found, it is up to the called func 91 // to determine how to process it. 92 func NewUntarWriterByName(writers func(string) (content.Writer, error), opts ...WriterOpt) content.Writer { 93 // process opts for default 94 wOpts := DefaultWriterOpts() 95 for _, opt := range opts { 96 if err := opt(&wOpts); err != nil { 97 return nil 98 } 99 } 100 101 // need a PassthroughMultiWriter here 102 return NewPassthroughMultiWriter(writers, func(r io.Reader, getwriter func(name string) io.Writer, done chan<- error) { 103 tr := tar.NewReader(r) 104 var err error 105 for { 106 header, err := tr.Next() 107 if err == io.EOF { 108 // clear the error, since we do not pass an io.EOF 109 err = nil 110 break // End of archive 111 } 112 if err != nil { 113 // pass the error on 114 err = fmt.Errorf("UntarWriter tar file header read error: %v", err) 115 break 116 } 117 // get the filename 118 filename := header.Name 119 120 // get the writer for this filename 121 w := getwriter(filename) 122 if w == nil { 123 continue 124 } 125 126 // write out the untarred data 127 // we can handle io.EOF, just go to the next file 128 // any other errors should stop and get reported 129 b := make([]byte, wOpts.Blocksize, wOpts.Blocksize) 130 for { 131 var n int 132 n, err = tr.Read(b) 133 if err != nil && err != io.EOF { 134 err = fmt.Errorf("UntarWriter file data read error: %v\n", err) 135 break 136 } 137 l := n 138 if n > len(b) { 139 l = len(b) 140 } 141 if _, err2 := w.Write(b[:l]); err2 != nil { 142 err = fmt.Errorf("UntarWriter error writing to underlying writer at for name '%s': %v", filename, err2) 143 break 144 } 145 if err == io.EOF { 146 // go to the next file 147 break 148 } 149 } 150 // did we break with a non-nil and non-EOF error? 151 if err != nil && err != io.EOF { 152 break 153 } 154 } 155 done <- err 156 }, opts...) 157 }