Implements the /MINAGE and /MAXAGE flags, which causes only files older or newer than the set values to be included in any copy operation
Signed-off-by: Florian Eder others.meder@gmail.com --- Again, both switches in one patch, as both are tightly connected --- programs/robocopy/main.c | 70 ++++++++++++++++++++++++++++++++++++ programs/robocopy/robocopy.h | 2 ++ 2 files changed, 72 insertions(+)
diff --git a/programs/robocopy/main.c b/programs/robocopy/main.c index 47ed5a12371..17d991e7435 100644 --- a/programs/robocopy/main.c +++ b/programs/robocopy/main.c @@ -129,6 +129,44 @@ WCHAR *get_absolute_path_with_trailing_backslash(WCHAR *path) return absolute_path; }
+static void parse_date_to_filetime(const WCHAR *date_string, FILETIME *filetime_pointer) +{ + /* + * robocopy interprets values between [0, 1899] as a relative offset (in days) from the current date, + * otherwise as an absolute date in the YYYYMMDD format (regardless of user locale) + */ + long value; + FILETIME time; + value = wcstol(date_string, NULL, 10); + if (value >= 0 && value < 1900) + { + /* relative offset of n days into the past */ + LARGE_INTEGER time_as_integer; + GetSystemTimeAsFileTime(&time); + /* FILETIME is no union with LONGLONG / LONG64, casting could be unsafe */ + time_as_integer.HighPart = time.dwHighDateTime; + time_as_integer.LowPart = time.dwLowDateTime; + /* 1000 * 1000 * 60 * 60 * 24 = 86400000000ns per day */ + time_as_integer.QuadPart -= 864000000000LL * value; + filetime_pointer->dwHighDateTime = time_as_integer.HighPart; + filetime_pointer->dwLowDateTime = time_as_integer.LowPart; + } + else if (value >= 19000101 && value <= 99991231) + { + /* absolute date in YYYYMMDD format */ + SYSTEMTIME time_as_systemtime; + memset(&time_as_systemtime, 0, sizeof(time_as_systemtime)); + if (swscanf(date_string, L"%4hu%2hu%2hu", &time_as_systemtime.wYear, &time_as_systemtime.wMonth, &time_as_systemtime.wDay) == 3 && + time_as_systemtime.wMonth > 0 && time_as_systemtime.wMonth <= 12 && + time_as_systemtime.wDay > 0 && time_as_systemtime.wDay <= 31 && + SystemTimeToFileTime(&time_as_systemtime, &time)) + { + filetime_pointer->dwHighDateTime = time.dwHighDateTime; + filetime_pointer->dwLowDateTime = time.dwLowDateTime; + } + } +} + static void parse_arguments(int argc, WCHAR *argv[]) { int i; @@ -231,6 +269,16 @@ static void parse_arguments(int argc, WCHAR *argv[]) if (swscanf(&(argv[i][5]), L"%lld", &value) == 1 && value >= 0) options.min_size = value; } + /* maxage - Include only newer files */ + else if (!wcsnicmp(argv[i], L"/maxage:", 8)) + { + parse_date_to_filetime(&(argv[i][8]), &options.max_time); + } + /* minage - Include only older files */ + else if (!wcsnicmp(argv[i], L"/minage:", 8)) + { + parse_date_to_filetime(&(argv[i][8]), &options.min_time); + } else { WINE_FIXME("encountered an unknown robocopy flag: %S\n", argv[i]); @@ -400,6 +448,7 @@ static BOOL is_valid_file(WCHAR *source) { HANDLE source_handle; LARGE_INTEGER source_size; + FILETIME source_creation_time, source_access_time, source_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); @@ -410,6 +459,14 @@ static BOOL is_valid_file(WCHAR *source) CloseHandle(source_handle); return FALSE; } + GetFileTime(source_handle, &source_creation_time, &source_access_time, &source_modified_time); + /* ignore file if source is not within max / min age (if set) */ + if ((options.min_time.dwHighDateTime != 0 && CompareFileTime(&source_creation_time, &options.min_time) > 0) || + (options.max_time.dwHighDateTime != 0 && CompareFileTime(&source_creation_time, &options.max_time) < 0)) + { + CloseHandle(source_handle); + return FALSE; + } CloseHandle(source_handle); return TRUE; } @@ -526,6 +583,7 @@ static BOOL perform_copy(struct robocopy_statistics *statistics)
static WCHAR *get_option_string(void) { + SYSTEMTIME time; WCHAR *string, temp_string[512]; memset(temp_string, 0, sizeof(temp_string));
@@ -578,6 +636,18 @@ static WCHAR *get_option_string(void) swprintf(temp_string + wcslen(temp_string), ARRAY_SIZE(temp_string) - wcslen(temp_string), L"/MIN:%lld ", options.min_size);
+ /* Min Age*/ + if (options.min_time.dwHighDateTime != 0) + if (FileTimeToSystemTime(&options.min_time, &time)) + swprintf(temp_string + wcslen(temp_string), ARRAY_SIZE(temp_string) - wcslen(temp_string), + L"/MINAGE:%hu-%02hu-%02hu ", time.wYear, time.wMonth, time.wDay); + + /* Max Age */ + if (options.max_time.dwHighDateTime != 0) + if (FileTimeToSystemTime(&options.max_time, &time)) + swprintf(temp_string + wcslen(temp_string), ARRAY_SIZE(temp_string) - wcslen(temp_string), + L"/MAXAGE:%hu-%02hu-%02hu ", time.wYear, time.wMonth, time.wDay); + string = wcsdup(temp_string); return string; } diff --git a/programs/robocopy/robocopy.h b/programs/robocopy/robocopy.h index 0d498a9649c..27c32d20262 100644 --- a/programs/robocopy/robocopy.h +++ b/programs/robocopy/robocopy.h @@ -48,6 +48,8 @@ struct robocopy_options { BOOL dry_run; LONGLONG min_size; LONGLONG max_size; + FILETIME min_time; + FILETIME max_time; };
struct robocopy_statistics {