aboutsummaryrefslogtreecommitdiffstats
path: root/executor/common.h
diff options
context:
space:
mode:
authorMark Johnston <markjdb@gmail.com>2022-06-13 13:55:38 -0400
committerDmitry Vyukov <dvyukov@google.com>2022-06-17 07:59:41 +0200
commitcb58b3b231a677b1a6c89cd2af59e4fab10f9144 (patch)
tree6c46ec03c5c6f148b569e9083e8c704796c8be6b /executor/common.h
parent1719ee24e741afb177677e9644f1c74aef1060fb (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.h33
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