@@ -14,6 +14,7 @@ import (
1414 "os"
1515 "os/exec"
1616 "path/filepath"
17+ "sort"
1718 "strings"
1819 "time"
1920
@@ -316,13 +317,23 @@ func createSquashFs(buildEnv env.ExecEnv, fn, dir string) (int64, error) {
316317 }
317318
318319 // Create reproducible tar archive.
320+ if os .Getenv ("ROFL_DEBUG_ROOTFS_HASH" ) != "" {
321+ if err := debugRootfsHashes (dir ); err != nil {
322+ return 0 , fmt .Errorf ("failed to compute rootfs debug hashes: %w" , err )
323+ }
324+ }
325+
319326 tarPath := fn + ".tar"
327+ tarPathEnv , err := buildEnv .PathToEnv (tarPath )
328+ if err != nil {
329+ return 0 , fmt .Errorf ("failed to translate tar path for container: %w" , err )
330+ }
320331 fmt .Printf ("Creating tar archive: %s\n " , tarPath )
321332 //nolint:gosec // tarPath is constructed internally, not from user input.
322333 tarCmd := exec .Command (
323334 "tar" ,
324335 "--create" ,
325- "--file=" + tarPath ,
336+ "--file=" + tarPathEnv ,
326337 "--format=gnu" ,
327338 "--sort=name" ,
328339 "--mtime=@0" ,
@@ -413,6 +424,69 @@ func createSquashFs(buildEnv env.ExecEnv, fn, dir string) (int64, error) {
413424 return fi .Size (), nil
414425}
415426
427+ // debugRootfsHashes prints deterministic SHA256 hashes for all files in dir to help diagnose
428+ // cross-host differences. Controlled via ROFL_DEBUG_ROOTFS_HASH env var.
429+ func debugRootfsHashes (dir string ) error {
430+ type entry struct {
431+ path string
432+ hash string
433+ }
434+ var entries []entry
435+
436+ err := filepath .WalkDir (dir , func (path string , d fs.DirEntry , err error ) error {
437+ if err != nil {
438+ return err
439+ }
440+ if d .IsDir () {
441+ return nil
442+ }
443+ rel , err := filepath .Rel (dir , path )
444+ if err != nil {
445+ return err
446+ }
447+
448+ fi , err := os .Lstat (path )
449+ if err != nil {
450+ return err
451+ }
452+
453+ switch {
454+ case fi .Mode ()& os .ModeSymlink != 0 :
455+ target , err := os .Readlink (path )
456+ if err != nil {
457+ return err
458+ }
459+ entries = append (entries , entry {path : rel , hash : "symlink->" + target })
460+ default :
461+ f , err := os .Open (path )
462+ if err != nil {
463+ return err
464+ }
465+ h := sha256 .New ()
466+ if _ , err = io .Copy (h , f ); err != nil {
467+ f .Close ()
468+ return err
469+ }
470+ f .Close ()
471+ entries = append (entries , entry {path : rel , hash : fmt .Sprintf ("%x" , h .Sum (nil ))})
472+ }
473+ return nil
474+ })
475+ if err != nil {
476+ return err
477+ }
478+
479+ sort .Slice (entries , func (i , j int ) bool {
480+ return entries [i ].path < entries [j ].path
481+ })
482+
483+ fmt .Println ("Rootfs file hashes (debug):" )
484+ for _ , e := range entries {
485+ fmt .Printf (" %s %s\n " , e .hash , e .path )
486+ }
487+ return nil
488+ }
489+
416490// sha256File computes a SHA-256 digest of the file with the given filename and returns a
417491// hex-encoded hash.
418492func sha256File (fn string ) (string , error ) {
@@ -443,17 +517,32 @@ func createVerityHashTree(buildEnv env.ExecEnv, fsFn, hashFn string) (string, er
443517 return "" , err
444518 }
445519
520+ // Translate paths for container environments.
521+ fsFnEnv , err := buildEnv .PathToEnv (fsFn )
522+ if err != nil {
523+ return "" , fmt .Errorf ("failed to translate filesystem path for container: %w" , err )
524+ }
525+
526+ hashFnEnv , err := buildEnv .PathToEnv (hashFn )
527+ if err != nil {
528+ return "" , fmt .Errorf ("failed to translate hash path for container: %w" , err )
529+ }
530+
446531 rootHashFn := hashFn + ".roothash"
532+ rootHashFnEnv , err := buildEnv .PathToEnv (rootHashFn )
533+ if err != nil {
534+ return "" , fmt .Errorf ("failed to translate roothash path for container: %w" , err )
535+ }
447536
448537 cmd := exec .Command ( //nolint:gosec
449538 veritysetupBin , "format" ,
450539 "--data-block-size=4096" ,
451540 "--hash-block-size=4096" ,
452541 "--uuid=00000000-0000-0000-0000-000000000000" ,
453542 "--salt=" + salt ,
454- "--root-hash-file=" + rootHashFn ,
455- fsFn ,
456- hashFn ,
543+ "--root-hash-file=" + rootHashFnEnv ,
544+ fsFnEnv ,
545+ hashFnEnv ,
457546 )
458547 var out strings.Builder
459548 cmd .Stderr = & out
@@ -556,14 +645,22 @@ func convertToQcow2(buildEnv env.ExecEnv, fn string) error {
556645 }
557646
558647 tmpOutFn := fn + ".qcow2"
648+ fnEnv , err := buildEnv .PathToEnv (fn )
649+ if err != nil {
650+ return fmt .Errorf ("failed to translate qcow input path for container: %w" , err )
651+ }
652+ tmpOutFnEnv , err := buildEnv .PathToEnv (tmpOutFn )
653+ if err != nil {
654+ return fmt .Errorf ("failed to translate qcow output path for container: %w" , err )
655+ }
559656
560657 // Execute qemu-img.
561658 cmd := exec .Command (
562659 qemuImgBin ,
563660 "convert" ,
564661 "-O" , "qcow2" ,
565- fn ,
566- tmpOutFn ,
662+ fnEnv ,
663+ tmpOutFnEnv ,
567664 )
568665 var out strings.Builder
569666 cmd .Stderr = & out
0 commit comments