aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/native
diff options
context:
space:
mode:
authorGravatar laszlocsomor <laszlocsomor@google.com>2018-07-06 02:16:34 -0700
committerGravatar Copybara-Service <copybara-piper@google.com>2018-07-06 02:17:54 -0700
commitb57ba275c0d67482f8bedc2888c34215721ee50e (patch)
tree30e6132db8dcb3603730f27cbab13c8e319b38dc /src/main/native
parentbc52a18a16d378193fd18a76fb58cc5e50414e37 (diff)
Windows,JNI: implement native DeletePath method
Implement a native DeletePath method that can delete files, directories, and junctions. The method should tolerate when concurrent processes delete the file. The new JNI function is more robust than Java IO file deletion function because it can also delete readonly files. See https://github.com/bazelbuild/bazel/issues/5513 Closes #5520. Change-Id: I21ea36dd64960b294e2b51600273bf4290ad7c0f PiperOrigin-RevId: 203448581
Diffstat (limited to 'src/main/native')
-rw-r--r--src/main/native/windows/file-jni.cc16
-rw-r--r--src/main/native/windows/file.cc103
-rw-r--r--src/main/native/windows/file.h17
3 files changed, 136 insertions, 0 deletions
diff --git a/src/main/native/windows/file-jni.cc b/src/main/native/windows/file-jni.cc
index b2c809efeb..e7c775daee 100644
--- a/src/main/native/windows/file-jni.cc
+++ b/src/main/native/windows/file-jni.cc
@@ -90,3 +90,19 @@ Java_com_google_devtools_build_lib_windows_jni_WindowsFileOperations_nativeCreat
}
return JNI_TRUE;
}
+
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_devtools_build_lib_windows_jni_WindowsFileOperations_nativeDeletePath(
+ JNIEnv* env, jclass clazz, jstring path, jobjectArray error_msg_holder) {
+ std::wstring wpath(bazel::windows::GetJavaWstring(env, path));
+ std::wstring error;
+ int result = bazel::windows::DeletePath(wpath, &error);
+ if (result != bazel::windows::DELETE_PATH_SUCCESS && !error.empty() &&
+ CanReportError(env, error_msg_holder)) {
+ ReportLastError(
+ bazel::windows::MakeErrorMessage(WSTR(__FILE__), __LINE__,
+ L"nativeDeletePath", wpath, error),
+ env, error_msg_holder);
+ }
+ return result;
+}
diff --git a/src/main/native/windows/file.cc b/src/main/native/windows/file.cc
index e9d7c18f5b..d8ae54710a 100644
--- a/src/main/native/windows/file.cc
+++ b/src/main/native/windows/file.cc
@@ -188,5 +188,108 @@ wstring CreateJunction(const wstring& junction_name,
return L"";
}
+int DeletePath(const wstring& path, wstring* error) {
+ const wchar_t* wpath = path.c_str();
+ if (!DeleteFileW(wpath)) {
+ DWORD err = GetLastError();
+ if (err == ERROR_SHARING_VIOLATION) {
+ // The file or directory is in use by some process.
+ return DELETE_PATH_ACCESS_DENIED;
+ } else if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
+ // The file or directory does not exist, or a parent directory does not
+ // exist, or a parent directory is actually a file.
+ return DELETE_PATH_DOES_NOT_EXIST;
+ } else if (err != ERROR_ACCESS_DENIED) {
+ // Some unknown error occurred.
+ if (error) {
+ *error = MakeErrorMessage(WSTR(__FILE__), __LINE__, L"DeleteFileW",
+ path, err);
+ }
+ return DELETE_PATH_ERROR;
+ }
+
+ // DeleteFileW failed with access denied, because the file is read-only or
+ // it is a directory.
+ DWORD attr = GetFileAttributesW(wpath);
+ if (attr == INVALID_FILE_ATTRIBUTES) {
+ err = GetLastError();
+ if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
+ // The file disappeared, or one of its parent directories disappeared,
+ // or one of its parent directories is no longer a directory.
+ return DELETE_PATH_DOES_NOT_EXIST;
+ }
+
+ // Some unknown error occurred.
+ if (error) {
+ *error = MakeErrorMessage(WSTR(__FILE__), __LINE__,
+ L"GetFileAttributesW", path, err);
+ }
+ return DELETE_PATH_ERROR;
+ }
+
+ if (attr & FILE_ATTRIBUTE_DIRECTORY) {
+ // It's a directory or a junction.
+ if (!RemoveDirectoryW(wpath)) {
+ // Failed to delete the directory.
+ err = GetLastError();
+ if (err == ERROR_SHARING_VIOLATION) {
+ // The junction or directory is in use by another process.
+ return DELETE_PATH_ACCESS_DENIED;
+ } else if (err == ERROR_DIR_NOT_EMPTY) {
+ // The directory is not empty.
+ return DELETE_PATH_DIRECTORY_NOT_EMPTY;
+ } else if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
+ // The directory or one of its directories disappeared or is no longer
+ // a directory.
+ return DELETE_PATH_DOES_NOT_EXIST;
+ }
+
+ // Some unknown error occurred.
+ if (error) {
+ *error = MakeErrorMessage(WSTR(__FILE__), __LINE__,
+ L"DeleteDirectoryW", path, err);
+ }
+ return DELETE_PATH_ERROR;
+ }
+ } else {
+ // It's a file and it's probably read-only.
+ // Make it writable then try deleting it again.
+ attr &= ~FILE_ATTRIBUTE_READONLY;
+ if (!SetFileAttributesW(wpath, attr)) {
+ err = GetLastError();
+ if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
+ // The file disappeared, or one of its parent directories disappeared,
+ // or one of its parent directories is no longer a directory.
+ return DELETE_PATH_DOES_NOT_EXIST;
+ }
+ // Some unknown error occurred.
+ if (error) {
+ *error = MakeErrorMessage(WSTR(__FILE__), __LINE__,
+ L"SetFileAttributesW", path, err);
+ }
+ return DELETE_PATH_ERROR;
+ }
+
+ if (!DeleteFileW(wpath)) {
+ // Failed to delete the file again.
+ err = GetLastError();
+ if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
+ // The file disappeared, or one of its parent directories disappeared,
+ // or one of its parent directories is no longer a directory.
+ return DELETE_PATH_DOES_NOT_EXIST;
+ }
+
+ // Some unknown error occurred.
+ if (error) {
+ *error = MakeErrorMessage(WSTR(__FILE__), __LINE__, L"DeleteFileW",
+ path, err);
+ }
+ return DELETE_PATH_ERROR;
+ }
+ }
+ }
+ return DELETE_PATH_SUCCESS;
+}
+
} // namespace windows
} // namespace bazel
diff --git a/src/main/native/windows/file.h b/src/main/native/windows/file.h
index 5a915fe9a4..f19ac2d92f 100644
--- a/src/main/native/windows/file.h
+++ b/src/main/native/windows/file.h
@@ -41,6 +41,15 @@ enum {
IS_JUNCTION_ERROR = 2,
};
+// Keep in sync with j.c.g.devtools.build.lib.windows.WindowsFileOperations
+enum {
+ DELETE_PATH_SUCCESS = 0,
+ DELETE_PATH_DOES_NOT_EXIST = 1,
+ DELETE_PATH_DIRECTORY_NOT_EMPTY = 2,
+ DELETE_PATH_ACCESS_DENIED = 3,
+ DELETE_PATH_ERROR = 4,
+};
+
// Determines whether `path` is a junction (or directory symlink).
//
// `path` should be an absolute, normalized, Windows-style path, with "\\?\"
@@ -85,6 +94,14 @@ HANDLE OpenDirectory(const WCHAR* path, bool read_write);
wstring CreateJunction(const wstring& junction_name,
const wstring& junction_target);
+// Deletes the file, junction, or empty directory at `path`.
+// Returns DELETE_PATH_SUCCESS if it successfully deleted the path, otherwise
+// returns one of the other DELETE_PATH_* constants (e.g. when the directory is
+// not empty or the file is in use by another process).
+// Returns DELETE_PATH_ERROR for unexpected errors. If `error` is not null, the
+// function writes an error message into it.
+int DeletePath(const wstring& path, wstring* error);
+
} // namespace windows
} // namespace bazel