@@ -53,20 +53,28 @@ type Renamer func(string) string
5353// handle the names of the files.
5454// If the file is not an archive, an error is returned.
5555func Archive (body io.Reader , location string , rename Renamer ) error {
56+ dummy := make (chan bool , 1 )
57+ defer close (dummy )
58+ return ArchiveCancel (body , location , rename , dummy )
59+ }
60+
61+ // ArchiveCancel is the same as Archive but with an extra channel to stop
62+ // the extraction.
63+ func ArchiveCancel (body io.Reader , location string , rename Renamer , cancel <- chan bool ) error {
5664 body , kind , err := match (body )
5765 if err != nil {
5866 errors .Annotatef (err , "Detect archive type" )
5967 }
6068
6169 switch kind .Extension {
6270 case "zip" :
63- return Zip (body , location , rename )
71+ return ZipCancel (body , location , rename , cancel )
6472 case "gz" :
65- return Gz (body , location , rename )
73+ return GzCancel (body , location , rename , cancel )
6674 case "bz2" :
67- return Bz2 (body , location , rename )
75+ return Bz2Cancel (body , location , rename , cancel )
6876 case "tar" :
69- return Tar (body , location , rename )
77+ return TarCancel (body , location , rename , cancel )
7078 default :
7179 return errors .New ("Not a supported archive" )
7280 }
@@ -75,6 +83,13 @@ func Archive(body io.Reader, location string, rename Renamer) error {
7583// Bz2 extracts a .bz2 or .tar.bz2 archived stream of data in the specified location.
7684// It accepts a rename function to handle the names of the files (see the example)
7785func Bz2 (body io.Reader , location string , rename Renamer ) error {
86+ dummy := make (chan bool , 1 )
87+ defer close (dummy )
88+ return Bz2Cancel (body , location , rename , dummy )
89+ }
90+
91+ // Bz2Cancel is the same as Bz2 but with an extra channel to stop the extraction.
92+ func Bz2Cancel (body io.Reader , location string , rename Renamer , cancel <- chan bool ) error {
7893 reader := bzip2 .NewReader (body )
7994
8095 body , kind , err := match (reader )
@@ -83,10 +98,10 @@ func Bz2(body io.Reader, location string, rename Renamer) error {
8398 }
8499
85100 if kind .Extension == "tar" {
86- return Tar (body , location , rename )
101+ return TarCancel (body , location , rename , cancel )
87102 }
88103
89- err = copy (location , 0666 , body )
104+ err = copy (location , 0666 , body , cancel )
90105 if err != nil {
91106 return err
92107 }
@@ -96,6 +111,13 @@ func Bz2(body io.Reader, location string, rename Renamer) error {
96111// Gz extracts a .gz or .tar.gz archived stream of data in the specified location.
97112// It accepts a rename function to handle the names of the files (see the example)
98113func Gz (body io.Reader , location string , rename Renamer ) error {
114+ dummy := make (chan bool , 1 )
115+ defer close (dummy )
116+ return GzCancel (body , location , rename , dummy )
117+ }
118+
119+ // GzCancel is the same as Gz but with an extra channel to stop the extraction.
120+ func GzCancel (body io.Reader , location string , rename Renamer , cancel <- chan bool ) error {
99121 reader , err := gzip .NewReader (body )
100122 if err != nil {
101123 return errors .Annotatef (err , "Gunzip" )
@@ -107,9 +129,9 @@ func Gz(body io.Reader, location string, rename Renamer) error {
107129 }
108130
109131 if kind .Extension == "tar" {
110- return Tar (body , location , rename )
132+ return TarCancel (body , location , rename , cancel )
111133 }
112- err = copy (location , 0666 , body )
134+ err = copy (location , 0666 , body , cancel )
113135 if err != nil {
114136 return err
115137 }
@@ -129,6 +151,13 @@ type link struct {
129151// Tar extracts a .tar archived stream of data in the specified location.
130152// It accepts a rename function to handle the names of the files (see the example)
131153func Tar (body io.Reader , location string , rename Renamer ) error {
154+ dummy := make (chan bool , 1 )
155+ defer close (dummy )
156+ return TarCancel (body , location , rename , dummy )
157+ }
158+
159+ // TarCancel is the same as Tar but with an extra channel to stop the extraction.
160+ func TarCancel (body io.Reader , location string , rename Renamer , cancel <- chan bool ) error {
132161 files := []file {}
133162 links := []link {}
134163 symlinks := []link {}
@@ -137,6 +166,12 @@ func Tar(body io.Reader, location string, rename Renamer) error {
137166 // attempting to create a file where there's no folder
138167 tr := tar .NewReader (body )
139168 for {
169+ select {
170+ case <- cancel :
171+ return errors .New ("interrupted" )
172+ default :
173+ }
174+
140175 header , err := tr .Next ()
141176 if err == io .EOF {
142177 break
@@ -165,7 +200,7 @@ func Tar(body io.Reader, location string, rename Renamer) error {
165200 }
166201 case tar .TypeReg , tar .TypeRegA :
167202 var data bytes.Buffer
168- if _ , err := io . Copy (& data , tr ); err != nil {
203+ if _ , err := copyCancel (& data , tr , cancel ); err != nil {
169204 return errors .Annotatef (err , "Read contents of file %s" , path )
170205 }
171206 files = append (files , file {Path : path , Mode : info .Mode (), Data : data })
@@ -184,18 +219,28 @@ func Tar(body io.Reader, location string, rename Renamer) error {
184219
185220 // Now we make another pass creating the files and links
186221 for i := range files {
187- if err := copy (files [i ].Path , files [i ].Mode , & files [i ].Data ); err != nil {
222+ if err := copy (files [i ].Path , files [i ].Mode , & files [i ].Data , cancel ); err != nil {
188223 return errors .Annotatef (err , "Create file %s" , files [i ].Path )
189224 }
190225 }
191226
192227 for i := range links {
228+ select {
229+ case <- cancel :
230+ return errors .New ("interrupted" )
231+ default :
232+ }
193233 if err := os .Link (links [i ].Name , links [i ].Path ); err != nil {
194234 return errors .Annotatef (err , "Create link %s" , links [i ].Path )
195235 }
196236 }
197237
198238 for i := range symlinks {
239+ select {
240+ case <- cancel :
241+ return errors .New ("interrupted" )
242+ default :
243+ }
199244 if err := os .Symlink (symlinks [i ].Name , symlinks [i ].Path ); err != nil {
200245 return errors .Annotatef (err , "Create link %s" , symlinks [i ].Path )
201246 }
@@ -206,9 +251,16 @@ func Tar(body io.Reader, location string, rename Renamer) error {
206251// Zip extracts a .zip archived stream of data in the specified location.
207252// It accepts a rename function to handle the names of the files (see the example).
208253func Zip (body io.Reader , location string , rename Renamer ) error {
254+ dummy := make (chan bool , 1 )
255+ defer close (dummy )
256+ return ZipCancel (body , location , rename , dummy )
257+ }
258+
259+ // ZipCancel is the same as Bz2 but with an extra channel to stop the extraction.
260+ func ZipCancel (body io.Reader , location string , rename Renamer , cancel <- chan bool ) error {
209261 // read the whole body into a buffer. Not sure this is the best way to do it
210262 buffer := bytes .NewBuffer ([]byte {})
211- io . Copy (buffer , body )
263+ copyCancel (buffer , body , cancel )
212264
213265 archive , err := zip .NewReader (bytes .NewReader (buffer .Bytes ()), int64 (buffer .Len ()))
214266 if err != nil {
@@ -221,6 +273,12 @@ func Zip(body io.Reader, location string, rename Renamer) error {
221273 // We make the first pass creating the directory structure, or we could end up
222274 // attempting to create a file where there's no folder
223275 for _ , header := range archive .File {
276+ select {
277+ case <- cancel :
278+ return errors .New ("interrupted" )
279+ default :
280+ }
281+
224282 path := header .Name
225283 if rename != nil {
226284 path = rename (path )
@@ -255,7 +313,7 @@ func Zip(body io.Reader, location string, rename Renamer) error {
255313 return errors .Annotatef (err , "Open file %s" , path )
256314 }
257315 var data bytes.Buffer
258- if _ , err := io . Copy (& data , f ); err != nil {
316+ if _ , err := copyCancel (& data , f , cancel ); err != nil {
259317 return errors .Annotatef (err , "Read contents of file %s" , path )
260318 }
261319 files = append (files , file {Path : path , Mode : info .Mode (), Data : data })
@@ -264,12 +322,17 @@ func Zip(body io.Reader, location string, rename Renamer) error {
264322
265323 // Now we make another pass creating the files and links
266324 for i := range files {
267- if err := copy (files [i ].Path , files [i ].Mode , & files [i ].Data ); err != nil {
325+ if err := copy (files [i ].Path , files [i ].Mode , & files [i ].Data , cancel ); err != nil {
268326 return errors .Annotatef (err , "Create file %s" , files [i ].Path )
269327 }
270328 }
271329
272330 for i := range links {
331+ select {
332+ case <- cancel :
333+ return errors .New ("interrupted" )
334+ default :
335+ }
273336 if err := os .Symlink (links [i ].Name , links [i ].Path ); err != nil {
274337 return errors .Annotatef (err , "Create link %s" , links [i ].Path )
275338 }
@@ -278,7 +341,7 @@ func Zip(body io.Reader, location string, rename Renamer) error {
278341 return nil
279342}
280343
281- func copy (path string , mode os.FileMode , src io.Reader ) error {
344+ func copy (path string , mode os.FileMode , src io.Reader , cancel <- chan bool ) error {
282345 // We add the execution permission to be able to create files inside it
283346 err := os .MkdirAll (filepath .Dir (path ), mode | os .ModeDir | 100 )
284347 if err != nil {
@@ -289,7 +352,7 @@ func copy(path string, mode os.FileMode, src io.Reader) error {
289352 return err
290353 }
291354 defer file .Close ()
292- _ , err = io . Copy (file , src )
355+ _ , err = copyCancel (file , src , cancel )
293356 return err
294357}
295358
0 commit comments