From e4aa186b1a2b0fea032159c3a6a240c587b39f56 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Tue, 9 Jun 2026 12:41:47 +0200 Subject: [PATCH] Fix touching of files that in Windows have the read-only attribute set --- HISTORY | 6 ++++++ cmdline/mingw.c | 25 +++++++++++++++++++++++++ cmdline/mingw.h | 10 ++++++++++ cmdline/touch.c | 16 ++++++++++++++++ 4 files changed, 57 insertions(+) diff --git a/HISTORY b/HISTORY index 1cd056fa..00e04daf 100644 --- a/HISTORY +++ b/HISTORY @@ -1,6 +1,12 @@ SnapRAID HISTORY ================ +14.7 2026/06 +============ + * Fix the 'touch' command on Windows for files with the read-only + attribute. The read-only attribute is temporarily removed and + then restored. No need to update if you are not on Windows. + 14.6 2026/06 ============ * Fix stack overflow in deep directory trees on platforms with a small diff --git a/cmdline/mingw.c b/cmdline/mingw.c index 1293b5c9..f9b03b22 100644 --- a/cmdline/mingw.c +++ b/cmdline/mingw.c @@ -766,6 +766,31 @@ int windows_fstat(int fd, struct windows_stat* st) return windows_info2stat(&info, &tag, st); } +int windows_get_file_attributes(const char* file) +{ + wchar_t conv_buf[CONV_MAX]; + + DWORD ret = GetFileAttributesW(convert(conv_buf, file)); + if (ret == INVALID_FILE_ATTRIBUTES) { + windows_errno(GetLastError()); + return -1; + } + + return ret; +} + +int windows_set_file_attributes(const char* file, int attributes) +{ + wchar_t conv_buf[CONV_MAX]; + + if (!SetFileAttributesW(convert(conv_buf, file), attributes)) { + windows_errno(GetLastError()); + return -1; + } + + return 0; +} + int windows_lstat(const char* file, struct windows_stat* st) { wchar_t conv_buf[CONV_MAX]; diff --git a/cmdline/mingw.h b/cmdline/mingw.h index 1ea1f4a4..3b0f1f9a 100644 --- a/cmdline/mingw.h +++ b/cmdline/mingw.h @@ -381,6 +381,16 @@ ssize_t windows_pwrite(int f, const void* buffer, size_t size, off_t offset); */ size_t windows_direct_size(void); +/** + * Like GetFileAttributes() + */ +int windows_get_file_attributes(const char* file); + +/** + * Like SetFileAttributes() + */ +int windows_set_file_attributes(const char* file, int attributes); + /****************************************************************************/ /* thread */ diff --git a/cmdline/touch.c b/cmdline/touch.c index f709fca4..2acc54a6 100644 --- a/cmdline/touch.c +++ b/cmdline/touch.c @@ -63,6 +63,22 @@ void state_touch(struct snapraid_state* state) /* open it */ f = open(path, flags); +#ifdef _WIN32 + if (f == -1) { + /* LCOV_EXCL_START */ + if (errno == EACCES) { + int attributes = windows_get_file_attributes(path); + if (attributes != -1 && (attributes & FILE_ATTRIBUTE_READONLY) != 0) { + /* remove read only attribute */ + if (windows_set_file_attributes(path, attributes & ~FILE_ATTRIBUTE_READONLY) == 0) { + f = open(path, flags); + windows_set_file_attributes(path, attributes); + } + } + } + /* LCOV_EXCL_STOP */ + } +#endif if (f == -1) { /* LCOV_EXCL_START */ log_error(errno, "Error opening file '%s'. %s.\n", path, strerror(errno));