win, fs: uv_fs_fchmod support for -A files

Adds uv_fs_chmod support for files with the Archive attribute cleared

Ref: https://github.com/libuv/libuv/pull/1777
Ref: https://github.com/nodejs/node/issues/12803
PR-URL: https://github.com/libuv/libuv/pull/1819
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
Bartosz Sosnowski 2018-05-17 09:27:09 +02:00
parent 60bac5a98f
commit b59fc58326
3 changed files with 183 additions and 3 deletions

View File

@ -1490,6 +1490,7 @@ static void fs__chmod(uv_fs_t* req) {
static void fs__fchmod(uv_fs_t* req) {
int fd = req->file.fd;
int clear_archive_flag;
HANDLE handle;
NTSTATUS nt_status;
IO_STATUS_BLOCK io_status;
@ -1497,7 +1498,11 @@ static void fs__fchmod(uv_fs_t* req) {
VERIFY_FD(fd, req);
handle = uv__get_osfhandle(fd);
handle = ReOpenFile(uv__get_osfhandle(fd), FILE_WRITE_ATTRIBUTES, 0, 0);
if (handle == INVALID_HANDLE_VALUE) {
SET_REQ_WIN32_ERROR(req, GetLastError());
return;
}
nt_status = pNtQueryInformationFile(handle,
&io_status,
@ -1507,7 +1512,27 @@ static void fs__fchmod(uv_fs_t* req) {
if (!NT_SUCCESS(nt_status)) {
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
return;
goto fchmod_cleanup;
}
/* Test if the Archive attribute is cleared */
if ((file_info.FileAttributes & FILE_ATTRIBUTE_ARCHIVE) == 0) {
/* Set Archive flag, otherwise setting or clearing the read-olny
flag will not work */
file_info.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
nt_status = pNtSetInformationFile(handle,
&io_status,
&file_info,
sizeof file_info,
FileBasicInformation);
if (!NT_SUCCESS(nt_status)) {
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
goto fchmod_cleanup;
}
/* Remeber to clear the flag later on */
clear_archive_flag = 1;
} else {
clear_archive_flag = 0;
}
if (req->fs.info.mode & _S_IWRITE) {
@ -1524,10 +1549,28 @@ static void fs__fchmod(uv_fs_t* req) {
if (!NT_SUCCESS(nt_status)) {
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
return;
goto fchmod_cleanup;
}
if (clear_archive_flag) {
file_info.FileAttributes &= ~FILE_ATTRIBUTE_ARCHIVE;
if (file_info.FileAttributes == 0) {
file_info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
}
nt_status = pNtSetInformationFile(handle,
&io_status,
&file_info,
sizeof file_info,
FileBasicInformation);
if (!NT_SUCCESS(nt_status)) {
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
goto fchmod_cleanup;
}
}
SET_REQ_SUCCESS(req);
fchmod_cleanup:
CloseHandle(handle);
}

View File

@ -3230,3 +3230,136 @@ TEST_IMPL(fs_exclusive_sharing_mode) {
return 0;
}
#endif
#ifdef _WIN32
int call_icacls(const char* command, ...) {
char icacls_command[1024];
va_list args;
va_start(args, command);
vsnprintf(icacls_command, ARRAYSIZE(icacls_command), command, args);
va_end(args);
return system(icacls_command);
}
TEST_IMPL(fs_open_readonly_acl) {
uv_passwd_t pwd;
uv_fs_t req;
int r;
/*
Based on Node.js test from
https://github.com/nodejs/node/commit/3ba81e34e86a5c32658e218cb6e65b13e8326bc5
If anything goes wrong, you can delte the test_fle_icacls with:
icacls test_file_icacls /remove "%USERNAME%" /inheritance:e
attrib -r test_file_icacls
del test_file_icacls
*/
/* Setup - clear the ACL and remove the file */
loop = uv_default_loop();
r = uv_os_get_passwd(&pwd);
ASSERT(r == 0);
call_icacls("icacls test_file_icacls /remove \"%s\" /inheritance:e",
pwd.username);
uv_fs_chmod(loop, &req, "test_file_icacls", S_IWUSR, NULL);
unlink("test_file_icacls");
/* Create the file */
r = uv_fs_open(loop,
&open_req1,
"test_file_icacls",
O_RDONLY | O_CREAT,
S_IRUSR,
NULL);
ASSERT(r >= 0);
ASSERT(open_req1.result >= 0);
uv_fs_req_cleanup(&open_req1);
r = uv_fs_close(NULL, &close_req, open_req1.result, NULL);
ASSERT(r == 0);
ASSERT(close_req.result == 0);
uv_fs_req_cleanup(&close_req);
/* Set up ACL */
r = call_icacls("icacls test_file_icacls /inheritance:r /remove \"%s\"",
pwd.username);
if (r != 0) {
goto acl_cleanup;
}
r = call_icacls("icacls test_file_icacls /grant \"%s\":RX", pwd.username);
if (r != 0) {
goto acl_cleanup;
}
/* Try opening the file */
r = uv_fs_open(NULL, &open_req1, "test_file_icacls", O_RDONLY, 0, NULL);
if (r < 0) {
goto acl_cleanup;
}
uv_fs_req_cleanup(&open_req1);
r = uv_fs_close(NULL, &close_req, open_req1.result, NULL);
if (r != 0) {
goto acl_cleanup;
}
uv_fs_req_cleanup(&close_req);
acl_cleanup:
/* Cleanup */
call_icacls("icacls test_file_icacls /remove \"%s\" /inheritance:e",
pwd.username);
unlink("test_file_icacls");
uv_os_free_passwd(&pwd);
ASSERT(r == 0);
MAKE_VALGRIND_HAPPY();
return 0;
}
#endif
#ifdef _WIN32
TEST_IMPL(fs_fchmod_archive_readolny) {
uv_fs_t req;
uv_file file;
int r;
/* Test clearing read-only flag from files with Archive flag cleared */
/* Setup*/
unlink("test_file");
r = uv_fs_open(NULL,
&req,
"test_file",
O_WRONLY | O_CREAT,
S_IWUSR | S_IRUSR,
NULL);
ASSERT(r >= 0);
ASSERT(req.result >= 0);
file = req.result;
uv_fs_req_cleanup(&req);
r = uv_fs_close(NULL, &req, file, NULL);
ASSERT(r == 0);
uv_fs_req_cleanup(&req);
/* Make the file read-only and clear archive flag */
r = SetFileAttributes("test_file", FILE_ATTRIBUTE_READONLY);
ASSERT(r != 0);
check_permission("test_file", 0400);
/* Try fchmod */
r = uv_fs_open(NULL, &req, "test_file", O_RDONLY, 0, NULL);
ASSERT(r >= 0);
ASSERT(req.result >= 0);
file = req.result;
uv_fs_req_cleanup(&req);
r = uv_fs_fchmod(NULL, &req, file, S_IWUSR, NULL);
ASSERT(r == 0);
ASSERT(req.result == 0);
uv_fs_req_cleanup(&req);
r = uv_fs_close(NULL, &req, file, NULL);
ASSERT(r == 0);
uv_fs_req_cleanup(&req);
check_permission("test_file", S_IWUSR);
/* Restore Archive flag for rest of the tests */
r = SetFileAttributes("test_file", FILE_ATTRIBUTE_ARCHIVE);
ASSERT(r != 0);
}
#endif

View File

@ -337,6 +337,8 @@ TEST_DECLARE (fs_file_pos_after_op_with_offset)
TEST_DECLARE (fs_null_req)
#ifdef _WIN32
TEST_DECLARE (fs_exclusive_sharing_mode)
TEST_DECLARE (fs_open_readonly_acl)
TEST_DECLARE (fs_fchmod_archive_readolny)
#endif
TEST_DECLARE (threadpool_queue_work_simple)
TEST_DECLARE (threadpool_queue_work_einval)
@ -869,6 +871,8 @@ TASK_LIST_START
TEST_ENTRY (fs_null_req)
#ifdef _WIN32
TEST_ENTRY (fs_exclusive_sharing_mode)
TEST_ENTRY (fs_open_readonly_acl)
TEST_ENTRY (fs_fchmod_archive_readolny)
#endif
TEST_ENTRY (get_osfhandle_valid_handle)
TEST_ENTRY (threadpool_queue_work_simple)