@@ -2,6 +2,8 @@ package dockerbuild
2
2
3
3
import (
4
4
"archive/tar"
5
+ "bytes"
6
+ "fmt"
5
7
"io"
6
8
"io/fs"
7
9
"os"
@@ -11,21 +13,24 @@ import (
11
13
"strings"
12
14
"time"
13
15
14
- "encr.dev/pkg/xos"
15
- "encr.dev/v2/compiler/build"
16
16
"github.com/cockroachdb/errors"
17
+ "github.com/google/go-containerregistry/pkg/v1/tarball"
17
18
"github.com/rs/zerolog/log"
19
+
20
+ "encr.dev/pkg/option"
21
+ "encr.dev/pkg/tarstream"
22
+ "encr.dev/pkg/xos"
23
+ "encr.dev/v2/compiler/build"
18
24
)
19
25
20
26
type tarCopier struct {
21
27
fileTimes * time.Time
22
- tw * tar. Writer
28
+ entries [] * tarEntry
23
29
seenDirs map [ImagePath ]bool
24
30
}
25
31
26
- func newTarCopier (tw * tar. Writer , opts ... tarCopyOption ) * tarCopier {
32
+ func newTarCopier (opts ... tarCopyOption ) * tarCopier {
27
33
tc := & tarCopier {
28
- tw : tw ,
29
34
seenDirs : make (map [ImagePath ]bool ),
30
35
}
31
36
for _ , opt := range opts {
@@ -263,9 +268,9 @@ func (tc *tarCopier) MkdirAll(dstPath ImagePath, mode fs.FileMode) (err error) {
263
268
Name : (dstPath + "/" ).String (), // from [archive/tar.FileInfoHeader]
264
269
Mode : int64 (mode .Perm ()),
265
270
}
266
- if err := tc .tw . WriteHeader ( header ); err != nil {
267
- return errors . Wrap ( err , "write tar header" )
268
- }
271
+ tc . entries = append ( tc .entries , & tarEntry {
272
+ header : header ,
273
+ })
269
274
tc .seenDirs [dstPath ] = true
270
275
}
271
276
@@ -293,9 +298,10 @@ func (tc *tarCopier) CopyFile(dstPath ImagePath, srcPath HostPath, fi fs.FileInf
293
298
}
294
299
295
300
header .Name = filepath .ToSlash (dstPath .String ())
296
- if err := tc . tw . WriteHeader ( header ); err != nil {
297
- return errors . Wrap ( err , "write tar header" )
301
+ entry := & tarEntry {
302
+ header : header ,
298
303
}
304
+ tc .entries = append (tc .entries , entry )
299
305
300
306
if fi .IsDir () {
301
307
tc .seenDirs [dstPath ] = true
@@ -304,28 +310,15 @@ func (tc *tarCopier) CopyFile(dstPath ImagePath, srcPath HostPath, fi fs.FileInf
304
310
305
311
// If this is not a symlink, write the file.
306
312
if (fi .Mode () & fs .ModeSymlink ) != fs .ModeSymlink {
307
- // Write the file
308
- f , err := os .Open (srcPath .String ())
309
- if err != nil {
310
- return errors .Wrap (err , "open file" )
311
- }
312
- defer func () {
313
- if closeErr := f .Close (); err == nil {
314
- err = errors .Wrap (closeErr , "close file" )
315
- }
316
- }()
317
-
318
- if _ , err = io .Copy (tc .tw , f ); err != nil {
319
- return errors .Wrap (err , "copy file" )
320
- }
313
+ entry .hostPath = option .Some (srcPath )
321
314
}
322
315
323
316
return nil
324
317
}
325
318
326
- func (tc * tarCopier ) WriteFile (dstPath string , mode fs.FileMode , data []byte ) (err error ) {
319
+ func (tc * tarCopier ) WriteFile (dstPath ImagePath , mode fs.FileMode , data []byte ) (err error ) {
327
320
header := & tar.Header {
328
- Name : dstPath ,
321
+ Name : filepath . ToSlash ( dstPath . String ()) ,
329
322
Typeflag : tar .TypeReg ,
330
323
Mode : int64 (mode .Perm ()),
331
324
Size : int64 (len (data )),
@@ -337,11 +330,83 @@ func (tc *tarCopier) WriteFile(dstPath string, mode fs.FileMode, data []byte) (e
337
330
header .ChangeTime = t
338
331
}
339
332
340
- header .Name = filepath .ToSlash (dstPath )
341
- if err := tc .tw .WriteHeader (header ); err != nil {
342
- return errors .Wrap (err , "write tar header" )
333
+ tc .entries = append (tc .entries , & tarEntry {
334
+ header : header ,
335
+ data : option .Some (data ),
336
+ })
337
+ return nil
338
+ }
339
+
340
+ type tarEntry struct {
341
+ header * tar.Header
342
+
343
+ data option.Option [[]byte ]
344
+ hostPath option.Option [HostPath ]
345
+ }
346
+
347
+ func (tc * tarCopier ) Opener () tarball.Opener {
348
+ errThunk := func (err error ) tarball.Opener {
349
+ return func () (io.ReadCloser , error ) {
350
+ return nil , err
351
+ }
343
352
}
344
353
345
- _ , err = tc .tw .Write (data )
346
- return errors .Wrap (err , "write file" )
354
+ var tv tarstream.TarVec
355
+ for _ , e := range tc .entries {
356
+ // create buffer to write tar header to
357
+ buf := new (bytes.Buffer )
358
+ tw := tar .NewWriter (buf )
359
+
360
+ // write tar header to buffer
361
+ if err := tw .WriteHeader (e .header ); err != nil {
362
+ return errThunk (errors .Wrap (err , fmt .Sprintf ("writing header %v" , e )))
363
+ }
364
+
365
+ memv := tarstream.MemVec {
366
+ Data : buf .Bytes (),
367
+ }
368
+
369
+ // add the tar header mem buffer to the tarvec
370
+ tv .Dvecs = append (tv .Dvecs , memv )
371
+ tv .Size += memv .GetSize ()
372
+
373
+ var dataEntry tarstream.Datavec
374
+ if hostPath , ok := e .hostPath .Get (); ok {
375
+ fi := e .header .FileInfo ()
376
+ dataEntry = & tarstream.PathVec {
377
+ Path : hostPath .String (),
378
+ Info : fi ,
379
+ }
380
+ } else if data , ok := e .data .Get (); ok {
381
+ dataEntry = tarstream.MemVec {Data : data }
382
+ }
383
+
384
+ if dataEntry != nil {
385
+ // add the file path info to the tarvec
386
+ size := dataEntry .GetSize ()
387
+ tv .Size += size
388
+ tv .Dvecs = append (tv .Dvecs , dataEntry )
389
+
390
+ // tar requires file entries to be padded out to
391
+ // 512 byte offset
392
+ // if needed, record how much padding is needed
393
+ // and add to the tarvec
394
+ if size % 512 != 0 {
395
+ padv := tarstream.PadVec {
396
+ Size : 512 - (size % 512 ),
397
+ }
398
+
399
+ tv .Dvecs = append (tv .Dvecs , padv )
400
+ tv .Size += padv .GetSize ()
401
+ }
402
+ }
403
+ }
404
+
405
+ tv .ComputeSize ()
406
+ tv .Pos = 0
407
+
408
+ return func () (io.ReadCloser , error ) {
409
+ tv2 := tv .Clone ()
410
+ return tv2 , nil
411
+ }
347
412
}
0 commit comments