diff --git a/winpr/libwinpr/file/generic.c b/winpr/libwinpr/file/generic.c index 1d59c0406..cf89b1362 100644 --- a/winpr/libwinpr/file/generic.c +++ b/winpr/libwinpr/file/generic.c @@ -539,15 +539,80 @@ DWORD WINAPI GetFileAttributesW(LPCWSTR lpFileName) return ret; } +static char* append(char* buffer, size_t size, const char* append) +{ + const size_t len = strnlen(buffer, size); + if (len == 0) + _snprintf(buffer, size, "%s", append); + else + { + strcat(buffer, "|"); + strcat(buffer, append); + } + + return buffer; +} + +static const char* flagsToStr(char* buffer, size_t size, DWORD flags) +{ + char strflags[32] = { 0 }; + if (flags & FILE_ATTRIBUTE_READONLY) + append(buffer, size, "FILE_ATTRIBUTE_READONLY"); + if (flags & FILE_ATTRIBUTE_HIDDEN) + append(buffer, size, "FILE_ATTRIBUTE_HIDDEN"); + if (flags & FILE_ATTRIBUTE_SYSTEM) + append(buffer, size, "FILE_ATTRIBUTE_SYSTEM"); + if (flags & FILE_ATTRIBUTE_DIRECTORY) + append(buffer, size, "FILE_ATTRIBUTE_DIRECTORY"); + if (flags & FILE_ATTRIBUTE_ARCHIVE) + append(buffer, size, "FILE_ATTRIBUTE_ARCHIVE"); + if (flags & FILE_ATTRIBUTE_DEVICE) + append(buffer, size, "FILE_ATTRIBUTE_DEVICE"); + if (flags & FILE_ATTRIBUTE_NORMAL) + append(buffer, size, "FILE_ATTRIBUTE_NORMAL"); + if (flags & FILE_ATTRIBUTE_TEMPORARY) + append(buffer, size, "FILE_ATTRIBUTE_TEMPORARY"); + if (flags & FILE_ATTRIBUTE_SPARSE_FILE) + append(buffer, size, "FILE_ATTRIBUTE_SPARSE_FILE"); + if (flags & FILE_ATTRIBUTE_REPARSE_POINT) + append(buffer, size, "FILE_ATTRIBUTE_REPARSE_POINT"); + if (flags & FILE_ATTRIBUTE_COMPRESSED) + append(buffer, size, "FILE_ATTRIBUTE_COMPRESSED"); + if (flags & FILE_ATTRIBUTE_OFFLINE) + append(buffer, size, "FILE_ATTRIBUTE_OFFLINE"); + if (flags & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) + append(buffer, size, "FILE_ATTRIBUTE_NOT_CONTENT_INDEXED"); + if (flags & FILE_ATTRIBUTE_ENCRYPTED) + append(buffer, size, "FILE_ATTRIBUTE_ENCRYPTED"); + if (flags & FILE_ATTRIBUTE_VIRTUAL) + append(buffer, size, "FILE_ATTRIBUTE_VIRTUAL"); + + _snprintf(strflags, sizeof(strflags), " [0x%08" PRIx32 "]", flags); + strcat(buffer, strflags); + return buffer; +} + BOOL SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes) { struct stat st; + int fd; + BOOL rc = FALSE; - if (stat(lpFileName, &st) != 0) + if (dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) { - return FALSE; + char buffer[8192] = { 0 }; + const char* flags = + flagsToStr(buffer, sizeof(buffer), dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); + WLog_WARN(TAG, "[%s] Unsupported flags %s, ignoring!", __FUNCTION__, flags); } + fd = open(lpFileName, O_RDONLY); + if (fd < 0) + return FALSE; + + if (fstat(fd, &st) != 0) + goto fail; + if (dwFileAttributes & FILE_ATTRIBUTE_READONLY) { st.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); @@ -557,12 +622,13 @@ BOOL SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes) st.st_mode |= S_IWUSR; } - if (chmod(lpFileName, st.st_mode) != 0) - { - return FALSE; - } + if (fchmod(fd, st.st_mode) != 0) + goto fail; - return TRUE; + rc = TRUE; +fail: + close(fd); + return rc; } BOOL SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes) @@ -570,6 +636,14 @@ BOOL SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes) BOOL ret; LPSTR lpCFileName; + if (dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) + { + char buffer[8192] = { 0 }; + const char* flags = + flagsToStr(buffer, sizeof(buffer), dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); + WLog_WARN(TAG, "[%s] Unsupported flags %s, ignoring!", __FUNCTION__, flags); + } + if (ConvertFromUnicode(CP_UTF8, 0, lpFileName, -1, &lpCFileName, 0, NULL, NULL) <= 0) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); diff --git a/winpr/libwinpr/file/test/CMakeLists.txt b/winpr/libwinpr/file/test/CMakeLists.txt index 6e5cb5428..399927110 100644 --- a/winpr/libwinpr/file/test/CMakeLists.txt +++ b/winpr/libwinpr/file/test/CMakeLists.txt @@ -9,6 +9,7 @@ if (NOT WIN32) TestFileCreateFile.c TestFileDeleteFile.c TestFileReadFile.c + TestSetFileAttributes.c TestFileWriteFile.c TestFilePatternMatch.c TestFileFindFirstFile.c diff --git a/winpr/libwinpr/file/test/TestSetFileAttributes.c b/winpr/libwinpr/file/test/TestSetFileAttributes.c new file mode 100644 index 000000000..e81c89c82 --- /dev/null +++ b/winpr/libwinpr/file/test/TestSetFileAttributes.c @@ -0,0 +1,149 @@ + +#include +#include +#include +#include +#include +#include +#include + +static const DWORD allflags[] = { + 0, + FILE_ATTRIBUTE_READONLY, + FILE_ATTRIBUTE_HIDDEN, + FILE_ATTRIBUTE_SYSTEM, + FILE_ATTRIBUTE_DIRECTORY, + FILE_ATTRIBUTE_ARCHIVE, + FILE_ATTRIBUTE_DEVICE, + FILE_ATTRIBUTE_NORMAL, + FILE_ATTRIBUTE_TEMPORARY, + FILE_ATTRIBUTE_SPARSE_FILE, + FILE_ATTRIBUTE_REPARSE_POINT, + FILE_ATTRIBUTE_COMPRESSED, + FILE_ATTRIBUTE_OFFLINE, + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, + FILE_ATTRIBUTE_ENCRYPTED, + FILE_ATTRIBUTE_VIRTUAL, + FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM, + FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DEVICE | + FILE_ATTRIBUTE_NORMAL, + FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_SPARSE_FILE | FILE_ATTRIBUTE_REPARSE_POINT | + FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_OFFLINE, + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_VIRTUAL +}; + +static BOOL test_SetFileAttributesA(void) +{ + BOOL rc = FALSE; + HANDLE handle; + DWORD x; + const DWORD flags[] = { 0, FILE_ATTRIBUTE_READONLY }; + char* name = GetKnownSubPath(KNOWN_PATH_TEMP, "afsklhjwe4oq5iu432oijrlkejadlkhjaklhfdkahfd"); + if (!name) + goto fail; + + for (x = 0; x < ARRAYSIZE(allflags); x++) + { + const DWORD flag = allflags[x]; + rc = SetFileAttributesA(NULL, flag); + if (rc) + goto fail; + + rc = SetFileAttributesA(name, flag); + if (rc) + goto fail; + } + + handle = CreateFileA(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, NULL); + if (handle == INVALID_HANDLE_VALUE) + goto fail; + CloseHandle(handle); + + for (x = 0; x < ARRAYSIZE(flags); x++) + { + DWORD attr; + const DWORD flag = flags[x]; + rc = SetFileAttributesA(name, flag); + if (!rc) + goto fail; + + attr = GetFileAttributesA(name); + if (flag != 0) + { + if ((attr & flag) == 0) + goto fail; + } + } + +fail: + DeleteFileA(name); + free(name); + return rc; +} + +static BOOL test_SetFileAttributesW(void) +{ + BOOL rc = FALSE; + WCHAR* name = NULL; + HANDLE handle; + DWORD x; + const DWORD flags[] = { 0, FILE_ATTRIBUTE_READONLY }; + char* base = GetKnownSubPath(KNOWN_PATH_TEMP, "afsklhjwe4oq5iu432oijrlkejadlkhjaklhfdkahfd"); + if (!base) + goto fail; + + ConvertToUnicode(CP_UTF8, 0, base, -1, &name, 0); + + for (x = 0; x < ARRAYSIZE(allflags); x++) + { + const DWORD flag = allflags[x]; + rc = SetFileAttributesW(NULL, flag); + if (rc) + goto fail; + + rc = SetFileAttributesW(name, flag); + if (rc) + goto fail; + } + + handle = CreateFileW(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, NULL); + if (handle == INVALID_HANDLE_VALUE) + goto fail; + CloseHandle(handle); + + for (x = 0; x < ARRAYSIZE(flags); x++) + { + DWORD attr; + const DWORD flag = flags[x]; + rc = SetFileAttributesW(name, flag); + if (!rc) + goto fail; + + attr = GetFileAttributesW(name); + if (flag != 0) + { + if ((attr & flag) == 0) + goto fail; + } + } + +fail: + DeleteFileW(name); + free(name); + free(base); + return rc; +} + +int TestSetFileAttributes(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!test_SetFileAttributesA()) + return -1; + if (!test_SetFileAttributesW()) + return -1; + return 0; +}