Handle symlinked directories correctly during gn clean on Windows.
Prior to this change, gn clean would recursively delete the contents of
symlinked directories on Windows, which could cause data loss when the
symlink pointed to important directories outside the out/<dir>.
This change adds a check for FILE_ATTRIBUTE_REPARSE_POINT to prevent
recursive deletion of symlinked directories. Now gn clean will only
remove the symlink itself while preserving the contents of the target
directory.
Change-Id: If0f4a631de081ad2aa4815db2927df8f80e9bf55
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/19980
Reviewed-by: Sylvain Defresne <sdefresne@chromium.org>
Reviewed-by: David Turner <digit@google.com>
Commit-Queue: Sylvain Defresne <sdefresne@chromium.org>
diff --git a/src/base/files/file_util.h b/src/base/files/file_util.h
index dbb69ec..e58bbd5 100644
--- a/src/base/files/file_util.h
+++ b/src/base/files/file_util.h
@@ -58,8 +58,8 @@
// Returns true if successful, false otherwise. It is considered successful
// to attempt to delete a file that does not exist.
//
-// In posix environment and if |path| is a symbolic link, this deletes only
-// the symlink. (even if the symlink points to a non-existent file)
+// If |path| is a symbolic link, this deletes only the symlink. (even if the
+// symlink points to a non-existent file)
//
// WARNING: USING THIS WITH recursive==true IS EQUIVALENT
// TO "rm -rf", SO USE WITH CAUTION.
diff --git a/src/base/files/file_util_win.cc b/src/base/files/file_util_win.cc
index 0bc07bf..d271799 100644
--- a/src/base/files/file_util_win.cc
+++ b/src/base/files/file_util_win.cc
@@ -74,7 +74,12 @@
DWORD this_result = ERROR_SUCCESS;
if (info.IsDirectory()) {
if (recursive) {
- this_result = DeleteFileRecursive(current, pattern, true);
+ // Do not recurse into reparse points (like symbolic links or junctions)
+ // to avoid deleting their targets.
+ if (!(info.find_data().dwFileAttributes &
+ FILE_ATTRIBUTE_REPARSE_POINT)) {
+ this_result = DeleteFileRecursive(current, pattern, true);
+ }
if (this_result == ERROR_SUCCESS &&
!::RemoveDirectory(ToWCharT(¤t.value()))) {
this_result = ::GetLastError();
@@ -135,7 +140,9 @@
: ::GetLastError();
}
- if (recursive) {
+ // Do not recurse into reparse points (like symbolic links or junctions) to
+ // avoid deleting their targets.
+ if (recursive && !(attr & FILE_ATTRIBUTE_REPARSE_POINT)) {
const DWORD error_code = DeleteFileRecursive(path, u"*", true);
if (error_code != ERROR_SUCCESS)
return error_code;