summaryrefslogtreecommitdiff
path: root/Utilities/cmlibuv/src/win/fs.c
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2016-08-31 09:01:07 -0400
committerBrad King <brad.king@kitware.com>2016-08-31 09:01:07 -0400
commitd96416fe482e17bc9f5a741d71d4a51a9b44f65e (patch)
treea708137b959a2a17d03ea8fdd08ed12cf7a63853 /Utilities/cmlibuv/src/win/fs.c
parent3800fc299fe7703c4a3976f9ba9028e1ebeb3d3e (diff)
parent3a713eaaf7d289b130acf0007b197553a6528112 (diff)
downloadcmake-d96416fe482e17bc9f5a741d71d4a51a9b44f65e.tar.gz
Merge branch 'upstream-libuv' into import-libuv
* upstream-libuv: libuv 2016-08-30 (897738b1)
Diffstat (limited to 'Utilities/cmlibuv/src/win/fs.c')
-rw-r--r--Utilities/cmlibuv/src/win/fs.c2491
1 files changed, 2491 insertions, 0 deletions
diff --git a/Utilities/cmlibuv/src/win/fs.c b/Utilities/cmlibuv/src/win/fs.c
new file mode 100644
index 0000000000..6a4157bfe5
--- /dev/null
+++ b/Utilities/cmlibuv/src/win/fs.c
@@ -0,0 +1,2491 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <direct.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <io.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include <stdio.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "req-inl.h"
+#include "handle-inl.h"
+
+#include <wincrypt.h>
+
+
+#define UV_FS_FREE_PATHS 0x0002
+#define UV_FS_FREE_PTR 0x0008
+#define UV_FS_CLEANEDUP 0x0010
+
+
+#define QUEUE_FS_TP_JOB(loop, req) \
+ do { \
+ uv__req_register(loop, req); \
+ uv__work_submit((loop), &(req)->work_req, uv__fs_work, uv__fs_done); \
+ } while (0)
+
+#define SET_REQ_RESULT(req, result_value) \
+ do { \
+ req->result = (result_value); \
+ if (req->result == -1) { \
+ req->sys_errno_ = _doserrno; \
+ req->result = uv_translate_sys_error(req->sys_errno_); \
+ } \
+ } while (0)
+
+#define SET_REQ_WIN32_ERROR(req, sys_errno) \
+ do { \
+ req->sys_errno_ = (sys_errno); \
+ req->result = uv_translate_sys_error(req->sys_errno_); \
+ } while (0)
+
+#define SET_REQ_UV_ERROR(req, uv_errno, sys_errno) \
+ do { \
+ req->result = (uv_errno); \
+ req->sys_errno_ = (sys_errno); \
+ } while (0)
+
+#define VERIFY_FD(fd, req) \
+ if (fd == -1) { \
+ req->result = UV_EBADF; \
+ req->sys_errno_ = ERROR_INVALID_HANDLE; \
+ return; \
+ }
+
+#define FILETIME_TO_UINT(filetime) \
+ (*((uint64_t*) &(filetime)) - 116444736000000000ULL)
+
+#define FILETIME_TO_TIME_T(filetime) \
+ (FILETIME_TO_UINT(filetime) / 10000000ULL)
+
+#define FILETIME_TO_TIME_NS(filetime, secs) \
+ ((FILETIME_TO_UINT(filetime) - (secs * 10000000ULL)) * 100)
+
+#define FILETIME_TO_TIMESPEC(ts, filetime) \
+ do { \
+ (ts).tv_sec = (long) FILETIME_TO_TIME_T(filetime); \
+ (ts).tv_nsec = (long) FILETIME_TO_TIME_NS(filetime, (ts).tv_sec); \
+ } while(0)
+
+#define TIME_T_TO_FILETIME(time, filetime_ptr) \
+ do { \
+ uint64_t bigtime = ((uint64_t) ((time) * 10000000ULL)) + \
+ 116444736000000000ULL; \
+ (filetime_ptr)->dwLowDateTime = bigtime & 0xFFFFFFFF; \
+ (filetime_ptr)->dwHighDateTime = bigtime >> 32; \
+ } while(0)
+
+#define IS_SLASH(c) ((c) == L'\\' || (c) == L'/')
+#define IS_LETTER(c) (((c) >= L'a' && (c) <= L'z') || \
+ ((c) >= L'A' && (c) <= L'Z'))
+
+const WCHAR JUNCTION_PREFIX[] = L"\\??\\";
+const WCHAR JUNCTION_PREFIX_LEN = 4;
+
+const WCHAR LONG_PATH_PREFIX[] = L"\\\\?\\";
+const WCHAR LONG_PATH_PREFIX_LEN = 4;
+
+const WCHAR UNC_PATH_PREFIX[] = L"\\\\?\\UNC\\";
+const WCHAR UNC_PATH_PREFIX_LEN = 8;
+
+
+void uv_fs_init() {
+ _fmode = _O_BINARY;
+}
+
+
+INLINE static int fs__capture_path(uv_fs_t* req, const char* path,
+ const char* new_path, const int copy_path) {
+ char* buf;
+ char* pos;
+ ssize_t buf_sz = 0, path_len, pathw_len = 0, new_pathw_len = 0;
+
+ /* new_path can only be set if path is also set. */
+ assert(new_path == NULL || path != NULL);
+
+ if (path != NULL) {
+ pathw_len = MultiByteToWideChar(CP_UTF8,
+ 0,
+ path,
+ -1,
+ NULL,
+ 0);
+ if (pathw_len == 0) {
+ return GetLastError();
+ }
+
+ buf_sz += pathw_len * sizeof(WCHAR);
+ }
+
+ if (path != NULL && copy_path) {
+ path_len = 1 + strlen(path);
+ buf_sz += path_len;
+ }
+
+ if (new_path != NULL) {
+ new_pathw_len = MultiByteToWideChar(CP_UTF8,
+ 0,
+ new_path,
+ -1,
+ NULL,
+ 0);
+ if (new_pathw_len == 0) {
+ return GetLastError();
+ }
+
+ buf_sz += new_pathw_len * sizeof(WCHAR);
+ }
+
+
+ if (buf_sz == 0) {
+ req->file.pathw = NULL;
+ req->fs.info.new_pathw = NULL;
+ req->path = NULL;
+ return 0;
+ }
+
+ buf = (char*) uv__malloc(buf_sz);
+ if (buf == NULL) {
+ return ERROR_OUTOFMEMORY;
+ }
+
+ pos = buf;
+
+ if (path != NULL) {
+ DWORD r = MultiByteToWideChar(CP_UTF8,
+ 0,
+ path,
+ -1,
+ (WCHAR*) pos,
+ pathw_len);
+ assert(r == (DWORD) pathw_len);
+ req->file.pathw = (WCHAR*) pos;
+ pos += r * sizeof(WCHAR);
+ } else {
+ req->file.pathw = NULL;
+ }
+
+ if (new_path != NULL) {
+ DWORD r = MultiByteToWideChar(CP_UTF8,
+ 0,
+ new_path,
+ -1,
+ (WCHAR*) pos,
+ new_pathw_len);
+ assert(r == (DWORD) new_pathw_len);
+ req->fs.info.new_pathw = (WCHAR*) pos;
+ pos += r * sizeof(WCHAR);
+ } else {
+ req->fs.info.new_pathw = NULL;
+ }
+
+ req->path = path;
+ if (path != NULL && copy_path) {
+ memcpy(pos, path, path_len);
+ assert(path_len == buf_sz - (pos - buf));
+ req->path = pos;
+ }
+
+ req->flags |= UV_FS_FREE_PATHS;
+
+ return 0;
+}
+
+
+
+INLINE static void uv_fs_req_init(uv_loop_t* loop, uv_fs_t* req,
+ uv_fs_type fs_type, const uv_fs_cb cb) {
+ uv_req_init(loop, (uv_req_t*) req);
+
+ req->type = UV_FS;
+ req->loop = loop;
+ req->flags = 0;
+ req->fs_type = fs_type;
+ req->result = 0;
+ req->ptr = NULL;
+ req->path = NULL;
+ req->cb = cb;
+}
+
+
+static int fs__wide_to_utf8(WCHAR* w_source_ptr,
+ DWORD w_source_len,
+ char** target_ptr,
+ uint64_t* target_len_ptr) {
+ int r;
+ int target_len;
+ char* target;
+ target_len = WideCharToMultiByte(CP_UTF8,
+ 0,
+ w_source_ptr,
+ w_source_len,
+ NULL,
+ 0,
+ NULL,
+ NULL);
+
+ if (target_len == 0) {
+ return -1;
+ }
+
+ if (target_len_ptr != NULL) {
+ *target_len_ptr = target_len;
+ }
+
+ if (target_ptr == NULL) {
+ return 0;
+ }
+
+ target = uv__malloc(target_len + 1);
+ if (target == NULL) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return -1;
+ }
+
+ r = WideCharToMultiByte(CP_UTF8,
+ 0,
+ w_source_ptr,
+ w_source_len,
+ target,
+ target_len,
+ NULL,
+ NULL);
+ assert(r == target_len);
+ target[target_len] = '\0';
+ *target_ptr = target;
+ return 0;
+}
+
+
+INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr,
+ uint64_t* target_len_ptr) {
+ char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ REPARSE_DATA_BUFFER* reparse_data = (REPARSE_DATA_BUFFER*) buffer;
+ WCHAR* w_target;
+ DWORD w_target_len;
+ DWORD bytes;
+
+ if (!DeviceIoControl(handle,
+ FSCTL_GET_REPARSE_POINT,
+ NULL,
+ 0,
+ buffer,
+ sizeof buffer,
+ &bytes,
+ NULL)) {
+ return -1;
+ }
+
+ if (reparse_data->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
+ /* Real symlink */
+ w_target = reparse_data->SymbolicLinkReparseBuffer.PathBuffer +
+ (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset /
+ sizeof(WCHAR));
+ w_target_len =
+ reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength /
+ sizeof(WCHAR);
+
+ /* Real symlinks can contain pretty much everything, but the only thing */
+ /* we really care about is undoing the implicit conversion to an NT */
+ /* namespaced path that CreateSymbolicLink will perform on absolute */
+ /* paths. If the path is win32-namespaced then the user must have */
+ /* explicitly made it so, and we better just return the unmodified */
+ /* reparse data. */
+ if (w_target_len >= 4 &&
+ w_target[0] == L'\\' &&
+ w_target[1] == L'?' &&
+ w_target[2] == L'?' &&
+ w_target[3] == L'\\') {
+ /* Starts with \??\ */
+ if (w_target_len >= 6 &&
+ ((w_target[4] >= L'A' && w_target[4] <= L'Z') ||
+ (w_target[4] >= L'a' && w_target[4] <= L'z')) &&
+ w_target[5] == L':' &&
+ (w_target_len == 6 || w_target[6] == L'\\')) {
+ /* \??\<drive>:\ */
+ w_target += 4;
+ w_target_len -= 4;
+
+ } else if (w_target_len >= 8 &&
+ (w_target[4] == L'U' || w_target[4] == L'u') &&
+ (w_target[5] == L'N' || w_target[5] == L'n') &&
+ (w_target[6] == L'C' || w_target[6] == L'c') &&
+ w_target[7] == L'\\') {
+ /* \??\UNC\<server>\<share>\ - make sure the final path looks like */
+ /* \\<server>\<share>\ */
+ w_target += 6;
+ w_target[0] = L'\\';
+ w_target_len -= 6;
+ }
+ }
+
+ } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
+ /* Junction. */
+ w_target = reparse_data->MountPointReparseBuffer.PathBuffer +
+ (reparse_data->MountPointReparseBuffer.SubstituteNameOffset /
+ sizeof(WCHAR));
+ w_target_len = reparse_data->MountPointReparseBuffer.SubstituteNameLength /
+ sizeof(WCHAR);
+
+ /* Only treat junctions that look like \??\<drive>:\ as symlink. */
+ /* Junctions can also be used as mount points, like \??\Volume{<guid>}, */
+ /* but that's confusing for programs since they wouldn't be able to */
+ /* actually understand such a path when returned by uv_readlink(). */
+ /* UNC paths are never valid for junctions so we don't care about them. */
+ if (!(w_target_len >= 6 &&
+ w_target[0] == L'\\' &&
+ w_target[1] == L'?' &&
+ w_target[2] == L'?' &&
+ w_target[3] == L'\\' &&
+ ((w_target[4] >= L'A' && w_target[4] <= L'Z') ||
+ (w_target[4] >= L'a' && w_target[4] <= L'z')) &&
+ w_target[5] == L':' &&
+ (w_target_len == 6 || w_target[6] == L'\\'))) {
+ SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
+ return -1;
+ }
+
+ /* Remove leading \??\ */
+ w_target += 4;
+ w_target_len -= 4;
+
+ } else {
+ /* Reparse tag does not indicate a symlink. */
+ SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
+ return -1;
+ }
+
+ return fs__wide_to_utf8(w_target, w_target_len, target_ptr, target_len_ptr);
+}
+
+
+void fs__open(uv_fs_t* req) {
+ DWORD access;
+ DWORD share;
+ DWORD disposition;
+ DWORD attributes = 0;
+ HANDLE file;
+ int fd, current_umask;
+ int flags = req->fs.info.file_flags;
+
+ /* Obtain the active umask. umask() never fails and returns the previous */
+ /* umask. */
+ current_umask = umask(0);
+ umask(current_umask);
+
+ /* convert flags and mode to CreateFile parameters */
+ switch (flags & (_O_RDONLY | _O_WRONLY | _O_RDWR)) {
+ case _O_RDONLY:
+ access = FILE_GENERIC_READ;
+ attributes |= FILE_FLAG_BACKUP_SEMANTICS;
+ break;
+ case _O_WRONLY:
+ access = FILE_GENERIC_WRITE;
+ break;
+ case _O_RDWR:
+ access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
+ break;
+ default:
+ goto einval;
+ }
+
+ if (flags & _O_APPEND) {
+ access &= ~FILE_WRITE_DATA;
+ access |= FILE_APPEND_DATA;
+ attributes &= ~FILE_FLAG_BACKUP_SEMANTICS;
+ }
+
+ /*
+ * Here is where we deviate significantly from what CRT's _open()
+ * does. We indiscriminately use all the sharing modes, to match
+ * UNIX semantics. In particular, this ensures that the file can
+ * be deleted even whilst it's open, fixing issue #1449.
+ */
+ share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+
+ switch (flags & (_O_CREAT | _O_EXCL | _O_TRUNC)) {
+ case 0:
+ case _O_EXCL:
+ disposition = OPEN_EXISTING;
+ break;
+ case _O_CREAT:
+ disposition = OPEN_ALWAYS;
+ break;
+ case _O_CREAT | _O_EXCL:
+ case _O_CREAT | _O_TRUNC | _O_EXCL:
+ disposition = CREATE_NEW;
+ break;
+ case _O_TRUNC:
+ case _O_TRUNC | _O_EXCL:
+ disposition = TRUNCATE_EXISTING;
+ break;
+ case _O_CREAT | _O_TRUNC:
+ disposition = CREATE_ALWAYS;
+ break;
+ default:
+ goto einval;
+ }
+
+ attributes |= FILE_ATTRIBUTE_NORMAL;
+ if (flags & _O_CREAT) {
+ if (!((req->fs.info.mode & ~current_umask) & _S_IWRITE)) {
+ attributes |= FILE_ATTRIBUTE_READONLY;
+ }
+ }
+
+ if (flags & _O_TEMPORARY ) {
+ attributes |= FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY;
+ access |= DELETE;
+ }
+
+ if (flags & _O_SHORT_LIVED) {
+ attributes |= FILE_ATTRIBUTE_TEMPORARY;
+ }
+
+ switch (flags & (_O_SEQUENTIAL | _O_RANDOM)) {
+ case 0:
+ break;
+ case _O_SEQUENTIAL:
+ attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
+ break;
+ case _O_RANDOM:
+ attributes |= FILE_FLAG_RANDOM_ACCESS;
+ break;
+ default:
+ goto einval;
+ }
+
+ /* Setting this flag makes it possible to open a directory. */
+ attributes |= FILE_FLAG_BACKUP_SEMANTICS;
+
+ file = CreateFileW(req->file.pathw,
+ access,
+ share,
+ NULL,
+ disposition,
+ attributes,
+ NULL);
+ if (file == INVALID_HANDLE_VALUE) {
+ DWORD error = GetLastError();
+ if (error == ERROR_FILE_EXISTS && (flags & _O_CREAT) &&
+ !(flags & _O_EXCL)) {
+ /* Special case: when ERROR_FILE_EXISTS happens and O_CREAT was */
+ /* specified, it means the path referred to a directory. */
+ SET_REQ_UV_ERROR(req, UV_EISDIR, error);
+ } else {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ }
+ return;
+ }
+
+ fd = _open_osfhandle((intptr_t) file, flags);
+ if (fd < 0) {
+ /* The only known failure mode for _open_osfhandle() is EMFILE, in which
+ * case GetLastError() will return zero. However we'll try to handle other
+ * errors as well, should they ever occur.
+ */
+ if (errno == EMFILE)
+ SET_REQ_UV_ERROR(req, UV_EMFILE, ERROR_TOO_MANY_OPEN_FILES);
+ else if (GetLastError() != ERROR_SUCCESS)
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ else
+ SET_REQ_WIN32_ERROR(req, UV_UNKNOWN);
+ CloseHandle(file);
+ return;
+ }
+
+ SET_REQ_RESULT(req, fd);
+ return;
+
+ einval:
+ SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+}
+
+void fs__close(uv_fs_t* req) {
+ int fd = req->file.fd;
+ int result;
+
+ VERIFY_FD(fd, req);
+
+ if (fd > 2)
+ result = _close(fd);
+ else
+ result = 0;
+
+ /* _close doesn't set _doserrno on failure, but it does always set errno
+ * to EBADF on failure.
+ */
+ if (result == -1) {
+ assert(errno == EBADF);
+ SET_REQ_UV_ERROR(req, UV_EBADF, ERROR_INVALID_HANDLE);
+ } else {
+ req->result = 0;
+ }
+}
+
+
+void fs__read(uv_fs_t* req) {
+ int fd = req->file.fd;
+ int64_t offset = req->fs.info.offset;
+ HANDLE handle;
+ OVERLAPPED overlapped, *overlapped_ptr;
+ LARGE_INTEGER offset_;
+ DWORD bytes;
+ DWORD error;
+ int result;
+ unsigned int index;
+
+ VERIFY_FD(fd, req);
+
+ handle = uv__get_osfhandle(fd);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
+ return;
+ }
+
+ if (offset != -1) {
+ memset(&overlapped, 0, sizeof overlapped);
+ overlapped_ptr = &overlapped;
+ } else {
+ overlapped_ptr = NULL;
+ }
+
+ index = 0;
+ bytes = 0;
+ do {
+ DWORD incremental_bytes;
+
+ if (offset != -1) {
+ offset_.QuadPart = offset + bytes;
+ overlapped.Offset = offset_.LowPart;
+ overlapped.OffsetHigh = offset_.HighPart;
+ }
+
+ result = ReadFile(handle,
+ req->fs.info.bufs[index].base,
+ req->fs.info.bufs[index].len,
+ &incremental_bytes,
+ overlapped_ptr);
+ bytes += incremental_bytes;
+ ++index;
+ } while (result && index < req->fs.info.nbufs);
+
+ if (result || bytes > 0) {
+ SET_REQ_RESULT(req, bytes);
+ } else {
+ error = GetLastError();
+ if (error == ERROR_HANDLE_EOF) {
+ SET_REQ_RESULT(req, bytes);
+ } else {
+ SET_REQ_WIN32_ERROR(req, error);
+ }
+ }
+}
+
+
+void fs__write(uv_fs_t* req) {
+ int fd = req->file.fd;
+ int64_t offset = req->fs.info.offset;
+ HANDLE handle;
+ OVERLAPPED overlapped, *overlapped_ptr;
+ LARGE_INTEGER offset_;
+ DWORD bytes;
+ int result;
+ unsigned int index;
+
+ VERIFY_FD(fd, req);
+
+ handle = uv__get_osfhandle(fd);
+ if (handle == INVALID_HANDLE_VALUE) {
+ SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
+ return;
+ }
+
+ if (offset != -1) {
+ memset(&overlapped, 0, sizeof overlapped);
+ overlapped_ptr = &overlapped;
+ } else {
+ overlapped_ptr = NULL;
+ }
+
+ index = 0;
+ bytes = 0;
+ do {
+ DWORD incremental_bytes;
+
+ if (offset != -1) {
+ offset_.QuadPart = offset + bytes;
+ overlapped.Offset = offset_.LowPart;
+ overlapped.OffsetHigh = offset_.HighPart;
+ }
+
+ result = WriteFile(handle,
+ req->fs.info.bufs[index].base,
+ req->fs.info.bufs[index].len,
+ &incremental_bytes,
+ overlapped_ptr);
+ bytes += incremental_bytes;
+ ++index;
+ } while (result && index < req->fs.info.nbufs);
+
+ if (result || bytes > 0) {
+ SET_REQ_RESULT(req, bytes);
+ } else {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ }
+}
+
+
+void fs__rmdir(uv_fs_t* req) {
+ int result = _wrmdir(req->file.pathw);
+ SET_REQ_RESULT(req, result);
+}
+
+
+void fs__unlink(uv_fs_t* req) {
+ const WCHAR* pathw = req->file.pathw;
+ HANDLE handle;
+ BY_HANDLE_FILE_INFORMATION info;
+ FILE_DISPOSITION_INFORMATION disposition;
+ IO_STATUS_BLOCK iosb;
+ NTSTATUS status;
+
+ handle = CreateFileW(pathw,
+ FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | DELETE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ return;
+ }
+
+ if (!GetFileInformationByHandle(handle, &info)) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ CloseHandle(handle);
+ return;
+ }
+
+ if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ /* Do not allow deletion of directories, unless it is a symlink. When */
+ /* the path refers to a non-symlink directory, report EPERM as mandated */
+ /* by POSIX.1. */
+
+ /* Check if it is a reparse point. If it's not, it's a normal directory. */
+ if (!(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+ SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED);
+ CloseHandle(handle);
+ return;
+ }
+
+ /* Read the reparse point and check if it is a valid symlink. */
+ /* If not, don't unlink. */
+ if (fs__readlink_handle(handle, NULL, NULL) < 0) {
+ DWORD error = GetLastError();
+ if (error == ERROR_SYMLINK_NOT_SUPPORTED)
+ error = ERROR_ACCESS_DENIED;
+ SET_REQ_WIN32_ERROR(req, error);
+ CloseHandle(handle);
+ return;
+ }
+ }
+
+ if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
+ /* Remove read-only attribute */
+ FILE_BASIC_INFORMATION basic = { 0 };
+
+ basic.FileAttributes = info.dwFileAttributes & ~(FILE_ATTRIBUTE_READONLY);
+
+ status = pNtSetInformationFile(handle,
+ &iosb,
+ &basic,
+ sizeof basic,
+ FileBasicInformation);
+ if (!NT_SUCCESS(status)) {
+ SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
+ CloseHandle(handle);
+ return;
+ }
+ }
+
+ /* Try to set the delete flag. */
+ disposition.DeleteFile = TRUE;
+ status = pNtSetInformationFile(handle,
+ &iosb,
+ &disposition,
+ sizeof disposition,
+ FileDispositionInformation);
+ if (NT_SUCCESS(status)) {
+ SET_REQ_SUCCESS(req);
+ } else {
+ SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
+ }
+
+ CloseHandle(handle);
+}
+
+
+void fs__mkdir(uv_fs_t* req) {
+ /* TODO: use req->mode. */
+ int result = _wmkdir(req->file.pathw);
+ SET_REQ_RESULT(req, result);
+}
+
+
+/* OpenBSD original: lib/libc/stdio/mktemp.c */
+void fs__mkdtemp(uv_fs_t* req) {
+ static const WCHAR *tempchars =
+ L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ static const size_t num_chars = 62;
+ static const size_t num_x = 6;
+ WCHAR *cp, *ep;
+ unsigned int tries, i;
+ size_t len;
+ HCRYPTPROV h_crypt_prov;
+ uint64_t v;
+ BOOL released;
+
+ len = wcslen(req->file.pathw);
+ ep = req->file.pathw + len;
+ if (len < num_x || wcsncmp(ep - num_x, L"XXXXXX", num_x)) {
+ SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+ return;
+ }
+
+ if (!CryptAcquireContext(&h_crypt_prov, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT)) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ return;
+ }
+
+ tries = TMP_MAX;
+ do {
+ if (!CryptGenRandom(h_crypt_prov, sizeof(v), (BYTE*) &v)) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ break;
+ }
+
+ cp = ep - num_x;
+ for (i = 0; i < num_x; i++) {
+ *cp++ = tempchars[v % num_chars];
+ v /= num_chars;
+ }
+
+ if (_wmkdir(req->file.pathw) == 0) {
+ len = strlen(req->path);
+ wcstombs((char*) req->path + len - num_x, ep - num_x, num_x);
+ SET_REQ_RESULT(req, 0);
+ break;
+ } else if (errno != EEXIST) {
+ SET_REQ_RESULT(req, -1);
+ break;
+ }
+ } while (--tries);
+
+ released = CryptReleaseContext(h_crypt_prov, 0);
+ assert(released);
+ if (tries == 0) {
+ SET_REQ_RESULT(req, -1);
+ }
+}
+
+
+void fs__scandir(uv_fs_t* req) {
+ static const size_t dirents_initial_size = 32;
+
+ HANDLE dir_handle = INVALID_HANDLE_VALUE;
+
+ uv__dirent_t** dirents = NULL;
+ size_t dirents_size = 0;
+ size_t dirents_used = 0;
+
+ IO_STATUS_BLOCK iosb;
+ NTSTATUS status;
+
+ /* Buffer to hold directory entries returned by NtQueryDirectoryFile.
+ * It's important that this buffer can hold at least one entry, regardless
+ * of the length of the file names present in the enumerated directory.
+ * A file name is at most 256 WCHARs long.
+ * According to MSDN, the buffer must be aligned at an 8-byte boundary.
+ */
+#if _MSC_VER
+ __declspec(align(8)) char buffer[8192];
+#else
+ __attribute__ ((aligned (8))) char buffer[8192];
+#endif
+
+ STATIC_ASSERT(sizeof buffer >=
+ sizeof(FILE_DIRECTORY_INFORMATION) + 256 * sizeof(WCHAR));
+
+ /* Open the directory. */
+ dir_handle =
+ CreateFileW(req->file.pathw,
+ FILE_LIST_DIRECTORY | SYNCHRONIZE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (dir_handle == INVALID_HANDLE_VALUE)
+ goto win32_error;
+
+ /* Read the first chunk. */
+ status = pNtQueryDirectoryFile(dir_handle,
+ NULL,
+ NULL,
+ NULL,
+ &iosb,
+ &buffer,
+ sizeof buffer,
+ FileDirectoryInformation,
+ FALSE,
+ NULL,
+ TRUE);
+
+ /* If the handle is not a directory, we'll get STATUS_INVALID_PARAMETER.
+ * This should be reported back as UV_ENOTDIR.
+ */
+ if (status == STATUS_INVALID_PARAMETER)
+ goto not_a_directory_error;
+
+ while (NT_SUCCESS(status)) {
+ char* position = buffer;
+ size_t next_entry_offset = 0;
+
+ do {
+ FILE_DIRECTORY_INFORMATION* info;
+ uv__dirent_t* dirent;
+
+ size_t wchar_len;
+ size_t utf8_len;
+
+ /* Obtain a pointer to the current directory entry. */
+ position += next_entry_offset;
+ info = (FILE_DIRECTORY_INFORMATION*) position;
+
+ /* Fetch the offset to the next directory entry. */
+ next_entry_offset = info->NextEntryOffset;
+
+ /* Compute the length of the filename in WCHARs. */
+ wchar_len = info->FileNameLength / sizeof info->FileName[0];
+
+ /* Skip over '.' and '..' entries. It has been reported that
+ * the SharePoint driver includes the terminating zero byte in
+ * the filename length. Strip those first.
+ */
+ while (wchar_len > 0 && info->FileName[wchar_len - 1] == L'\0')
+ wchar_len -= 1;
+
+ if (wchar_len == 0)
+ continue;
+ if (wchar_len == 1 && info->FileName[0] == L'.')
+ continue;
+ if (wchar_len == 2 && info->FileName[0] == L'.' &&
+ info->FileName[1] == L'.')
+ continue;
+
+ /* Compute the space required to store the filename as UTF-8. */
+ utf8_len = WideCharToMultiByte(
+ CP_UTF8, 0, &info->FileName[0], wchar_len, NULL, 0, NULL, NULL);
+ if (utf8_len == 0)
+ goto win32_error;
+
+ /* Resize the dirent array if needed. */
+ if (dirents_used >= dirents_size) {
+ size_t new_dirents_size =
+ dirents_size == 0 ? dirents_initial_size : dirents_size << 1;
+ uv__dirent_t** new_dirents =
+ uv__realloc(dirents, new_dirents_size * sizeof *dirents);
+
+ if (new_dirents == NULL)
+ goto out_of_memory_error;
+
+ dirents_size = new_dirents_size;
+ dirents = new_dirents;
+ }
+
+ /* Allocate space for the uv dirent structure. The dirent structure
+ * includes room for the first character of the filename, but `utf8_len`
+ * doesn't count the NULL terminator at this point.
+ */
+ dirent = uv__malloc(sizeof *dirent + utf8_len);
+ if (dirent == NULL)
+ goto out_of_memory_error;
+
+ dirents[dirents_used++] = dirent;
+
+ /* Convert file name to UTF-8. */
+ if (WideCharToMultiByte(CP_UTF8,
+ 0,
+ &info->FileName[0],
+ wchar_len,
+ &dirent->d_name[0],
+ utf8_len,
+ NULL,
+ NULL) == 0)
+ goto win32_error;
+
+ /* Add a null terminator to the filename. */
+ dirent->d_name[utf8_len] = '\0';
+
+ /* Fill out the type field. */
+ if (info->FileAttributes & FILE_ATTRIBUTE_DEVICE)
+ dirent->d_type = UV__DT_CHAR;
+ else if (info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
+ dirent->d_type = UV__DT_LINK;
+ else if (info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ dirent->d_type = UV__DT_DIR;
+ else
+ dirent->d_type = UV__DT_FILE;
+ } while (next_entry_offset != 0);
+
+ /* Read the next chunk. */
+ status = pNtQueryDirectoryFile(dir_handle,
+ NULL,
+ NULL,
+ NULL,
+ &iosb,
+ &buffer,
+ sizeof buffer,
+ FileDirectoryInformation,
+ FALSE,
+ NULL,
+ FALSE);
+
+ /* After the first pNtQueryDirectoryFile call, the function may return
+ * STATUS_SUCCESS even if the buffer was too small to hold at least one
+ * directory entry.
+ */
+ if (status == STATUS_SUCCESS && iosb.Information == 0)
+ status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ if (status != STATUS_NO_MORE_FILES)
+ goto nt_error;
+
+ CloseHandle(dir_handle);
+
+ /* Store the result in the request object. */
+ req->ptr = dirents;
+ if (dirents != NULL)
+ req->flags |= UV_FS_FREE_PTR;
+
+ SET_REQ_RESULT(req, dirents_used);
+
+ /* `nbufs` will be used as index by uv_fs_scandir_next. */
+ req->fs.info.nbufs = 0;
+
+ return;
+
+nt_error:
+ SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
+ goto cleanup;
+
+win32_error:
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ goto cleanup;
+
+not_a_directory_error:
+ SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY);
+ goto cleanup;
+
+out_of_memory_error:
+ SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+ goto cleanup;
+
+cleanup:
+ if (dir_handle != INVALID_HANDLE_VALUE)
+ CloseHandle(dir_handle);
+ while (dirents_used > 0)
+ uv__free(dirents[--dirents_used]);
+ if (dirents != NULL)
+ uv__free(dirents);
+}
+
+
+INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf) {
+ FILE_ALL_INFORMATION file_info;
+ FILE_FS_VOLUME_INFORMATION volume_info;
+ NTSTATUS nt_status;
+ IO_STATUS_BLOCK io_status;
+
+ nt_status = pNtQueryInformationFile(handle,
+ &io_status,
+ &file_info,
+ sizeof file_info,
+ FileAllInformation);
+
+ /* Buffer overflow (a warning status code) is expected here. */
+ if (NT_ERROR(nt_status)) {
+ SetLastError(pRtlNtStatusToDosError(nt_status));
+ return -1;
+ }
+
+ nt_status = pNtQueryVolumeInformationFile(handle,
+ &io_status,
+ &volume_info,
+ sizeof volume_info,
+ FileFsVolumeInformation);
+
+ /* Buffer overflow (a warning status code) is expected here. */
+ if (io_status.Status == STATUS_NOT_IMPLEMENTED) {
+ statbuf->st_dev = 0;
+ } else if (NT_ERROR(nt_status)) {
+ SetLastError(pRtlNtStatusToDosError(nt_status));
+ return -1;
+ } else {
+ statbuf->st_dev = volume_info.VolumeSerialNumber;
+ }
+
+ /* Todo: st_mode should probably always be 0666 for everyone. We might also
+ * want to report 0777 if the file is a .exe or a directory.
+ *
+ * Currently it's based on whether the 'readonly' attribute is set, which
+ * makes little sense because the semantics are so different: the 'read-only'
+ * flag is just a way for a user to protect against accidental deletion, and
+ * serves no security purpose. Windows uses ACLs for that.
+ *
+ * Also people now use uv_fs_chmod() to take away the writable bit for good
+ * reasons. Windows however just makes the file read-only, which makes it
+ * impossible to delete the file afterwards, since read-only files can't be
+ * deleted.
+ *
+ * IOW it's all just a clusterfuck and we should think of something that
+ * makes slightly more sense.
+ *
+ * And uv_fs_chmod should probably just fail on windows or be a total no-op.
+ * There's nothing sensible it can do anyway.
+ */
+ statbuf->st_mode = 0;
+
+ if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+ /*
+ * It is possible for a file to have FILE_ATTRIBUTE_REPARSE_POINT but not have
+ * any link data. In that case DeviceIoControl() in fs__readlink_handle() sets
+ * the last error to ERROR_NOT_A_REPARSE_POINT. Then the stat result mode
+ * calculated below will indicate a normal directory or file, as if
+ * FILE_ATTRIBUTE_REPARSE_POINT was not present.
+ */
+ if (fs__readlink_handle(handle, NULL, &statbuf->st_size) == 0) {
+ statbuf->st_mode |= S_IFLNK;
+ } else if (GetLastError() != ERROR_NOT_A_REPARSE_POINT) {
+ return -1;
+ }
+ }
+
+ if (statbuf->st_mode == 0) {
+ if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ statbuf->st_mode |= _S_IFDIR;
+ statbuf->st_size = 0;
+ } else {
+ statbuf->st_mode |= _S_IFREG;
+ statbuf->st_size = file_info.StandardInformation.EndOfFile.QuadPart;
+ }
+ }
+
+ if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_READONLY)
+ statbuf->st_mode |= _S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6);
+ else
+ statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
+ ((_S_IREAD | _S_IWRITE) >> 6);
+
+ FILETIME_TO_TIMESPEC(statbuf->st_atim, file_info.BasicInformation.LastAccessTime);
+ FILETIME_TO_TIMESPEC(statbuf->st_ctim, file_info.BasicInformation.ChangeTime);
+ FILETIME_TO_TIMESPEC(statbuf->st_mtim, file_info.BasicInformation.LastWriteTime);
+ FILETIME_TO_TIMESPEC(statbuf->st_birthtim, file_info.BasicInformation.CreationTime);
+
+ statbuf->st_ino = file_info.InternalInformation.IndexNumber.QuadPart;
+
+ /* st_blocks contains the on-disk allocation size in 512-byte units. */
+ statbuf->st_blocks =
+ file_info.StandardInformation.AllocationSize.QuadPart >> 9ULL;
+
+ statbuf->st_nlink = file_info.StandardInformation.NumberOfLinks;
+
+ /* The st_blksize is supposed to be the 'optimal' number of bytes for reading
+ * and writing to the disk. That is, for any definition of 'optimal' - it's
+ * supposed to at least avoid read-update-write behavior when writing to the
+ * disk.
+ *
+ * However nobody knows this and even fewer people actually use this value,
+ * and in order to fill it out we'd have to make another syscall to query the
+ * volume for FILE_FS_SECTOR_SIZE_INFORMATION.
+ *
+ * Therefore we'll just report a sensible value that's quite commonly okay
+ * on modern hardware.
+ */
+ statbuf->st_blksize = 2048;
+
+ /* Todo: set st_flags to something meaningful. Also provide a wrapper for
+ * chattr(2).
+ */
+ statbuf->st_flags = 0;
+
+ /* Windows has nothing sensible to say about these values, so they'll just
+ * remain empty.
+ */
+ statbuf->st_gid = 0;
+ statbuf->st_uid = 0;
+ statbuf->st_rdev = 0;
+ statbuf->st_gen = 0;
+
+ return 0;
+}
+
+
+INLINE static void fs__stat_prepare_path(WCHAR* pathw) {
+ size_t len = wcslen(pathw);
+
+ /* TODO: ignore namespaced paths. */
+ if (len > 1 && pathw[len - 2] != L':' &&
+ (pathw[len - 1] == L'\\' || pathw[len - 1] == L'/')) {
+ pathw[len - 1] = '\0';
+ }
+}
+
+
+INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) {
+ HANDLE handle;
+ DWORD flags;
+
+ flags = FILE_FLAG_BACKUP_SEMANTICS;
+ if (do_lstat) {
+ flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+ }
+
+ handle = CreateFileW(req->file.pathw,
+ FILE_READ_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ flags,
+ NULL);
+ if (handle == INVALID_HANDLE_VALUE) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ return;
+ }
+
+ if (fs__stat_handle(handle, &req->statbuf) != 0) {
+ DWORD error = GetLastError();
+ if (do_lstat && error == ERROR_SYMLINK_NOT_SUPPORTED) {
+ /* We opened a reparse point but it was not a symlink. Try again. */
+ fs__stat_impl(req, 0);
+
+ } else {
+ /* Stat failed. */
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ }
+
+ CloseHandle(handle);
+ return;
+ }
+
+ req->ptr = &req->statbuf;
+ req->result = 0;
+ CloseHandle(handle);
+}
+
+
+static void fs__stat(uv_fs_t* req) {
+ fs__stat_prepare_path(req->file.pathw);
+ fs__stat_impl(req, 0);
+}
+
+
+static void fs__lstat(uv_fs_t* req) {
+ fs__stat_prepare_path(req->file.pathw);
+ fs__stat_impl(req, 1);
+}
+
+
+static void fs__fstat(uv_fs_t* req) {
+ int fd = req->file.fd;
+ HANDLE handle;
+
+ VERIFY_FD(fd, req);
+
+ handle = uv__get_osfhandle(fd);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
+ return;
+ }
+
+ if (fs__stat_handle(handle, &req->statbuf) != 0) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ return;
+ }
+
+ req->ptr = &req->statbuf;
+ req->result = 0;
+}
+
+
+static void fs__rename(uv_fs_t* req) {
+ if (!MoveFileExW(req->file.pathw, req->fs.info.new_pathw, MOVEFILE_REPLACE_EXISTING)) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ return;
+ }
+
+ SET_REQ_RESULT(req, 0);
+}
+
+
+INLINE static void fs__sync_impl(uv_fs_t* req) {
+ int fd = req->file.fd;
+ int result;
+
+ VERIFY_FD(fd, req);
+
+ result = FlushFileBuffers(uv__get_osfhandle(fd)) ? 0 : -1;
+ if (result == -1) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ } else {
+ SET_REQ_RESULT(req, result);
+ }
+}
+
+
+static void fs__fsync(uv_fs_t* req) {
+ fs__sync_impl(req);
+}
+
+
+static void fs__fdatasync(uv_fs_t* req) {
+ fs__sync_impl(req);
+}
+
+
+static void fs__ftruncate(uv_fs_t* req) {
+ int fd = req->file.fd;
+ HANDLE handle;
+ NTSTATUS status;
+ IO_STATUS_BLOCK io_status;
+ FILE_END_OF_FILE_INFORMATION eof_info;
+
+ VERIFY_FD(fd, req);
+
+ handle = uv__get_osfhandle(fd);
+
+ eof_info.EndOfFile.QuadPart = req->fs.info.offset;
+
+ status = pNtSetInformationFile(handle,
+ &io_status,
+ &eof_info,
+ sizeof eof_info,
+ FileEndOfFileInformation);
+
+ if (NT_SUCCESS(status)) {
+ SET_REQ_RESULT(req, 0);
+ } else {
+ SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
+ }
+}
+
+
+static void fs__sendfile(uv_fs_t* req) {
+ int fd_in = req->file.fd, fd_out = req->fs.info.fd_out;
+ size_t length = req->fs.info.bufsml[0].len;
+ int64_t offset = req->fs.info.offset;
+ const size_t max_buf_size = 65536;
+ size_t buf_size = length < max_buf_size ? length : max_buf_size;
+ int n, result = 0;
+ int64_t result_offset = 0;
+ char* buf = (char*) uv__malloc(buf_size);
+ if (!buf) {
+ uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+ }
+
+ if (offset != -1) {
+ result_offset = _lseeki64(fd_in, offset, SEEK_SET);
+ }
+
+ if (result_offset == -1) {
+ result = -1;
+ } else {
+ while (length > 0) {
+ n = _read(fd_in, buf, length < buf_size ? length : buf_size);
+ if (n == 0) {
+ break;
+ } else if (n == -1) {
+ result = -1;
+ break;
+ }
+
+ length -= n;
+
+ n = _write(fd_out, buf, n);
+ if (n == -1) {
+ result = -1;
+ break;
+ }
+
+ result += n;
+ }
+ }
+
+ uv__free(buf);
+
+ SET_REQ_RESULT(req, result);
+}
+
+
+static void fs__access(uv_fs_t* req) {
+ DWORD attr = GetFileAttributesW(req->file.pathw);
+
+ if (attr == INVALID_FILE_ATTRIBUTES) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ return;
+ }
+
+ /*
+ * Access is possible if
+ * - write access wasn't requested,
+ * - or the file isn't read-only,
+ * - or it's a directory.
+ * (Directories cannot be read-only on Windows.)
+ */
+ if (!(req->flags & W_OK) ||
+ !(attr & FILE_ATTRIBUTE_READONLY) ||
+ (attr & FILE_ATTRIBUTE_DIRECTORY)) {
+ SET_REQ_RESULT(req, 0);
+ } else {
+ SET_REQ_WIN32_ERROR(req, UV_EPERM);
+ }
+
+}
+
+
+static void fs__chmod(uv_fs_t* req) {
+ int result = _wchmod(req->file.pathw, req->fs.info.mode);
+ SET_REQ_RESULT(req, result);
+}
+
+
+static void fs__fchmod(uv_fs_t* req) {
+ int fd = req->file.fd;
+ HANDLE handle;
+ NTSTATUS nt_status;
+ IO_STATUS_BLOCK io_status;
+ FILE_BASIC_INFORMATION file_info;
+
+ VERIFY_FD(fd, req);
+
+ handle = uv__get_osfhandle(fd);
+
+ nt_status = pNtQueryInformationFile(handle,
+ &io_status,
+ &file_info,
+ sizeof file_info,
+ FileBasicInformation);
+
+ if (!NT_SUCCESS(nt_status)) {
+ SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
+ return;
+ }
+
+ if (req->fs.info.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_SUCCESS(nt_status)) {
+ SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
+ return;
+ }
+
+ SET_REQ_SUCCESS(req);
+}
+
+
+INLINE static int fs__utime_handle(HANDLE handle, double atime, double mtime) {
+ FILETIME filetime_a, filetime_m;
+
+ TIME_T_TO_FILETIME(atime, &filetime_a);
+ TIME_T_TO_FILETIME(mtime, &filetime_m);
+
+ if (!SetFileTime(handle, NULL, &filetime_a, &filetime_m)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void fs__utime(uv_fs_t* req) {
+ HANDLE handle;
+
+ handle = CreateFileW(req->file.pathw,
+ FILE_WRITE_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ return;
+ }
+
+ if (fs__utime_handle(handle, req->fs.time.atime, req->fs.time.mtime) != 0) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ CloseHandle(handle);
+ return;
+ }
+
+ CloseHandle(handle);
+
+ req->result = 0;
+}
+
+
+static void fs__futime(uv_fs_t* req) {
+ int fd = req->file.fd;
+ HANDLE handle;
+ VERIFY_FD(fd, req);
+
+ handle = uv__get_osfhandle(fd);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
+ return;
+ }
+
+ if (fs__utime_handle(handle, req->fs.time.atime, req->fs.time.mtime) != 0) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ return;
+ }
+
+ req->result = 0;
+}
+
+
+static void fs__link(uv_fs_t* req) {
+ DWORD r = CreateHardLinkW(req->fs.info.new_pathw, req->file.pathw, NULL);
+ if (r == 0) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ } else {
+ req->result = 0;
+ }
+}
+
+
+static void fs__create_junction(uv_fs_t* req, const WCHAR* path,
+ const WCHAR* new_path) {
+ HANDLE handle = INVALID_HANDLE_VALUE;
+ REPARSE_DATA_BUFFER *buffer = NULL;
+ int created = 0;
+ int target_len;
+ int is_absolute, is_long_path;
+ int needed_buf_size, used_buf_size, used_data_size, path_buf_len;
+ int start, len, i;
+ int add_slash;
+ DWORD bytes;
+ WCHAR* path_buf;
+
+ target_len = wcslen(path);
+ is_long_path = wcsncmp(path, LONG_PATH_PREFIX, LONG_PATH_PREFIX_LEN) == 0;
+
+ if (is_long_path) {
+ is_absolute = 1;
+ } else {
+ is_absolute = target_len >= 3 && IS_LETTER(path[0]) &&
+ path[1] == L':' && IS_SLASH(path[2]);
+ }
+
+ if (!is_absolute) {
+ /* Not supporting relative paths */
+ SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_NOT_SUPPORTED);
+ return;
+ }
+
+ /* Do a pessimistic calculation of the required buffer size */
+ needed_buf_size =
+ FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) +
+ JUNCTION_PREFIX_LEN * sizeof(WCHAR) +
+ 2 * (target_len + 2) * sizeof(WCHAR);
+
+ /* Allocate the buffer */
+ buffer = (REPARSE_DATA_BUFFER*)uv__malloc(needed_buf_size);
+ if (!buffer) {
+ uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+ }
+
+ /* Grab a pointer to the part of the buffer where filenames go */
+ path_buf = (WCHAR*)&(buffer->MountPointReparseBuffer.PathBuffer);
+ path_buf_len = 0;
+
+ /* Copy the substitute (internal) target path */
+ start = path_buf_len;
+
+ wcsncpy((WCHAR*)&path_buf[path_buf_len], JUNCTION_PREFIX,
+ JUNCTION_PREFIX_LEN);
+ path_buf_len += JUNCTION_PREFIX_LEN;
+
+ add_slash = 0;
+ for (i = is_long_path ? LONG_PATH_PREFIX_LEN : 0; path[i] != L'\0'; i++) {
+ if (IS_SLASH(path[i])) {
+ add_slash = 1;
+ continue;
+ }
+
+ if (add_slash) {
+ path_buf[path_buf_len++] = L'\\';
+ add_slash = 0;
+ }
+
+ path_buf[path_buf_len++] = path[i];
+ }
+ path_buf[path_buf_len++] = L'\\';
+ len = path_buf_len - start;
+
+ /* Set the info about the substitute name */
+ buffer->MountPointReparseBuffer.SubstituteNameOffset = start * sizeof(WCHAR);
+ buffer->MountPointReparseBuffer.SubstituteNameLength = len * sizeof(WCHAR);
+
+ /* Insert null terminator */
+ path_buf[path_buf_len++] = L'\0';
+
+ /* Copy the print name of the target path */
+ start = path_buf_len;
+ add_slash = 0;
+ for (i = is_long_path ? LONG_PATH_PREFIX_LEN : 0; path[i] != L'\0'; i++) {
+ if (IS_SLASH(path[i])) {
+ add_slash = 1;
+ continue;
+ }
+
+ if (add_slash) {
+ path_buf[path_buf_len++] = L'\\';
+ add_slash = 0;
+ }
+
+ path_buf[path_buf_len++] = path[i];
+ }
+ len = path_buf_len - start;
+ if (len == 2) {
+ path_buf[path_buf_len++] = L'\\';
+ len++;
+ }
+
+ /* Set the info about the print name */
+ buffer->MountPointReparseBuffer.PrintNameOffset = start * sizeof(WCHAR);
+ buffer->MountPointReparseBuffer.PrintNameLength = len * sizeof(WCHAR);
+
+ /* Insert another null terminator */
+ path_buf[path_buf_len++] = L'\0';
+
+ /* Calculate how much buffer space was actually used */
+ used_buf_size = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) +
+ path_buf_len * sizeof(WCHAR);
+ used_data_size = used_buf_size -
+ FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer);
+
+ /* Put general info in the data buffer */
+ buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
+ buffer->ReparseDataLength = used_data_size;
+ buffer->Reserved = 0;
+
+ /* Create a new directory */
+ if (!CreateDirectoryW(new_path, NULL)) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ goto error;
+ }
+ created = 1;
+
+ /* Open the directory */
+ handle = CreateFileW(new_path,
+ GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS |
+ FILE_FLAG_OPEN_REPARSE_POINT,
+ NULL);
+ if (handle == INVALID_HANDLE_VALUE) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ goto error;
+ }
+
+ /* Create the actual reparse point */
+ if (!DeviceIoControl(handle,
+ FSCTL_SET_REPARSE_POINT,
+ buffer,
+ used_buf_size,
+ NULL,
+ 0,
+ &bytes,
+ NULL)) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ goto error;
+ }
+
+ /* Clean up */
+ CloseHandle(handle);
+ uv__free(buffer);
+
+ SET_REQ_RESULT(req, 0);
+ return;
+
+error:
+ uv__free(buffer);
+
+ if (handle != INVALID_HANDLE_VALUE) {
+ CloseHandle(handle);
+ }
+
+ if (created) {
+ RemoveDirectoryW(new_path);
+ }
+}
+
+
+static void fs__symlink(uv_fs_t* req) {
+ WCHAR* pathw = req->file.pathw;
+ WCHAR* new_pathw = req->fs.info.new_pathw;
+ int flags = req->fs.info.file_flags;
+ int result;
+
+
+ if (flags & UV_FS_SYMLINK_JUNCTION) {
+ fs__create_junction(req, pathw, new_pathw);
+ } else if (pCreateSymbolicLinkW) {
+ result = pCreateSymbolicLinkW(new_pathw,
+ pathw,
+ flags & UV_FS_SYMLINK_DIR ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) ? 0 : -1;
+ if (result == -1) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ } else {
+ SET_REQ_RESULT(req, result);
+ }
+ } else {
+ SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED);
+ }
+}
+
+
+static void fs__readlink(uv_fs_t* req) {
+ HANDLE handle;
+
+ handle = CreateFileW(req->file.pathw,
+ 0,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ return;
+ }
+
+ if (fs__readlink_handle(handle, (char**) &req->ptr, NULL) != 0) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ CloseHandle(handle);
+ return;
+ }
+
+ req->flags |= UV_FS_FREE_PTR;
+ SET_REQ_RESULT(req, 0);
+
+ CloseHandle(handle);
+}
+
+
+static size_t fs__realpath_handle(HANDLE handle, char** realpath_ptr) {
+ int r;
+ DWORD w_realpath_len;
+ WCHAR* w_realpath_ptr = NULL;
+ WCHAR* w_realpath_buf;
+
+ w_realpath_len = pGetFinalPathNameByHandleW(handle, NULL, 0, VOLUME_NAME_DOS);
+ if (w_realpath_len == 0) {
+ return -1;
+ }
+
+ w_realpath_buf = uv__malloc((w_realpath_len + 1) * sizeof(WCHAR));
+ if (w_realpath_buf == NULL) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return -1;
+ }
+ w_realpath_ptr = w_realpath_buf;
+
+ if (pGetFinalPathNameByHandleW(handle,
+ w_realpath_ptr,
+ w_realpath_len,
+ VOLUME_NAME_DOS) == 0) {
+ uv__free(w_realpath_buf);
+ SetLastError(ERROR_INVALID_HANDLE);
+ return -1;
+ }
+
+ /* convert UNC path to long path */
+ if (wcsncmp(w_realpath_ptr,
+ UNC_PATH_PREFIX,
+ UNC_PATH_PREFIX_LEN) == 0) {
+ w_realpath_ptr += 6;
+ *w_realpath_ptr = L'\\';
+ w_realpath_len -= 6;
+ } else if (wcsncmp(w_realpath_ptr,
+ LONG_PATH_PREFIX,
+ LONG_PATH_PREFIX_LEN) == 0) {
+ w_realpath_ptr += 4;
+ w_realpath_len -= 4;
+ } else {
+ uv__free(w_realpath_buf);
+ SetLastError(ERROR_INVALID_HANDLE);
+ return -1;
+ }
+
+ r = fs__wide_to_utf8(w_realpath_ptr, w_realpath_len, realpath_ptr, NULL);
+ uv__free(w_realpath_buf);
+ return r;
+}
+
+static void fs__realpath(uv_fs_t* req) {
+ HANDLE handle;
+
+ if (!pGetFinalPathNameByHandleW) {
+ SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED);
+ return;
+ }
+
+ handle = CreateFileW(req->file.pathw,
+ 0,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (handle == INVALID_HANDLE_VALUE) {
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ return;
+ }
+
+ if (fs__realpath_handle(handle, (char**) &req->ptr) == -1) {
+ CloseHandle(handle);
+ SET_REQ_WIN32_ERROR(req, GetLastError());
+ return;
+ }
+
+ CloseHandle(handle);
+ req->flags |= UV_FS_FREE_PTR;
+ SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__chown(uv_fs_t* req) {
+ req->result = 0;
+}
+
+
+static void fs__fchown(uv_fs_t* req) {
+ req->result = 0;
+}
+
+
+static void uv__fs_work(struct uv__work* w) {
+ uv_fs_t* req;
+
+ req = container_of(w, uv_fs_t, work_req);
+ assert(req->type == UV_FS);
+
+#define XX(uc, lc) case UV_FS_##uc: fs__##lc(req); break;
+ switch (req->fs_type) {
+ XX(OPEN, open)
+ XX(CLOSE, close)
+ XX(READ, read)
+ XX(WRITE, write)
+ XX(SENDFILE, sendfile)
+ XX(STAT, stat)
+ XX(LSTAT, lstat)
+ XX(FSTAT, fstat)
+ XX(FTRUNCATE, ftruncate)
+ XX(UTIME, utime)
+ XX(FUTIME, futime)
+ XX(ACCESS, access)
+ XX(CHMOD, chmod)
+ XX(FCHMOD, fchmod)
+ XX(FSYNC, fsync)
+ XX(FDATASYNC, fdatasync)
+ XX(UNLINK, unlink)
+ XX(RMDIR, rmdir)
+ XX(MKDIR, mkdir)
+ XX(MKDTEMP, mkdtemp)
+ XX(RENAME, rename)
+ XX(SCANDIR, scandir)
+ XX(LINK, link)
+ XX(SYMLINK, symlink)
+ XX(READLINK, readlink)
+ XX(REALPATH, realpath)
+ XX(CHOWN, chown)
+ XX(FCHOWN, fchown);
+ default:
+ assert(!"bad uv_fs_type");
+ }
+}
+
+
+static void uv__fs_done(struct uv__work* w, int status) {
+ uv_fs_t* req;
+
+ req = container_of(w, uv_fs_t, work_req);
+ uv__req_unregister(req->loop, req);
+
+ if (status == UV_ECANCELED) {
+ assert(req->result == 0);
+ req->result = UV_ECANCELED;
+ }
+
+ req->cb(req);
+}
+
+
+void uv_fs_req_cleanup(uv_fs_t* req) {
+ if (req->flags & UV_FS_CLEANEDUP)
+ return;
+
+ if (req->flags & UV_FS_FREE_PATHS)
+ uv__free(req->file.pathw);
+
+ if (req->flags & UV_FS_FREE_PTR) {
+ if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL)
+ uv__fs_scandir_cleanup(req);
+ else
+ uv__free(req->ptr);
+ }
+
+ req->path = NULL;
+ req->file.pathw = NULL;
+ req->fs.info.new_pathw = NULL;
+ req->ptr = NULL;
+
+ req->flags |= UV_FS_CLEANEDUP;
+}
+
+
+int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
+ int mode, uv_fs_cb cb) {
+ int err;
+
+ uv_fs_req_init(loop, req, UV_FS_OPEN, cb);
+
+ err = fs__capture_path(req, path, NULL, cb != NULL);
+ if (err) {
+ return uv_translate_sys_error(err);
+ }
+
+ req->fs.info.file_flags = flags;
+ req->fs.info.mode = mode;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__open(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
+ uv_fs_req_init(loop, req, UV_FS_CLOSE, cb);
+ req->file.fd = fd;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__close(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_read(uv_loop_t* loop,
+ uv_fs_t* req,
+ uv_file fd,
+ const uv_buf_t bufs[],
+ unsigned int nbufs,
+ int64_t offset,
+ uv_fs_cb cb) {
+ if (bufs == NULL || nbufs == 0)
+ return UV_EINVAL;
+
+ uv_fs_req_init(loop, req, UV_FS_READ, cb);
+
+ req->file.fd = fd;
+
+ req->fs.info.nbufs = nbufs;
+ req->fs.info.bufs = req->fs.info.bufsml;
+ if (nbufs > ARRAY_SIZE(req->fs.info.bufsml))
+ req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs));
+
+ if (req->fs.info.bufs == NULL)
+ return UV_ENOMEM;
+
+ memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs));
+
+ req->fs.info.offset = offset;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__read(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_write(uv_loop_t* loop,
+ uv_fs_t* req,
+ uv_file fd,
+ const uv_buf_t bufs[],
+ unsigned int nbufs,
+ int64_t offset,
+ uv_fs_cb cb) {
+ if (bufs == NULL || nbufs == 0)
+ return UV_EINVAL;
+
+ uv_fs_req_init(loop, req, UV_FS_WRITE, cb);
+
+ req->file.fd = fd;
+
+ req->fs.info.nbufs = nbufs;
+ req->fs.info.bufs = req->fs.info.bufsml;
+ if (nbufs > ARRAY_SIZE(req->fs.info.bufsml))
+ req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs));
+
+ if (req->fs.info.bufs == NULL)
+ return UV_ENOMEM;
+
+ memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs));
+
+ req->fs.info.offset = offset;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__write(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
+ uv_fs_cb cb) {
+ int err;
+
+ uv_fs_req_init(loop, req, UV_FS_UNLINK, cb);
+
+ err = fs__capture_path(req, path, NULL, cb != NULL);
+ if (err) {
+ return uv_translate_sys_error(err);
+ }
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__unlink(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
+ uv_fs_cb cb) {
+ int err;
+
+ uv_fs_req_init(loop, req, UV_FS_MKDIR, cb);
+
+ err = fs__capture_path(req, path, NULL, cb != NULL);
+ if (err) {
+ return uv_translate_sys_error(err);
+ }
+
+ req->fs.info.mode = mode;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__mkdir(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl,
+ uv_fs_cb cb) {
+ int err;
+
+ uv_fs_req_init(loop, req, UV_FS_MKDTEMP, cb);
+
+ err = fs__capture_path(req, tpl, NULL, TRUE);
+ if (err)
+ return uv_translate_sys_error(err);
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__mkdtemp(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+ int err;
+
+ uv_fs_req_init(loop, req, UV_FS_RMDIR, cb);
+
+ err = fs__capture_path(req, path, NULL, cb != NULL);
+ if (err) {
+ return uv_translate_sys_error(err);
+ }
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__rmdir(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
+ uv_fs_cb cb) {
+ int err;
+
+ uv_fs_req_init(loop, req, UV_FS_SCANDIR, cb);
+
+ err = fs__capture_path(req, path, NULL, cb != NULL);
+ if (err) {
+ return uv_translate_sys_error(err);
+ }
+
+ req->fs.info.file_flags = flags;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__scandir(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path,
+ const char* new_path, uv_fs_cb cb) {
+ int err;
+
+ uv_fs_req_init(loop, req, UV_FS_LINK, cb);
+
+ err = fs__capture_path(req, path, new_path, cb != NULL);
+ if (err) {
+ return uv_translate_sys_error(err);
+ }
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__link(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
+ const char* new_path, int flags, uv_fs_cb cb) {
+ int err;
+
+ uv_fs_req_init(loop, req, UV_FS_SYMLINK, cb);
+
+ err = fs__capture_path(req, path, new_path, cb != NULL);
+ if (err) {
+ return uv_translate_sys_error(err);
+ }
+
+ req->fs.info.file_flags = flags;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__symlink(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
+ uv_fs_cb cb) {
+ int err;
+
+ uv_fs_req_init(loop, req, UV_FS_READLINK, cb);
+
+ err = fs__capture_path(req, path, NULL, cb != NULL);
+ if (err) {
+ return uv_translate_sys_error(err);
+ }
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__readlink(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path,
+ uv_fs_cb cb) {
+ int err;
+
+ if (!req || !path) {
+ return UV_EINVAL;
+ }
+
+ uv_fs_req_init(loop, req, UV_FS_REALPATH, cb);
+
+ err = fs__capture_path(req, path, NULL, cb != NULL);
+ if (err) {
+ return uv_translate_sys_error(err);
+ }
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__realpath(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid,
+ uv_gid_t gid, uv_fs_cb cb) {
+ int err;
+
+ uv_fs_req_init(loop, req, UV_FS_CHOWN, cb);
+
+ err = fs__capture_path(req, path, NULL, cb != NULL);
+ if (err) {
+ return uv_translate_sys_error(err);
+ }
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__chown(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_uid_t uid,
+ uv_gid_t gid, uv_fs_cb cb) {
+ uv_fs_req_init(loop, req, UV_FS_FCHOWN, cb);
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__fchown(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+ int err;
+
+ uv_fs_req_init(loop, req, UV_FS_STAT, cb);
+
+ err = fs__capture_path(req, path, NULL, cb != NULL);
+ if (err) {
+ return uv_translate_sys_error(err);
+ }
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__stat(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+ int err;
+
+ uv_fs_req_init(loop, req, UV_FS_LSTAT, cb);
+
+ err = fs__capture_path(req, path, NULL, cb != NULL);
+ if (err) {
+ return uv_translate_sys_error(err);
+ }
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__lstat(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
+ uv_fs_req_init(loop, req, UV_FS_FSTAT, cb);
+ req->file.fd = fd;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__fstat(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path,
+ const char* new_path, uv_fs_cb cb) {
+ int err;
+
+ uv_fs_req_init(loop, req, UV_FS_RENAME, cb);
+
+ err = fs__capture_path(req, path, new_path, cb != NULL);
+ if (err) {
+ return uv_translate_sys_error(err);
+ }
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__rename(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
+ uv_fs_req_init(loop, req, UV_FS_FSYNC, cb);
+ req->file.fd = fd;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__fsync(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
+ uv_fs_req_init(loop, req, UV_FS_FDATASYNC, cb);
+ req->file.fd = fd;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__fdatasync(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file fd,
+ int64_t offset, uv_fs_cb cb) {
+ uv_fs_req_init(loop, req, UV_FS_FTRUNCATE, cb);
+
+ req->file.fd = fd;
+ req->fs.info.offset = offset;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__ftruncate(req);
+ return req->result;
+ }
+}
+
+
+
+int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file fd_out,
+ uv_file fd_in, int64_t in_offset, size_t length, uv_fs_cb cb) {
+ uv_fs_req_init(loop, req, UV_FS_SENDFILE, cb);
+
+ req->file.fd = fd_in;
+ req->fs.info.fd_out = fd_out;
+ req->fs.info.offset = in_offset;
+ req->fs.info.bufsml[0].len = length;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__sendfile(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_access(uv_loop_t* loop,
+ uv_fs_t* req,
+ const char* path,
+ int flags,
+ uv_fs_cb cb) {
+ int err;
+
+ uv_fs_req_init(loop, req, UV_FS_ACCESS, cb);
+
+ err = fs__capture_path(req, path, NULL, cb != NULL);
+ if (err)
+ return uv_translate_sys_error(err);
+
+ req->flags = flags;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ }
+
+ fs__access(req);
+ return req->result;
+}
+
+
+int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
+ uv_fs_cb cb) {
+ int err;
+
+ uv_fs_req_init(loop, req, UV_FS_CHMOD, cb);
+
+ err = fs__capture_path(req, path, NULL, cb != NULL);
+ if (err) {
+ return uv_translate_sys_error(err);
+ }
+
+ req->fs.info.mode = mode;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__chmod(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int mode,
+ uv_fs_cb cb) {
+ uv_fs_req_init(loop, req, UV_FS_FCHMOD, cb);
+
+ req->file.fd = fd;
+ req->fs.info.mode = mode;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__fchmod(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime,
+ double mtime, uv_fs_cb cb) {
+ int err;
+
+ uv_fs_req_init(loop, req, UV_FS_UTIME, cb);
+
+ err = fs__capture_path(req, path, NULL, cb != NULL);
+ if (err) {
+ return uv_translate_sys_error(err);
+ }
+
+ req->fs.time.atime = atime;
+ req->fs.time.mtime = mtime;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__utime(req);
+ return req->result;
+ }
+}
+
+
+int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime,
+ double mtime, uv_fs_cb cb) {
+ uv_fs_req_init(loop, req, UV_FS_FUTIME, cb);
+
+ req->file.fd = fd;
+ req->fs.time.atime = atime;
+ req->fs.time.mtime = mtime;
+
+ if (cb) {
+ QUEUE_FS_TP_JOB(loop, req);
+ return 0;
+ } else {
+ fs__futime(req);
+ return req->result;
+ }
+}