From a3a92356d9a3da25bce659b7b967961eeb8ab956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Sat, 5 Apr 2025 11:21:08 +0100 Subject: [PATCH] refuse to delete unknown files with -debugdir When creating a new debugdir directory, add a sentinel .garble-debugdir file at its root so that we can later know that we created it and the user is very unlikely to have left important data there. When emptying an existing debugdir directory, only do so if it has that sentinel file, for added safety. Fixes #932. --- main.go | 22 ++++++++++++++++++---- testdata/script/debugdir.txtar | 16 ++++++++++++++-- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index 14ce0b9..7a96b4a 100644 --- a/main.go +++ b/main.go @@ -602,16 +602,30 @@ This command wraps "go %s". Below is its help: os.Setenv("GARBLE_SHARED", sharedTempDir) if flagDebugDir != "" { + origDir := flagDebugDir flagDebugDir, err = filepath.Abs(flagDebugDir) if err != nil { return nil, err } - - if err := os.RemoveAll(flagDebugDir); err != nil { - return nil, fmt.Errorf("could not empty debugdir: %v", err) + sentinel := filepath.Join(flagDebugDir, ".garble-debugdir") + if entries, err := os.ReadDir(flagDebugDir); errors.Is(err, fs.ErrNotExist) { + } else if err == nil && len(entries) == 0 { + // It's OK to delete an existing directory as long as it's empty. + } else if _, err := os.Lstat(sentinel); err == nil { + // It's OK to delete a non-empty directory which was created by an earlier + // invocation of `garble -debugdir`, which we know by leaving a sentinel file. + if err := os.RemoveAll(flagDebugDir); err != nil { + return nil, fmt.Errorf("could not empty debugdir: %v", err) + } + } else { + return nil, fmt.Errorf("debugdir %q has unknown contents; empty it first", origDir) } + if err := os.MkdirAll(flagDebugDir, 0o755); err != nil { - return nil, err + return nil, fmt.Errorf("could not create debugdir directory: %v", err) + } + if err := os.WriteFile(sentinel, nil, 0o666); err != nil { + return nil, fmt.Errorf("could not create debugdir sentinel: %v", err) } } diff --git a/testdata/script/debugdir.txtar b/testdata/script/debugdir.txtar index 0c59811..fba5c15 100644 --- a/testdata/script/debugdir.txtar +++ b/testdata/script/debugdir.txtar @@ -1,4 +1,4 @@ -exec garble -debugdir ./debug1 build +exec garble -debugdir=debug1 build exists 'debug1/test/main/imported/imported.go' 'debug1/test/main/main.go' 'debug1/reflect/type.go' exists 'debug1/runtime/error.go' 'debug1/runtime/funcdata.h' 'debug1/runtime/asm.s' [amd64] exists 'debug1/runtime/cpuflags_amd64.go' 'debug1/runtime/asm_amd64.s' @@ -7,14 +7,26 @@ exists 'debug1/runtime/error.go' 'debug1/runtime/funcdata.h' 'debug1/runtime/asm ! grep ImportedFunc $WORK/debug1/test/main/main.go ! grep 'some comment' $WORK/debug1/test/main/main.go +# We should refuse to delete non-empty directories which weren't created +# by an earlier invocation of garble -debugdir, as that could lead to data loss. +! exec garble -debugdir=notdebug build +stderr 'debugdir "notdebug" has unknown contents; empty it first' +exists notdebug/important_data.txt +exists notdebug/subdir/important_data.txt + [short] stop # Sources from previous builds should be deleted cp $WORK/debug1/test/main/main.go $WORK/debug1/some_file_from_prev_build.go -exec garble -debugdir ./debug1 build -v +exec garble -debugdir=debug1 build -v stderr 'test/main' # we force rebuilds with -debugdir ! exists $WORK/debug1/some_file_from_prev_build.go + +-- notdebug/important_data.txt -- +This file should not be deleted by -debugdir. +-- notdebug/subdir/important_data.txt -- +This file should not be deleted by -debugdir. -- go.mod -- module test/main