diff --git a/common.gypi b/common.gypi index 840d3a7a..f871a473 100644 --- a/common.gypi +++ b/common.gypi @@ -1,5 +1,6 @@ { '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 @@ -87,6 +88,11 @@ 'DataExecutionPrevention': 2, # enable DEP 'AllowIsolation': 'true', 'SuppressStartupBanner': 'true', + 'target_conditions': [ + ['_type=="executable"', { + 'SubSystem': 1, # console executable + }], + ], }, }, 'conditions': [ @@ -103,61 +109,55 @@ ], }], [ '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']}, + }], + ], }], ], }, diff --git a/src/win/fs.c b/src/win/fs.c index 29b703e9..770b4df1 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -430,15 +430,93 @@ void fs__symlink(uv_fs_t* req, const char* path, const char* new_path, void fs__readlink(uv_fs_t* req, const char* path) { int result = -1; - assert(0 && "implement me"); + BOOL rv; + HANDLE symlink; + void* buffer; + DWORD bytes_returned; + REPARSE_DATA_BUFFER* reparse_data; + int utf8size; - /* TODO: the link path must be returned in a req->ptr buffer, - * which need to be alloce'd here. - * Just do this (it'll take care of freeing the buffer). - * req->ptr = malloc(...); - * req->flags |= UV_FS_FREE_PTR; - * Also result needs to contain the length of the string. - */ + 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); } diff --git a/src/win/winapi.h b/src/win/winapi.h index 859dd562..9dc0acb2 100644 --- a/src/win/winapi.h +++ b/src/win/winapi.h @@ -4075,6 +4075,33 @@ (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; diff --git a/test/test-fs.c b/test/test-fs.c index 9a57174f..e1738819 100644 --- a/test/test-fs.c +++ b/test/test-fs.c @@ -65,6 +65,7 @@ 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; @@ -127,6 +128,13 @@ static void symlink_cb(uv_fs_t* req) { 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); @@ -936,6 +944,8 @@ TEST_IMPL(fs_symlink) { unlink("test_file"); unlink("test_file_symlink"); unlink("test_file_symlink2"); + unlink("test_file_symlink_symlink"); + unlink("test_file_symlink2_symlink"); uv_init(); @@ -992,6 +1002,13 @@ TEST_IMPL(fs_symlink) { 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); @@ -1012,6 +1029,13 @@ TEST_IMPL(fs_symlink) { 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. @@ -1021,7 +1045,9 @@ TEST_IMPL(fs_symlink) { /* 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