diff options
| author | Mark Johnston <markjdb@gmail.com> | 2022-06-13 13:55:38 -0400 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2022-06-17 07:59:41 +0200 |
| commit | cb58b3b231a677b1a6c89cd2af59e4fab10f9144 (patch) | |
| tree | 6c46ec03c5c6f148b569e9083e8c704796c8be6b | |
| parent | 1719ee24e741afb177677e9644f1c74aef1060fb (diff) | |
executor: try harder to unlink files on FreeBSD
There is a BSD syscall, chflags(2), which lets one set various flags on
a file, including several that prevent unlinking. The use of this flag
can cause the executor to fail to clean up tmpdirs, which can lead to
spurious reports.
Thus, when unlinking fails, try again after clearing relevant flags. I
suspect this would be useful on other BSDs but I can't easily verify
that this change works there. It may eventually be worth having a
BSD-specific remove_dir() implementation.
| -rw-r--r-- | executor/common.h | 33 | ||||
| -rw-r--r-- | pkg/csource/generated.go | 32 |
2 files changed, 61 insertions, 4 deletions
diff --git a/executor/common.h b/executor/common.h index b98dd3fc1..9cd92983f 100644 --- a/executor/common.h +++ b/executor/common.h @@ -244,6 +244,19 @@ static void use_temporary_dir(void) #include <sys/stat.h> #include <sys/types.h> +#if GOOS_freebsd +// Unset file flags which might inhibit unlinking. +static void reset_flags(const char* filename) +{ + struct stat st; + if (lstat(filename, &st)) + exitf("lstat(%s) failed", filename); + st.st_flags &= ~(SF_NOUNLINK | UF_NOUNLINK | SF_IMMUTABLE | UF_IMMUTABLE); + if (lchflags(filename, st.st_flags)) + exitf("lchflags(%s) failed", filename); +} +#endif + // We need to prevent the compiler from unrolling the while loop by using the gcc's noinline attribute // because otherwise it could trigger the compiler warning about a potential format truncation // when a filename is constructed with help of snprintf. This warning occurs because by unrolling @@ -280,12 +293,28 @@ static void __attribute__((noinline)) remove_dir(const char* dir) remove_dir(filename); continue; } - if (unlink(filename)) + if (unlink(filename)) { +#if GOOS_freebsd + if (errno == EPERM) { + reset_flags(filename); + if (unlink(filename) == 0) + continue; + } +#endif exitf("unlink(%s) failed", filename); + } } closedir(dp); - if (rmdir(dir)) + while (rmdir(dir)) { +#if GOOS_freebsd + if (errno == EPERM) { + reset_flags(dir); + if (rmdir(dir) == 0) + break; + } +#endif exitf("rmdir(%s) failed", dir); + } } #endif #endif diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go index 7e9c922b1..bfd60f23e 100644 --- a/pkg/csource/generated.go +++ b/pkg/csource/generated.go @@ -216,6 +216,18 @@ static void use_temporary_dir(void) #include <string.h> #include <sys/stat.h> #include <sys/types.h> + +#if GOOS_freebsd +static void reset_flags(const char* filename) +{ + struct stat st; + if (lstat(filename, &st)) + exitf("lstat(%s) failed", filename); + st.st_flags &= ~(SF_NOUNLINK | UF_NOUNLINK | SF_IMMUTABLE | UF_IMMUTABLE); + if (lchflags(filename, st.st_flags)) + exitf("lchflags(%s) failed", filename); +} +#endif static void __attribute__((noinline)) remove_dir(const char* dir) { DIR* dp = opendir(dir); @@ -240,12 +252,28 @@ static void __attribute__((noinline)) remove_dir(const char* dir) remove_dir(filename); continue; } - if (unlink(filename)) + if (unlink(filename)) { +#if GOOS_freebsd + if (errno == EPERM) { + reset_flags(filename); + if (unlink(filename) == 0) + continue; + } +#endif exitf("unlink(%s) failed", filename); + } } closedir(dp); - if (rmdir(dir)) + while (rmdir(dir)) { +#if GOOS_freebsd + if (errno == EPERM) { + reset_flags(dir); + if (rmdir(dir) == 0) + break; + } +#endif exitf("rmdir(%s) failed", dir); + } } #endif #endif |
