Skip to content

Commit 92b87b2

Browse files
committed
cmd/rofl/build: Improve reproducible builds with squashfs-tools
1 parent 085bb7b commit 92b87b2

File tree

1 file changed

+88
-9
lines changed

1 file changed

+88
-9
lines changed

cmd/rofl/build/artifacts.go

Lines changed: 88 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -287,29 +287,108 @@ func ensureBinaryExists(buildEnv env.ExecEnv, name, pkg string) error {
287287
return nil
288288
}
289289

290+
// cleanEnvForReproducibility filters out locale and timestamp environment variables and sets
291+
// consistent values for reproducible builds.
292+
func cleanEnvForReproducibility() []string {
293+
env := []string{}
294+
for _, e := range os.Environ() {
295+
if !strings.HasPrefix(e, "LC_") && !strings.HasPrefix(e, "LANG=") && !strings.HasPrefix(e, "TZ=") && !strings.HasPrefix(e, "SOURCE_DATE_EPOCH=") {
296+
env = append(env, e)
297+
}
298+
}
299+
return append(env, "LC_ALL=C", "TZ=UTC", "SOURCE_DATE_EPOCH=0")
300+
}
301+
290302
// createSquashFs creates a squashfs filesystem in the given file using directory dir to populate
291303
// it.
292304
//
293305
// Returns the size of the created filesystem image in bytes.
294306
func createSquashFs(buildEnv env.ExecEnv, fn, dir string) (int64, error) {
295-
const mkSquashFsBin = "mksquashfs"
296-
if err := ensureBinaryExists(buildEnv, mkSquashFsBin, "squashfs-tools"); err != nil {
307+
const (
308+
sqfstarBin = "sqfstar"
309+
fakerootBin = "fakeroot"
310+
)
311+
if err := ensureBinaryExists(buildEnv, sqfstarBin, "squashfs-tools"); err != nil {
312+
return 0, err
313+
}
314+
if err := ensureBinaryExists(buildEnv, fakerootBin, fakerootBin); err != nil {
315+
return 0, err
316+
}
317+
318+
// Create reproducible tar archive.
319+
tarPath := fn + ".tar"
320+
fmt.Printf("Creating tar archive: %s\n", tarPath)
321+
//nolint:gosec // tarPath is constructed internally, not from user input.
322+
tarCmd := exec.Command(
323+
"tar",
324+
"--create",
325+
"--file="+tarPath,
326+
"--format=gnu",
327+
"--sort=name",
328+
"--mtime=@0",
329+
"--owner=0",
330+
"--group=0",
331+
"--numeric-owner",
332+
"--mode=a-s",
333+
".",
334+
)
335+
tarCmd.Dir = dir
336+
tarCmd.Env = cleanEnvForReproducibility()
337+
338+
var tarOut strings.Builder
339+
tarCmd.Stderr = &tarOut
340+
tarCmd.Stdout = &tarOut
341+
if err := buildEnv.WrapCommand(tarCmd); err != nil {
297342
return 0, err
298343
}
344+
if err := tarCmd.Run(); err != nil {
345+
return 0, fmt.Errorf("failed to create tar archive: %w\n%s", err, tarOut.String())
346+
}
347+
348+
// Compute and print tar archive hash for verification.
349+
tarFile, err := os.Open(tarPath)
350+
if err != nil {
351+
return 0, fmt.Errorf("failed to open tar archive for hashing: %w", err)
352+
}
353+
tarHasher := sha256.New()
354+
if _, err := io.Copy(tarHasher, tarFile); err != nil {
355+
tarFile.Close()
356+
return 0, fmt.Errorf("failed to hash tar archive: %w", err)
357+
}
358+
tarFile.Close()
359+
tarHash := hex.EncodeToString(tarHasher.Sum(nil))
360+
fmt.Printf("TAR archive SHA256: %s\n", tarHash)
299361

300-
// Execute mksquashfs.
362+
// Convert tar to squashfs using sqfstar under fakeroot.
301363
cmd := exec.Command(
302-
mkSquashFsBin,
303-
dir,
364+
fakerootBin,
365+
"--",
366+
sqfstarBin,
304367
fn,
368+
"-reproducible",
305369
"-comp", "gzip",
370+
"-b", "1M",
371+
"-processors", "1",
306372
"-noappend",
307-
"-mkfs-time", "1234",
308-
"-all-time", "1234",
309-
"-root-time", "1234",
373+
"-mkfs-time", "0",
374+
"-all-time", "0",
375+
"-nopad",
310376
"-all-root",
311-
"-reproducible",
377+
"-force-uid", "0",
378+
"-force-gid", "0",
312379
)
380+
381+
cmd.Env = cleanEnvForReproducibility()
382+
383+
// Open tar file and pipe it to sqfstar's stdin.
384+
tarFile, err = os.Open(tarPath)
385+
if err != nil {
386+
return 0, fmt.Errorf("failed to open tar archive: %w", err)
387+
}
388+
defer tarFile.Close()
389+
defer os.Remove(tarPath)
390+
391+
cmd.Stdin = tarFile
313392
var out strings.Builder
314393
cmd.Stderr = &out
315394
cmd.Stdout = &out

0 commit comments

Comments
 (0)