@@ -2,6 +2,8 @@ package dockerbuild
22
33import (
44 "archive/tar"
5+ "bytes"
6+ "fmt"
57 "io"
68 "io/fs"
79 "os"
@@ -11,21 +13,24 @@ import (
1113 "strings"
1214 "time"
1315
14- "encr.dev/pkg/xos"
15- "encr.dev/v2/compiler/build"
1616 "github.com/cockroachdb/errors"
17+ "github.com/google/go-containerregistry/pkg/v1/tarball"
1718 "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"
1824)
1925
2026type tarCopier struct {
2127 fileTimes * time.Time
22- tw * tar. Writer
28+ entries [] * tarEntry
2329 seenDirs map [ImagePath ]bool
2430}
2531
26- func newTarCopier (tw * tar. Writer , opts ... tarCopyOption ) * tarCopier {
32+ func newTarCopier (opts ... tarCopyOption ) * tarCopier {
2733 tc := & tarCopier {
28- tw : tw ,
2934 seenDirs : make (map [ImagePath ]bool ),
3035 }
3136 for _ , opt := range opts {
@@ -260,12 +265,12 @@ func (tc *tarCopier) MkdirAll(dstPath ImagePath, mode fs.FileMode) (err error) {
260265 header := & tar.Header {
261266 Typeflag : tar .TypeDir ,
262267 ModTime : modTime ,
263- Name : (dstPath + "/" ). String (), // from [archive/tar.FileInfoHeader]
268+ Name : tarHeaderName (dstPath , true ),
264269 Mode : int64 (mode .Perm ()),
265270 }
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+ })
269274 tc .seenDirs [dstPath ] = true
270275 }
271276
@@ -292,10 +297,9 @@ func (tc *tarCopier) CopyFile(dstPath ImagePath, srcPath HostPath, fi fs.FileInf
292297 header .Mode = 0755
293298 }
294299
295- header .Name = filepath .ToSlash (dstPath .String ())
296- if err := tc .tw .WriteHeader (header ); err != nil {
297- return errors .Wrap (err , "write tar header" )
298- }
300+ header .Name = tarHeaderName (dstPath , fi .IsDir ())
301+ entry := & tarEntry {header : header }
302+ tc .entries = append (tc .entries , entry )
299303
300304 if fi .IsDir () {
301305 tc .seenDirs [dstPath ] = true
@@ -304,28 +308,15 @@ func (tc *tarCopier) CopyFile(dstPath ImagePath, srcPath HostPath, fi fs.FileInf
304308
305309 // If this is not a symlink, write the file.
306310 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- }
311+ entry .hostPath = option .Some (srcPath )
321312 }
322313
323314 return nil
324315}
325316
326- func (tc * tarCopier ) WriteFile (dstPath string , mode fs.FileMode , data []byte ) (err error ) {
317+ func (tc * tarCopier ) WriteFile (dstPath ImagePath , mode fs.FileMode , data []byte ) (err error ) {
327318 header := & tar.Header {
328- Name : dstPath ,
319+ Name : tarHeaderName ( dstPath , false ) ,
329320 Typeflag : tar .TypeReg ,
330321 Mode : int64 (mode .Perm ()),
331322 Size : int64 (len (data )),
@@ -337,11 +328,84 @@ func (tc *tarCopier) WriteFile(dstPath string, mode fs.FileMode, data []byte) (e
337328 header .ChangeTime = t
338329 }
339330
340- header .Name = filepath .ToSlash (dstPath )
341- if err := tc .tw .WriteHeader (header ); err != nil {
342- return errors .Wrap (err , "write tar header" )
331+ tc .entries = append (tc .entries , & tarEntry {
332+ header : header ,
333+ data : option .Some (data ),
334+ })
335+ return nil
336+ }
337+
338+ type tarEntry struct {
339+ header * tar.Header
340+
341+ data option.Option [[]byte ]
342+ hostPath option.Option [HostPath ]
343+ }
344+
345+ func (tc * tarCopier ) Opener () tarball.Opener {
346+ errThunk := func (err error ) tarball.Opener {
347+ return func () (io.ReadCloser , error ) {
348+ return nil , err
349+ }
350+ }
351+
352+ var dvecs []tarstream.Datavec
353+ for _ , e := range tc .entries {
354+ log .Info ().Str ("file" , e .header .Name ).Interface ("header" , e .header ).Msg ("processing file" )
355+ // create buffer to write tar header to
356+ buf := new (bytes.Buffer )
357+ tw := tar .NewWriter (buf )
358+
359+ // write tar header to buffer
360+ if err := tw .WriteHeader (e .header ); err != nil {
361+ return errThunk (errors .Wrap (err , fmt .Sprintf ("writing header %v" , e )))
362+ }
363+
364+ memv := tarstream.MemVec {
365+ Data : buf .Bytes (),
366+ }
367+
368+ // add the tar header mem buffer to the tarvec
369+ dvecs = append (dvecs , memv )
370+
371+ var dataEntry tarstream.Datavec
372+ if hostPath , ok := e .hostPath .Get (); ok {
373+ fi := e .header .FileInfo ()
374+ dataEntry = & tarstream.PathVec {
375+ Path : hostPath .String (),
376+ Info : fi ,
377+ }
378+ } else if data , ok := e .data .Get (); ok {
379+ dataEntry = tarstream.MemVec {Data : data }
380+ }
381+
382+ if dataEntry != nil {
383+ // add the file path info to the tarvec
384+ dvecs = append (dvecs , dataEntry )
385+
386+ // tar requires file entries to be padded out to 512 bytes.
387+ if ! e .header .FileInfo ().IsDir () {
388+ if size := dataEntry .GetSize (); size % 512 != 0 {
389+ padv := tarstream.PadVec {
390+ Size : 512 - (size % 512 ),
391+ }
392+ dvecs = append (dvecs , padv )
393+ }
394+ }
395+ }
343396 }
344397
345- _ , err = tc .tw .Write (data )
346- return errors .Wrap (err , "write file" )
398+ tv := tarstream .NewTarVec (dvecs )
399+ return func () (io.ReadCloser , error ) {
400+ tv2 := tv .Clone ()
401+ return tv2 , nil
402+ }
403+ }
404+
405+ func tarHeaderName (p ImagePath , isDir bool ) string {
406+ name := strings .TrimPrefix (filepath .ToSlash (p .String ()), "/" )
407+ if isDir {
408+ name += "/"
409+ }
410+ return name
347411}
0 commit comments