[PATCH 32/41] robocopy: add flag to prevent overwriting of newer / older files (/XN, /XO)

Florian Eder others.meder at gmail.com
Mon Sep 6 09:55:09 CDT 2021


Implements the /XN and /XO flags, which prevent older / newer files in the
destination folder from being overwritten

Signed-off-by: Florian Eder <others.meder at gmail.com>
---
 programs/robocopy/main.c     | 46 ++++++++++++++++++++++++++++++++----
 programs/robocopy/robocopy.h |  2 ++
 2 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/programs/robocopy/main.c b/programs/robocopy/main.c
index 17d991e7435..2fc0c39503d 100644
--- a/programs/robocopy/main.c
+++ b/programs/robocopy/main.c
@@ -232,6 +232,16 @@ static void parse_arguments(int argc, WCHAR *argv[])
                 options.purge_source = TRUE;
                 options.purge_source_files = TRUE;
             }
+            /* xn - Don't overwrite files that are newer (in the destination) */
+            else if (!wcsicmp(argv[i], L"/xn"))
+            {
+                options.dont_overwrite_newer_files = TRUE;
+            }
+            /* xo - Don't overwrite files that are older (in the destination) */
+            else if (!wcsicmp(argv[i], L"/xo"))
+            {
+                options.dont_overwrite_older_files = TRUE;
+            }
             /* xf - Excluded Files */
             else if (!wcsicmp(argv[i], L"/xf"))
             {
@@ -444,11 +454,12 @@ static void get_file_paths_in_folder(WCHAR *directory_path, struct list *paths,
     }
 }
 
-static BOOL is_valid_file(WCHAR *source)
+static BOOL is_valid_file(WCHAR *source, WCHAR *destination)
 {
-    HANDLE source_handle;
+    HANDLE source_handle, destination_handle;
     LARGE_INTEGER source_size;
-    FILETIME source_creation_time, source_access_time, source_modified_time;
+    FILETIME source_creation_time, source_access_time, source_modified_time,
+             destination_creation_time, destination_access_time, destination_modified_time;
     source_handle = CreateFileW(source, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
     if (source_handle == INVALID_HANDLE_VALUE) return FALSE;
     GetFileSizeEx(source_handle, &source_size);
@@ -467,7 +478,24 @@ static BOOL is_valid_file(WCHAR *source)
         CloseHandle(source_handle);
         return FALSE;
     }
+
+    destination_handle = CreateFileW(destination, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+    if (destination_handle == INVALID_HANDLE_VALUE)
+    {
+        CloseHandle(source_handle);
+        return TRUE;
+    }
+    GetFileTime(destination_handle, &destination_creation_time, &destination_access_time, &destination_modified_time);
+    /* don't overwrite newer or older files if set to do so */
+    if ((options.dont_overwrite_newer_files && CompareFileTime(&source_creation_time, &destination_creation_time) > 0) ||
+        (options.dont_overwrite_older_files && CompareFileTime(&source_creation_time, &destination_creation_time) < 0))
+    {
+        CloseHandle(source_handle);
+        CloseHandle(destination_handle);
+        return FALSE;
+    }
     CloseHandle(source_handle);
+    CloseHandle(destination_handle);
     return TRUE;
 }
 
@@ -515,8 +543,8 @@ static BOOL perform_copy(struct robocopy_statistics *statistics)
         }
         else
         {
-            /* ignore file if the file size is not within the allowed limits */
-            if (!is_valid_file(current_absolute_path)) continue;
+            /* ignore file if the file size is not within the allowed limits (or would cause an illegal overwrite) */
+            if (!is_valid_file(current_absolute_path, target_path)) continue;
 
             if (options.dry_run || copy_or_move_file(current_absolute_path, target_path, options.purge_source_files))
             {
@@ -626,6 +654,14 @@ static WCHAR *get_option_string(void)
             wcscat(temp_string, L"/MOV ");
     }
 
+    /* Ignore newer files */
+    if (options.dont_overwrite_newer_files)
+        wcscat(temp_string, L"/XN ");
+
+    /* Ignore older files */
+    if (options.dont_overwrite_older_files)
+        wcscat(temp_string, L"/XO ");
+
     /* Max File Size */
     if (options.max_size != MAXLONGLONG)
         swprintf(temp_string + wcslen(temp_string), ARRAY_SIZE(temp_string) - wcslen(temp_string),
diff --git a/programs/robocopy/robocopy.h b/programs/robocopy/robocopy.h
index 27c32d20262..cf396f88dfc 100644
--- a/programs/robocopy/robocopy.h
+++ b/programs/robocopy/robocopy.h
@@ -50,6 +50,8 @@ struct robocopy_options {
     LONGLONG max_size;
     FILETIME min_time;
     FILETIME max_time;
+    BOOL dont_overwrite_newer_files;
+    BOOL dont_overwrite_older_files;
 };
 
 struct robocopy_statistics {
-- 
2.32.0




More information about the wine-devel mailing list