windows: implement missing fs functions
This commit is contained in:
parent
30ca028196
commit
cf5ed86a79
149
src/win/fs.c
149
src/win/fs.c
@ -323,6 +323,50 @@ void fs__chmod(uv_fs_t* req, const char* path, int mode) {
|
||||
}
|
||||
|
||||
|
||||
void fs__fchmod(uv_fs_t* req, uv_file file, int mode) {
|
||||
int result;
|
||||
HANDLE handle;
|
||||
NTSTATUS nt_status;
|
||||
IO_STATUS_BLOCK io_status;
|
||||
FILE_BASIC_INFORMATION file_info;
|
||||
|
||||
handle = (HANDLE)_get_osfhandle(file);
|
||||
|
||||
nt_status = pNtQueryInformationFile(handle,
|
||||
&io_status,
|
||||
&file_info,
|
||||
sizeof file_info,
|
||||
FileBasicInformation);
|
||||
|
||||
if (nt_status != STATUS_SUCCESS) {
|
||||
result = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (mode & _S_IWRITE) {
|
||||
file_info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
|
||||
} else {
|
||||
file_info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
|
||||
}
|
||||
|
||||
nt_status = pNtSetInformationFile(handle,
|
||||
&io_status,
|
||||
&file_info,
|
||||
sizeof file_info,
|
||||
FileBasicInformation);
|
||||
|
||||
if (nt_status != STATUS_SUCCESS) {
|
||||
result = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
done:
|
||||
SET_REQ_RESULT(req, result);
|
||||
}
|
||||
|
||||
|
||||
void fs__utime(uv_fs_t* req, const char* path, double atime, double mtime) {
|
||||
int result;
|
||||
struct _utimbuf b = {(time_t)atime, (time_t)mtime};
|
||||
@ -339,6 +383,11 @@ void fs__futime(uv_fs_t* req, uv_file file, double atime, double mtime) {
|
||||
}
|
||||
|
||||
|
||||
void fs__nop(uv_fs_t* req) {
|
||||
req->result = 0;
|
||||
}
|
||||
|
||||
|
||||
static DWORD WINAPI uv_fs_thread_proc(void* parameter) {
|
||||
uv_fs_t* req = (uv_fs_t*) parameter;
|
||||
uv_loop_t* loop = req->loop;
|
||||
@ -380,6 +429,7 @@ static DWORD WINAPI uv_fs_thread_proc(void* parameter) {
|
||||
fs__readdir(req, (const char*)req->arg0, (int)req->arg1);
|
||||
break;
|
||||
case UV_FS_STAT:
|
||||
case UV_FS_LSTAT:
|
||||
fs__stat(req, (const char*)req->arg0);
|
||||
break;
|
||||
case UV_FS_FSTAT:
|
||||
@ -405,12 +455,19 @@ static DWORD WINAPI uv_fs_thread_proc(void* parameter) {
|
||||
case UV_FS_CHMOD:
|
||||
fs__chmod(req, (const char*)req->arg0, (int)req->arg1);
|
||||
break;
|
||||
case UV_FS_FCHMOD:
|
||||
fs__fchmod(req, (uv_file)req->arg0, (int)req->arg1);
|
||||
break;
|
||||
case UV_FS_UTIME:
|
||||
fs__utime(req, (const char*)req->arg0, req->arg4, req->arg5);
|
||||
break;
|
||||
case UV_FS_FUTIME:
|
||||
fs__futime(req, (uv_file)req->arg0, req->arg4, req->arg5);
|
||||
break;
|
||||
case UV_FS_CHOWN:
|
||||
case UV_FS_FCHOWN:
|
||||
fs__nop(req);
|
||||
break;
|
||||
default:
|
||||
assert(!"bad uv_fs_type");
|
||||
}
|
||||
@ -544,13 +601,6 @@ int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
|
||||
assert(0 && "implement me");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// uv_fs_readlink, uv_fs_fchmod, uv_fs_chown, uv_fs_fchown
|
||||
int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path,
|
||||
const char* new_path, uv_fs_cb cb) {
|
||||
assert(0 && "implement me");
|
||||
@ -572,24 +622,34 @@ int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode,
|
||||
uv_fs_cb cb) {
|
||||
assert(0 && "implement me");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, int uid,
|
||||
int gid, uv_fs_cb cb) {
|
||||
assert(0 && "implement me");
|
||||
return -1;
|
||||
if (cb) {
|
||||
uv_fs_req_init_async(loop, req, UV_FS_CHOWN, cb);
|
||||
WRAP_REQ_ARGS3(req, path, uid, gid);
|
||||
STRDUP_ARG(req, 0);
|
||||
QUEUE_FS_TP_JOB(loop, req);
|
||||
} else {
|
||||
uv_fs_req_init_sync(loop, req, UV_FS_CHOWN);
|
||||
fs__nop(req);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, int uid,
|
||||
int gid, uv_fs_cb cb) {
|
||||
assert(0 && "implement me");
|
||||
return -1;
|
||||
if (cb) {
|
||||
uv_fs_req_init_async(loop, req, UV_FS_FCHOWN, cb);
|
||||
WRAP_REQ_ARGS3(req, file, uid, gid);
|
||||
QUEUE_FS_TP_JOB(loop, req);
|
||||
} else {
|
||||
uv_fs_req_init_sync(loop, req, UV_FS_FCHOWN);
|
||||
fs__nop(req);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -630,6 +690,44 @@ int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
|
||||
}
|
||||
|
||||
|
||||
/* TODO: add support for links. */
|
||||
int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
|
||||
int len = strlen(path);
|
||||
char* path2 = NULL;
|
||||
int has_backslash = (path[len - 1] == '\\' || path[len - 1] == '/');
|
||||
|
||||
if (path[len - 1] == '\\' || path[len - 1] == '/') {
|
||||
path2 = strdup(path);
|
||||
if (!path2) {
|
||||
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
|
||||
}
|
||||
|
||||
path2[len - 1] = '\0';
|
||||
}
|
||||
|
||||
if (cb) {
|
||||
uv_fs_req_init_async(loop, req, UV_FS_LSTAT, cb);
|
||||
if (path2) {
|
||||
WRAP_REQ_ARGS1(req, path2);
|
||||
req->flags |= UV_FS_FREE_ARG0;
|
||||
} else {
|
||||
WRAP_REQ_ARGS1(req, path);
|
||||
STRDUP_ARG(req, 0);
|
||||
}
|
||||
|
||||
QUEUE_FS_TP_JOB(loop, req);
|
||||
} else {
|
||||
uv_fs_req_init_sync(loop, req, UV_FS_LSTAT);
|
||||
fs__stat(req, path2 ? path2 : path);
|
||||
if (path2) {
|
||||
free(path2);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
|
||||
if (cb) {
|
||||
uv_fs_req_init_async(loop, req, UV_FS_FSTAT, cb);
|
||||
@ -735,6 +833,21 @@ int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode,
|
||||
uv_fs_cb cb) {
|
||||
if (cb) {
|
||||
uv_fs_req_init_async(loop, req, UV_FS_FCHMOD, cb);
|
||||
WRAP_REQ_ARGS2(req, file, mode);
|
||||
QUEUE_FS_TP_JOB(loop, req);
|
||||
} else {
|
||||
uv_fs_req_init_sync(loop, req, UV_FS_FCHMOD);
|
||||
fs__fchmod(req, file, mode);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime,
|
||||
double mtime, uv_fs_cb cb) {
|
||||
if (cb) {
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
|
||||
sRtlNtStatusToDosError pRtlNtStatusToDosError;
|
||||
sNtQueryInformationFile pNtQueryInformationFile;
|
||||
sNtSetInformationFile pNtSetInformationFile;
|
||||
sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
|
||||
sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes;
|
||||
|
||||
@ -55,6 +56,13 @@ void uv_winapi_init() {
|
||||
uv_fatal_error(GetLastError(), "GetProcAddress");
|
||||
}
|
||||
|
||||
pNtSetInformationFile = (sNtSetInformationFile) GetProcAddress(
|
||||
ntdll_module,
|
||||
"NtSetInformationFile");
|
||||
if (pNtSetInformationFile == NULL) {
|
||||
uv_fatal_error(GetLastError(), "GetProcAddress");
|
||||
}
|
||||
|
||||
kernel32_module = GetModuleHandleA("kernel32.dll");
|
||||
if (kernel32_module == NULL) {
|
||||
uv_fatal_error(GetLastError(), "GetModuleHandleA");
|
||||
|
||||
@ -4096,6 +4096,14 @@ typedef struct _FILE_PIPE_LOCAL_INFORMATION {
|
||||
ULONG NamedPipeEnd;
|
||||
} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
|
||||
|
||||
typedef struct _FILE_BASIC_INFORMATION {
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
DWORD FileAttributes;
|
||||
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
|
||||
|
||||
typedef enum _FILE_INFORMATION_CLASS {
|
||||
FileDirectoryInformation = 1,
|
||||
FileFullDirectoryInformation,
|
||||
@ -4165,6 +4173,12 @@ typedef NTSTATUS (NTAPI *sNtQueryInformationFile)
|
||||
ULONG Length,
|
||||
FILE_INFORMATION_CLASS FileInformationClass);
|
||||
|
||||
typedef NTSTATUS (NTAPI *sNtSetInformationFile)
|
||||
(HANDLE FileHandle,
|
||||
PIO_STATUS_BLOCK IoStatusBlock,
|
||||
PVOID FileInformation,
|
||||
ULONG Length,
|
||||
FILE_INFORMATION_CLASS FileInformationClass);
|
||||
|
||||
/*
|
||||
* Kernel32 headers
|
||||
@ -4197,6 +4211,7 @@ typedef BOOL (WINAPI* sSetFileCompletionNotificationModes)
|
||||
/* Ntapi function pointers */
|
||||
extern sRtlNtStatusToDosError pRtlNtStatusToDosError;
|
||||
extern sNtQueryInformationFile pNtQueryInformationFile;
|
||||
extern sNtSetInformationFile pNtSetInformationFile;
|
||||
|
||||
|
||||
/* Kernel32 function pointers */
|
||||
|
||||
122
test/test-fs.c
122
test/test-fs.c
@ -60,6 +60,8 @@ static int sendfile_cb_count;
|
||||
static int fstat_cb_count;
|
||||
static int chmod_cb_count;
|
||||
static int fchmod_cb_count;
|
||||
static int chown_cb_count;
|
||||
static int fchown_cb_count;
|
||||
|
||||
static uv_loop_t* loop;
|
||||
|
||||
@ -93,7 +95,15 @@ void check_permission(const char* filename, int mode) {
|
||||
ASSERT(req.result == 0);
|
||||
|
||||
s = req.ptr;
|
||||
#ifdef _WIN32
|
||||
/*
|
||||
* On Windows, chmod can only modify S_IWUSR (_S_IWRITE) bit,
|
||||
* so only testing for the specified flags.
|
||||
*/
|
||||
ASSERT((s->st_mode & 0777) & mode);
|
||||
#else
|
||||
ASSERT((s->st_mode & 0777) == mode);
|
||||
#endif
|
||||
|
||||
uv_fs_req_cleanup(&req);
|
||||
}
|
||||
@ -104,7 +114,7 @@ static void fchmod_cb(uv_fs_t* req) {
|
||||
ASSERT(req->result == 0);
|
||||
fchmod_cb_count++;
|
||||
uv_fs_req_cleanup(req);
|
||||
check_permission("test_file", 0600);
|
||||
check_permission("test_file", (int)req->data);
|
||||
}
|
||||
|
||||
|
||||
@ -113,7 +123,23 @@ static void chmod_cb(uv_fs_t* req) {
|
||||
ASSERT(req->result == 0);
|
||||
chmod_cb_count++;
|
||||
uv_fs_req_cleanup(req);
|
||||
check_permission("test_file", 0200);
|
||||
check_permission("test_file", (int)req->data);
|
||||
}
|
||||
|
||||
|
||||
static void fchown_cb(uv_fs_t* req) {
|
||||
ASSERT(req->fs_type == UV_FS_FCHOWN);
|
||||
ASSERT(req->result == 0);
|
||||
fchown_cb_count++;
|
||||
uv_fs_req_cleanup(req);
|
||||
}
|
||||
|
||||
|
||||
static void chown_cb(uv_fs_t* req) {
|
||||
ASSERT(req->fs_type == UV_FS_CHOWN);
|
||||
ASSERT(req->result == 0);
|
||||
chown_cb_count++;
|
||||
uv_fs_req_cleanup(req);
|
||||
}
|
||||
|
||||
|
||||
@ -598,6 +624,7 @@ TEST_IMPL(fs_fstat) {
|
||||
int r;
|
||||
uv_fs_t req;
|
||||
uv_file file;
|
||||
struct stat* s;
|
||||
|
||||
/* Setup. */
|
||||
unlink("test_file");
|
||||
@ -606,7 +633,8 @@ TEST_IMPL(fs_fstat) {
|
||||
|
||||
loop = uv_default_loop();
|
||||
|
||||
r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT, 0, NULL);
|
||||
r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT,
|
||||
S_IWRITE | S_IREAD, NULL);
|
||||
ASSERT(r == 0);
|
||||
ASSERT(req.result != -1);
|
||||
file = req.result;
|
||||
@ -620,7 +648,7 @@ TEST_IMPL(fs_fstat) {
|
||||
r = uv_fs_fstat(loop, &req, file, NULL);
|
||||
ASSERT(r == 0);
|
||||
ASSERT(req.result == 0);
|
||||
struct stat* s = req.ptr;
|
||||
s = req.ptr;
|
||||
ASSERT(s->st_size == sizeof(test_buf));
|
||||
uv_fs_req_cleanup(&req);
|
||||
|
||||
@ -637,7 +665,7 @@ TEST_IMPL(fs_fstat) {
|
||||
uv_fs_req_cleanup(&req);
|
||||
|
||||
/*
|
||||
* Run the loop just to check we don't have make any extranious uv_ref()
|
||||
* Run the loop just to check we don't have make any extraneous uv_ref()
|
||||
* calls. This should drop out immediately.
|
||||
*/
|
||||
uv_run(loop);
|
||||
@ -661,7 +689,8 @@ TEST_IMPL(fs_chmod) {
|
||||
|
||||
loop = uv_default_loop();
|
||||
|
||||
r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT, 0, NULL);
|
||||
r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT,
|
||||
S_IWRITE | S_IREAD, NULL);
|
||||
ASSERT(r == 0);
|
||||
ASSERT(req.result != -1);
|
||||
file = req.result;
|
||||
@ -672,6 +701,7 @@ TEST_IMPL(fs_chmod) {
|
||||
ASSERT(req.result == sizeof(test_buf));
|
||||
uv_fs_req_cleanup(&req);
|
||||
|
||||
#ifndef _WIN32
|
||||
/* Make the file write-only */
|
||||
r = uv_fs_chmod(loop, &req, "test_file", 0200, NULL);
|
||||
ASSERT(r == 0);
|
||||
@ -679,6 +709,15 @@ TEST_IMPL(fs_chmod) {
|
||||
uv_fs_req_cleanup(&req);
|
||||
|
||||
check_permission("test_file", 0200);
|
||||
#endif
|
||||
|
||||
/* Make the file read-only */
|
||||
r = uv_fs_chmod(loop, &req, "test_file", 0400, NULL);
|
||||
ASSERT(r == 0);
|
||||
ASSERT(req.result == 0);
|
||||
uv_fs_req_cleanup(&req);
|
||||
|
||||
check_permission("test_file", 0400);
|
||||
|
||||
/* Make the file read+write with sync uv_fs_fchmod */
|
||||
r = uv_fs_fchmod(loop, &req, file, 0600, NULL);
|
||||
@ -688,13 +727,24 @@ TEST_IMPL(fs_chmod) {
|
||||
|
||||
check_permission("test_file", 0600);
|
||||
|
||||
#ifndef _WIN32
|
||||
/* async chmod */
|
||||
req.data = (void*)0200;
|
||||
r = uv_fs_chmod(loop, &req, "test_file", 0200, chmod_cb);
|
||||
ASSERT(r == 0);
|
||||
uv_run(loop);
|
||||
ASSERT(chmod_cb_count == 1);
|
||||
#endif
|
||||
|
||||
/* async chmod */
|
||||
req.data = (void*)0400;
|
||||
r = uv_fs_chmod(loop, &req, "test_file", 0400, chmod_cb);
|
||||
ASSERT(r == 0);
|
||||
uv_run(loop);
|
||||
ASSERT(chmod_cb_count == 1);
|
||||
|
||||
/* async fchmod */
|
||||
req.data = (void*)0600;
|
||||
r = uv_fs_fchmod(loop, &req, file, 0600, fchmod_cb);
|
||||
ASSERT(r == 0);
|
||||
uv_run(loop);
|
||||
@ -703,7 +753,7 @@ TEST_IMPL(fs_chmod) {
|
||||
close(file);
|
||||
|
||||
/*
|
||||
* Run the loop just to check we don't have make any extranious uv_ref()
|
||||
* Run the loop just to check we don't have make any extraneous uv_ref()
|
||||
* calls. This should drop out immediately.
|
||||
*/
|
||||
uv_run(loop);
|
||||
@ -713,3 +763,61 @@ TEST_IMPL(fs_chmod) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
TEST_IMPL(fs_chown) {
|
||||
int r;
|
||||
uv_fs_t req;
|
||||
uv_file file;
|
||||
|
||||
/* Setup. */
|
||||
unlink("test_file");
|
||||
|
||||
uv_init();
|
||||
|
||||
loop = uv_default_loop();
|
||||
|
||||
r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT,
|
||||
S_IWRITE | S_IREAD, NULL);
|
||||
ASSERT(r == 0);
|
||||
ASSERT(req.result != -1);
|
||||
file = req.result;
|
||||
uv_fs_req_cleanup(&req);
|
||||
|
||||
/* sync chown */
|
||||
r = uv_fs_chown(loop, &req, "test_file", -1, -1, NULL);
|
||||
ASSERT(r == 0);
|
||||
ASSERT(req.result == 0);
|
||||
uv_fs_req_cleanup(&req);
|
||||
|
||||
/* sync fchown */
|
||||
r = uv_fs_fchown(loop, &req, file, -1, -1, NULL);
|
||||
ASSERT(r == 0);
|
||||
ASSERT(req.result == 0);
|
||||
uv_fs_req_cleanup(&req);
|
||||
|
||||
/* async chown */
|
||||
r = uv_fs_chown(loop, &req, "test_file", -1, -1, chown_cb);
|
||||
ASSERT(r == 0);
|
||||
uv_run(loop);
|
||||
ASSERT(chown_cb_count == 1);
|
||||
|
||||
/* async fchown */
|
||||
r = uv_fs_fchown(loop, &req, file, -1, -1, fchown_cb);
|
||||
ASSERT(r == 0);
|
||||
uv_run(loop);
|
||||
ASSERT(fchown_cb_count == 1);
|
||||
|
||||
close(file);
|
||||
|
||||
/*
|
||||
* Run the loop just to check we don't have make any extraneous uv_ref()
|
||||
* calls. This should drop out immediately.
|
||||
*/
|
||||
uv_run(loop);
|
||||
|
||||
/* Cleanup. */
|
||||
unlink("test_file");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -78,6 +78,7 @@ TEST_DECLARE (fs_async_dir)
|
||||
TEST_DECLARE (fs_async_sendfile)
|
||||
TEST_DECLARE (fs_fstat)
|
||||
TEST_DECLARE (fs_chmod)
|
||||
TEST_DECLARE (fs_chown)
|
||||
TEST_DECLARE (threadpool_queue_work_simple)
|
||||
#ifdef _WIN32
|
||||
TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows)
|
||||
@ -183,6 +184,7 @@ TASK_LIST_START
|
||||
TEST_ENTRY (fs_async_sendfile)
|
||||
TEST_ENTRY (fs_fstat)
|
||||
TEST_ENTRY (fs_chmod)
|
||||
TEST_ENTRY (fs_chown)
|
||||
|
||||
TEST_ENTRY (threadpool_queue_work_simple)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user