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 /executor/common.h | |
| 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.
Diffstat (limited to 'executor/common.h')
| -rw-r--r-- | executor/common.h | 33 |
1 files changed, 31 insertions, 2 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 |
