{
'variables': {
+ 'visibility%': 'hidden', # V8's visibility setting
'target_arch%': 'ia32', # set v8's target architecture
'host_arch%': 'ia32', # set v8's host architecture
'library%': 'static_library', # allow override to 'shared_library' for DLL/.so builds
'DataExecutionPrevention': 2, # enable DEP
'AllowIsolation': 'true',
'SuppressStartupBanner': 'true',
+ 'target_conditions': [
+ ['_type=="executable"', {
+ 'SubSystem': 1, # console executable
+ }],
+ ],
},
},
'conditions': [
],
}],
[ 'OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris"', {
- 'target_defaults': {
- 'cflags': [ '-Wall', '-pthread', '-fno-rtti', '-fno-exceptions' ],
- 'ldflags': [ '-pthread', ],
- 'conditions': [
- [ 'target_arch=="ia32"', {
- 'cflags': [ '-m32' ],
- 'ldflags': [ '-m32' ],
- }],
- [ 'OS=="linux"', {
- 'cflags': [ '-ansi' ],
- }],
- [ 'visibility=="hidden"', {
- 'cflags': [ '-fvisibility=hidden' ],
- }],
- ],
- },
+ 'cflags': [ '-Wall', '-pthread', ],
+ 'cflags_cc': [ '-fno-rtti', '-fno-exceptions' ],
+ 'ldflags': [ '-pthread', ],
+ 'conditions': [
+ [ 'target_arch=="ia32"', {
+ 'cflags': [ '-m32' ],
+ 'ldflags': [ '-m32' ],
+ }],
+ [ 'OS=="linux"', {
+ 'cflags': [ '-ansi' ],
+ }],
+ [ 'visibility=="hidden"', {
+ 'cflags': [ '-fvisibility=hidden' ],
+ }],
+ ],
}],
['OS=="mac"', {
- 'target_defaults': {
- 'xcode_settings': {
- 'ALWAYS_SEARCH_USER_PATHS': 'NO',
- 'GCC_C_LANGUAGE_STANDARD': 'ansi', # -ansi
- 'GCC_CW_ASM_SYNTAX': 'NO', # No -fasm-blocks
- 'GCC_DYNAMIC_NO_PIC': 'NO', # No -mdynamic-no-pic
- # (Equivalent to -fPIC)
- 'GCC_ENABLE_CPP_EXCEPTIONS': 'NO', # -fno-exceptions
- 'GCC_ENABLE_CPP_RTTI': 'NO', # -fno-rtti
- 'GCC_ENABLE_PASCAL_STRINGS': 'NO', # No -mpascal-strings
- # GCC_INLINES_ARE_PRIVATE_EXTERN maps to -fvisibility-inlines-hidden
- 'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES',
- 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden
- 'GCC_THREADSAFE_STATICS': 'NO', # -fno-threadsafe-statics
- 'GCC_TREAT_WARNINGS_AS_ERRORS': 'YES', # -Werror
- 'GCC_VERSION': '4.2',
- 'GCC_WARN_ABOUT_MISSING_NEWLINE': 'YES', # -Wnewline-eof
- 'MACOSX_DEPLOYMENT_TARGET': '10.4', # -mmacosx-version-min=10.4
- 'PREBINDING': 'NO', # No -Wl,-prebind
- 'USE_HEADERMAP': 'NO',
- 'OTHER_CFLAGS': [
- '-fno-strict-aliasing',
- ],
- 'WARNING_CFLAGS': [
- '-Wall',
- '-Wendif-labels',
- '-W',
- '-Wno-unused-parameter',
- '-Wnon-virtual-dtor',
- ],
- },
- 'target_conditions': [
- ['_type!="static_library"', {
- 'xcode_settings': {'OTHER_LDFLAGS': ['-Wl,-search_paths_first']},
- }],
+ 'xcode_settings': {
+ 'ALWAYS_SEARCH_USER_PATHS': 'NO',
+ 'GCC_CW_ASM_SYNTAX': 'NO', # No -fasm-blocks
+ 'GCC_DYNAMIC_NO_PIC': 'NO', # No -mdynamic-no-pic
+ # (Equivalent to -fPIC)
+ 'GCC_ENABLE_CPP_EXCEPTIONS': 'NO', # -fno-exceptions
+ 'GCC_ENABLE_CPP_RTTI': 'NO', # -fno-rtti
+ 'GCC_ENABLE_PASCAL_STRINGS': 'NO', # No -mpascal-strings
+ # GCC_INLINES_ARE_PRIVATE_EXTERN maps to -fvisibility-inlines-hidden
+ 'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES',
+ 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden
+ 'GCC_THREADSAFE_STATICS': 'NO', # -fno-threadsafe-statics
+ 'GCC_VERSION': '4.2',
+ 'GCC_WARN_ABOUT_MISSING_NEWLINE': 'YES', # -Wnewline-eof
+ 'MACOSX_DEPLOYMENT_TARGET': '10.4', # -mmacosx-version-min=10.4
+ 'PREBINDING': 'NO', # No -Wl,-prebind
+ 'USE_HEADERMAP': 'NO',
+ 'OTHER_CFLAGS': [
+ '-fno-strict-aliasing',
+ ],
+ 'WARNING_CFLAGS': [
+ '-Wall',
+ '-Wendif-labels',
+ '-W',
+ '-Wno-unused-parameter',
],
},
+ 'target_conditions': [
+ ['_type!="static_library"', {
+ 'xcode_settings': {'OTHER_LDFLAGS': ['-Wl,-search_paths_first']},
+ }],
+ ],
}],
],
},
#define UV_FS_PRIVATE_FIELDS \
int flags; \
+ int last_error; \
struct _stat stat; \
void* arg0; \
union { \
int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path,
const char* new_path, uv_fs_cb cb);
+/*
+ * This flag can be used with uv_fs_symlink on Windows
+ * to specify whether path argument points to a directory.
+ */
+#define UV_FS_SYMLINK_DIR 0x0001
+
int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
- const char* new_path, uv_fs_cb cb);
+ const char* new_path, int flags, uv_fs_cb cb);
int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
uv_fs_cb cb);
void uv__req_init(uv_req_t* req) {
/* loop->counters.req_init++; */
req->type = UV_UNKNOWN_REQ;
- req->data = NULL;
}
#include <CoreServices/CoreServices.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
+#include <mach-o/dyld.h> /* _NSGetExecutablePath */
uint64_t uv_hrtime() {
case UV_FS_READDIR:
/*
* XXX This is pretty bad.
- * We alloc and copy the large null termiated string list from libeio.
+ * We alloc and copy the large null terminated string list from libeio.
* This is done because libeio is going to free eio->ptr2 after this
* callback. We must keep it until uv_fs_req_cleanup. If we get rid of
* libeio this can be avoided.
req->ptr = malloc(buflen);
memcpy(req->ptr, req->eio->ptr2, buflen);
break;
+
case UV_FS_STAT:
case UV_FS_LSTAT:
case UV_FS_FSTAT:
req->ptr = req->eio->ptr2;
break;
+ case UV_FS_READLINK:
+ if (req->result == -1) {
+ req->ptr = NULL;
+ } else {
+ assert(req->result > 0);
+
+ if ((name = realloc(req->eio->ptr2, req->result + 1)) == NULL) {
+ /* Not enough memory. Reuse buffer, chop off last byte. */
+ name = req->eio->ptr2;
+ req->result--;
+ }
+
+ name[req->result] = '\0';
+ req->ptr = name;
+ req->result = 0;
+ }
+ break;
+
default:
break;
}
int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
- const char* new_path, uv_fs_cb cb) {
+ const char* new_path, int flags, uv_fs_cb cb) {
WRAP_EIO(UV_FS_SYMLINK, eio_symlink, symlink, ARGS2(path, new_path))
}
int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
uv_fs_cb cb) {
- assert(0 && "implement me");
- return -1;
+ size_t size;
+ int status;
+ char* buf;
+
+ status = -1;
+
+ uv_fs_req_init(loop, req, UV_FS_READLINK, cb);
+
+ if (cb) {
+ if ((req->eio = eio_readlink(path, EIO_PRI_DEFAULT, uv__fs_after, req))) {
+ uv_ref(loop);
+ return 0;
+ } else {
+ uv_err_new(loop, ENOMEM);
+ return -1;
+ }
+ } else {
+ /* pathconf(_PC_PATH_MAX) may return -1 to signify that path
+ * lengths have no upper limit or aren't suitable for malloc'ing.
+ */
+ if ((size = pathconf(path, _PC_PATH_MAX)) == -1) {
+#if defined(PATH_MAX)
+ size = PATH_MAX;
+#else
+ size = 4096;
+#endif
+ }
+
+ if ((buf = malloc(size + 1)) == NULL) {
+ uv_err_new(loop, ENOMEM);
+ return -1;
+ }
+
+ if ((size = readlink(path, buf, size)) == -1) {
+ req->errorno = errno;
+ req->result = -1;
+ free(buf);
+ } else {
+ /* Cannot conceivably fail since it shrinks the buffer. */
+ buf = realloc(buf, size + 1);
+ buf[size] = '\0';
+ req->result = 0;
+ req->ptr = buf;
+ }
+
+ return 0;
+ }
+
+ assert(0 && "unreachable");
}
#include <assert.h>
#include <malloc.h>
#include <direct.h>
+#include <errno.h>
#include <fcntl.h>
#include <io.h>
#include <sys/stat.h>
#define UV_FS_FREE_ARG1 0x0004
#define UV_FS_FREE_PTR 0x0008
#define UV_FS_CLEANEDUP 0x0010
+#define UV_FS_LAST_ERROR_SET 0x0020
#define STRDUP_ARG(req, i) \
req->arg##i = (void*)strdup((const char*)req->arg##i); \
uv_ref((loop));
+#define SET_UV_LAST_ERROR_FROM_REQ(req) \
+ if (req->flags & UV_FS_LAST_ERROR_SET) { \
+ uv_set_sys_error(req->loop, req->last_error); \
+ }
+
+#define SET_REQ_LAST_ERROR(req, error) \
+ req->last_error = error; \
+ req->flags |= UV_FS_LAST_ERROR_SET;
+
+
void uv_fs_init() {
_fmode = _O_BINARY;
}
req->result = 0;
req->ptr = NULL;
req->errorno = 0;
+ req->last_error = 0;
memset(&req->overlapped, 0, sizeof(req->overlapped));
}
if(dir == INVALID_HANDLE_VALUE) {
result = -1;
+ SET_REQ_LAST_ERROR(req, GetLastError());
goto done;
}
void fs__fsync(uv_fs_t* req, uv_file file) {
int result = FlushFileBuffers((HANDLE)_get_osfhandle(file)) ? 0 : -1;
+ if (result == -1) {
+ SET_REQ_LAST_ERROR(req, GetLastError());
+ }
SET_REQ_RESULT(req, result);
}
}
+void fs__link(uv_fs_t* req, const char* path, const char* new_path) {
+ int result = CreateHardLinkA(new_path, path, NULL) ? 0 : -1;
+ if (result == -1) {
+ SET_REQ_LAST_ERROR(req, GetLastError());
+ }
+ SET_REQ_RESULT(req, result);
+}
+
+
+void fs__symlink(uv_fs_t* req, const char* path, const char* new_path,
+ int flags) {
+ int result;
+ if (pCreateSymbolicLinkA) {
+ result = pCreateSymbolicLinkA(new_path,
+ path,
+ flags & UV_FS_SYMLINK_DIR ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) ? 0 : -1;
+ if (result == -1) {
+ SET_REQ_LAST_ERROR(req, GetLastError());
+ }
+ } else {
+ result = -1;
+ errno = ENOTSUP;
+ }
+
+ SET_REQ_RESULT(req, result);
+}
+
+
+void fs__readlink(uv_fs_t* req, const char* path) {
+ int result = -1;
+ BOOL rv;
+ HANDLE symlink;
+ void* buffer;
+ DWORD bytes_returned;
+ REPARSE_DATA_BUFFER* reparse_data;
+ int utf8size;
+
+ symlink = CreateFileA(path,
+ 0,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+
+ if (INVALID_HANDLE_VALUE == symlink) {
+ result = -1;
+ SET_REQ_LAST_ERROR(req, GetLastError());
+ goto done;
+ }
+
+ buffer = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+ if (!buffer) {
+ uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
+ }
+
+ rv = DeviceIoControl(symlink,
+ FSCTL_GET_REPARSE_POINT,
+ NULL,
+ 0,
+ buffer,
+ MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
+ &bytes_returned,
+ NULL);
+
+ if (!rv) {
+ result = -1;
+ SET_REQ_LAST_ERROR(req, GetLastError());
+ goto done;
+ }
+
+ reparse_data = buffer;
+ if (reparse_data->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
+ result = -1;
+ /* something is seriously wrong */
+ SET_REQ_LAST_ERROR(req, GetLastError());
+ goto done;
+ }
+
+ utf8size = uv_utf16_to_utf8(reparse_data->SymbolicLinkReparseBuffer.PathBuffer + (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)),
+ reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t),
+ NULL,
+ 0);
+ if (!utf8size) {
+ result = -1;
+ SET_REQ_LAST_ERROR(req, GetLastError());
+ goto done;
+ }
+
+ req->ptr = malloc(utf8size + 1);
+ if (!req->ptr) {
+ uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
+ }
+
+ req->flags |= UV_FS_FREE_PTR;
+
+ utf8size = uv_utf16_to_utf8(reparse_data->SymbolicLinkReparseBuffer.PathBuffer + (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)),
+ reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t),
+ req->ptr,
+ utf8size);
+ if (!utf8size) {
+ result = -1;
+ SET_REQ_LAST_ERROR(req, GetLastError());
+ goto done;
+ }
+
+ ((char*)req->ptr)[utf8size] = '\0';
+ result = 0;
+
+done:
+ if (buffer) {
+ free(buffer);
+ }
+
+ if (symlink != INVALID_HANDLE_VALUE) {
+ CloseHandle(symlink);
+ }
+
+ SET_REQ_RESULT(req, result);
+}
+
+
void fs__nop(uv_fs_t* req) {
req->result = 0;
}
case UV_FS_FUTIME:
fs__futime(req, (uv_file)req->arg0, req->arg4, req->arg5);
break;
+ case UV_FS_LINK:
+ fs__link(req, (const char*)req->arg0, (const char*)req->arg1);
+ break;
+ case UV_FS_SYMLINK:
+ fs__symlink(req, (const char*)req->arg0, (const char*)req->arg1, (int)req->arg2);
+ break;
+ case UV_FS_READLINK:
+ fs__readlink(req, (const char*)req->arg0);
+ break;
case UV_FS_CHOWN:
case UV_FS_FCHOWN:
fs__nop(req);
} else {
uv_fs_req_init_sync(loop, req, UV_FS_OPEN);
fs__open(req, path, flags, mode);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_CLOSE);
fs__close(req, file);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_READ);
fs__read(req, file, buf, length, offset);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_WRITE);
fs__write(req, file, buf, length, offset);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_UNLINK);
fs__unlink(req, path);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_MKDIR);
fs__mkdir(req, path, mode);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_RMDIR);
fs__rmdir(req, path);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_READDIR);
fs__readdir(req, path, flags);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
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");
- return -1;
+ if (cb) {
+ uv_fs_req_init_async(loop, req, UV_FS_LINK, cb);
+ WRAP_REQ_ARGS2(req, path, new_path);
+ STRDUP_ARG(req, 0);
+ STRDUP_ARG(req, 1);
+ QUEUE_FS_TP_JOB(loop, req);
+ } else {
+ uv_fs_req_init_sync(loop, req, UV_FS_LINK);
+ fs__link(req, path, new_path);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
+ }
+
+ return 0;
}
int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
- const char* new_path, uv_fs_cb cb) {
- assert(0 && "implement me");
- return -1;
+ const char* new_path, int flags, uv_fs_cb cb) {
+ if (cb) {
+ uv_fs_req_init_async(loop, req, UV_FS_SYMLINK, cb);
+ WRAP_REQ_ARGS3(req, path, new_path, flags);
+ STRDUP_ARG(req, 0);
+ STRDUP_ARG(req, 1);
+ QUEUE_FS_TP_JOB(loop, req);
+ } else {
+ uv_fs_req_init_sync(loop, req, UV_FS_SYMLINK);
+ fs__symlink(req, path, new_path, flags);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
+ }
+
+ return 0;
}
int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
uv_fs_cb cb) {
- assert(0 && "implement me");
- return -1;
+ if (cb) {
+ uv_fs_req_init_async(loop, req, UV_FS_READLINK, cb);
+ 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_READLINK);
+ fs__readlink(req, path);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
+ }
+
+ return 0;
}
} else {
uv_fs_req_init_sync(loop, req, UV_FS_CHOWN);
fs__nop(req);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_FCHOWN);
fs__nop(req);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
if (path2) {
free(path2);
}
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
if (path2) {
free(path2);
}
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_FSTAT);
fs__fstat(req, file);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_RENAME);
fs__rename(req, path, new_path);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_FDATASYNC);
fs__fsync(req, file);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_FSYNC);
fs__fsync(req, file);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_FTRUNCATE);
fs__ftruncate(req, file, offset);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_SENDFILE);
fs__sendfile(req, out_fd, in_fd, in_offset, length);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_CHMOD);
fs__chmod(req, path, mode);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_FCHMOD);
fs__fchmod(req, file, mode);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_UTIME);
fs__utime(req, path, atime, mtime);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
} else {
uv_fs_req_init_sync(loop, req, UV_FS_FUTIME);
fs__futime(req, file, atime, mtime);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
}
return 0;
void uv_process_fs_req(uv_loop_t* loop, uv_fs_t* req) {
assert(req->cb);
+ SET_UV_LAST_ERROR_FROM_REQ(req);
req->cb(req);
}
sNtSetInformationFile pNtSetInformationFile;
sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes;
+sCreateSymbolicLinkA pCreateSymbolicLinkA;
void uv_winapi_init() {
pSetFileCompletionNotificationModes = (sSetFileCompletionNotificationModes)
GetProcAddress(kernel32_module, "SetFileCompletionNotificationModes");
+
+ pCreateSymbolicLinkA = (sCreateSymbolicLinkA)
+ GetProcAddress(kernel32_module, "CreateSymbolicLinkA");
}
(FACILITY_NTWIN32 << 16) | ERROR_SEVERITY_ERROR)))
#endif
+/* from ntifs.h */
+typedef struct _REPARSE_DATA_BUFFER {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union {
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ } DUMMYUNIONNAME;
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
#define FILE_SKIP_COMPLETION_PORT_ON_SUCCESS 0x1
#define FILE_SKIP_SET_EVENT_ON_HANDLE 0x2
+#define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
+
#ifdef __MINGW32__
typedef struct _OVERLAPPED_ENTRY {
ULONG_PTR lpCompletionKey;
(HANDLE FileHandle,
UCHAR Flags);
+typedef BOOLEAN (WINAPI* sCreateSymbolicLinkA)
+ (LPCSTR lpSymlinkFileName,
+ LPCSTR lpTargetFileName,
+ DWORD dwFlags);
+
/* Ntapi function pointers */
extern sRtlNtStatusToDosError pRtlNtStatusToDosError;
/* Kernel32 function pointers */
extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
extern sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes;
+extern sCreateSymbolicLinkA pCreateSymbolicLinkA;
#endif /* UV_WIN_WINAPI_H_ */
#include "uv.h"
#include "task.h"
+#include <errno.h>
#include <string.h> /* memset */
#include <fcntl.h>
#include <sys/stat.h>
static int fchmod_cb_count;
static int chown_cb_count;
static int fchown_cb_count;
+static int link_cb_count;
+static int symlink_cb_count;
+static int readlink_cb_count;
static uv_loop_t* loop;
}
+static void link_cb(uv_fs_t* req) {
+ ASSERT(req->fs_type == UV_FS_LINK);
+ ASSERT(req->result == 0);
+ link_cb_count++;
+ uv_fs_req_cleanup(req);
+}
+
+
+static void symlink_cb(uv_fs_t* req) {
+ ASSERT(req->fs_type == UV_FS_SYMLINK);
+ ASSERT(req->result == 0);
+ symlink_cb_count++;
+ uv_fs_req_cleanup(req);
+}
+
+static void readlink_cb(uv_fs_t* req) {
+ ASSERT(req->fs_type == UV_FS_READLINK);
+ ASSERT(req->result == 0);
+ ASSERT(strcmp(req->ptr, "test_file_symlink2") == 0);
+ readlink_cb_count++;
+ uv_fs_req_cleanup(req);
+}
+
static void fchmod_cb(uv_fs_t* req) {
ASSERT(req->fs_type == UV_FS_FCHMOD);
ASSERT(req->result == 0);
ASSERT(req->fs_type == UV_FS_READDIR);
ASSERT(req->result == 2);
ASSERT(req->ptr);
- ASSERT(strcmp((const char*)req->ptr, "file1") == 0);
- ASSERT(strcmp((char*)req->ptr + strlen((const char*)req->ptr) + 1,
- "file2") == 0);
+ ASSERT(memcmp(req->ptr, "file1\0file2\0", 12) == 0
+ || memcmp(req->ptr, "file2\0file1\0", 12) == 0);
readdir_cb_count++;
uv_fs_req_cleanup(req);
ASSERT(!req->ptr);
ASSERT(r == 0);
uv_run(loop);
ASSERT(chmod_cb_count == 1);
+ chmod_cb_count = 0; /* reset for the next test */
#endif
/* async chmod */
unlink("test_file");
return 0;
+}
+
+
+TEST_IMPL(fs_link) {
+ int r;
+ uv_fs_t req;
+ uv_file file;
+ uv_file link;
+
+ /* Setup. */
+ unlink("test_file");
+ unlink("test_file_link");
+ unlink("test_file_link2");
+
+ 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);
+
+ r = uv_fs_write(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL);
+ ASSERT(r == 0);
+ ASSERT(req.result == sizeof(test_buf));
+ uv_fs_req_cleanup(&req);
+
+ close(file);
+
+ /* sync link */
+ r = uv_fs_link(loop, &req, "test_file", "test_file_link", NULL);
+ ASSERT(r == 0);
+ ASSERT(req.result == 0);
+ uv_fs_req_cleanup(&req);
+
+ r = uv_fs_open(loop, &req, "test_file_link", O_RDWR, 0, NULL);
+ ASSERT(r == 0);
+ ASSERT(req.result != -1);
+ link = req.result;
+ uv_fs_req_cleanup(&req);
+
+ memset(buf, 0, sizeof(buf));
+ r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL);
+ ASSERT(r == 0);
+ ASSERT(req.result != -1);
+ ASSERT(strcmp(buf, test_buf) == 0);
+
+ close(link);
+
+ /* async link */
+ r = uv_fs_link(loop, &req, "test_file", "test_file_link2", link_cb);
+ ASSERT(r == 0);
+ uv_run(loop);
+ ASSERT(link_cb_count == 1);
+
+ r = uv_fs_open(loop, &req, "test_file_link2", O_RDWR, 0, NULL);
+ ASSERT(r == 0);
+ ASSERT(req.result != -1);
+ link = req.result;
+ uv_fs_req_cleanup(&req);
+
+ memset(buf, 0, sizeof(buf));
+ r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL);
+ ASSERT(r == 0);
+ ASSERT(req.result != -1);
+ ASSERT(strcmp(buf, test_buf) == 0);
+
+ close(link);
+
+ /*
+ * 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");
+ unlink("test_file_link");
+ unlink("test_file_link2");
+
+ return 0;
+}
+
+
+TEST_IMPL(fs_symlink) {
+ int r;
+ uv_fs_t req;
+ uv_file file;
+ uv_file link;
+
+ /* Setup. */
+ unlink("test_file");
+ unlink("test_file_symlink");
+ unlink("test_file_symlink2");
+ unlink("test_file_symlink_symlink");
+ unlink("test_file_symlink2_symlink");
+
+ 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);
+
+ r = uv_fs_write(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL);
+ ASSERT(r == 0);
+ ASSERT(req.result == sizeof(test_buf));
+ uv_fs_req_cleanup(&req);
+
+ close(file);
+
+ /* sync symlink */
+ r = uv_fs_symlink(loop, &req, "test_file", "test_file_symlink", 0, NULL);
+ ASSERT(r == 0);
+#ifdef _WIN32
+ if (req.result == -1) {
+ if (req.errorno == ENOTSUP) {
+ /*
+ * Windows doesn't support symlinks on older versions.
+ * We just pass the test and bail out early if we get ENOTSUP.
+ */
+ return 0;
+ } else if (uv_last_error(loop).sys_errno_ == ERROR_PRIVILEGE_NOT_HELD) {
+ /*
+ * Creating a symlink is only allowed when running elevated.
+ * We pass the test and bail out early if we get ERROR_PRIVILEGE_NOT_HELD.
+ */
+ return 0;
+ }
+ }
+#endif
+ ASSERT(req.result == 0);
+ uv_fs_req_cleanup(&req);
+
+ r = uv_fs_open(loop, &req, "test_file_symlink", O_RDWR, 0, NULL);
+ ASSERT(r == 0);
+ ASSERT(req.result != -1);
+ link = req.result;
+ uv_fs_req_cleanup(&req);
+
+ memset(buf, 0, sizeof(buf));
+ r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL);
+ ASSERT(r == 0);
+ ASSERT(req.result != -1);
+ ASSERT(strcmp(buf, test_buf) == 0);
+
+ close(link);
+
+ r = uv_fs_symlink(loop, &req, "test_file_symlink", "test_file_symlink_symlink", 0, NULL);
+ ASSERT(r == 0);
+ r = uv_fs_readlink(loop, &req, "test_file_symlink_symlink", NULL);
+ ASSERT(r == 0);
+ ASSERT(strcmp(req.ptr, "test_file_symlink") == 0);
+ uv_fs_req_cleanup(&req);
+
+ /* async link */
+ r = uv_fs_symlink(loop, &req, "test_file", "test_file_symlink2", 0, symlink_cb);
+ ASSERT(r == 0);
+ uv_run(loop);
+ ASSERT(symlink_cb_count == 1);
+
+ r = uv_fs_open(loop, &req, "test_file_symlink2", O_RDWR, 0, NULL);
+ ASSERT(r == 0);
+ ASSERT(req.result != -1);
+ link = req.result;
+ uv_fs_req_cleanup(&req);
+
+ memset(buf, 0, sizeof(buf));
+ r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL);
+ ASSERT(r == 0);
+ ASSERT(req.result != -1);
+ ASSERT(strcmp(buf, test_buf) == 0);
+
+ close(link);
+
+ r = uv_fs_symlink(loop, &req, "test_file_symlink2", "test_file_symlink2_symlink", 0, NULL);
+ ASSERT(r == 0);
+ r = uv_fs_readlink(loop, &req, "test_file_symlink2_symlink", readlink_cb);
+ ASSERT(r == 0);
+ uv_run(loop);
+ ASSERT(readlink_cb_count == 1);
+
+ /*
+ * 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");
+ unlink("test_file_symlink");
+ unlink("test_file_symlink_symlink");
+ unlink("test_file_symlink2");
+ unlink("test_file_symlink2_symlink");
+
+ return 0;
}
\ No newline at end of file
TEST_DECLARE (fs_fstat)
TEST_DECLARE (fs_chmod)
TEST_DECLARE (fs_chown)
+TEST_DECLARE (fs_link)
+TEST_DECLARE (fs_symlink)
TEST_DECLARE (threadpool_queue_work_simple)
#ifdef _WIN32
TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows)
TEST_ENTRY (fs_fstat)
TEST_ENTRY (fs_chmod)
TEST_ENTRY (fs_chown)
+ TEST_ENTRY (fs_link)
+ TEST_ENTRY (fs_symlink)
TEST_ENTRY (threadpool_queue_work_simple)
String::Utf8Value dest(args[0]->ToString());
String::Utf8Value path(args[1]->ToString());
+ // Just set to zero for now. Support UV_FS_SYMLINK_DIR in the future.
+ int flags = 0;
+
if (args[2]->IsFunction()) {
- ASYNC_CALL(symlink, args[2], *dest, *path)
+ ASYNC_CALL(symlink, args[2], *dest, *path, flags)
} else {
- SYNC_CALL(symlink, *path, *dest, *path)
+ SYNC_CALL(symlink, *path, *dest, *path, flags)
return Undefined();
}
}