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