diff --git a/common.gypi b/common.gypi index c2df528f2..79306a755 100644 --- a/common.gypi +++ b/common.gypi @@ -32,6 +32,9 @@ 'LinkIncremental': 2, # enable incremental linking }, }, + 'xcode_settings': { + 'GCC_OPTIMIZATION_LEVEL': '0', + }, 'conditions': [ ['OS != "win"', { 'defines': [ 'EV_VERIFY=2' ], diff --git a/include/uv.h b/include/uv.h index f6fc52fee..bf95fdef7 100644 --- a/include/uv.h +++ b/include/uv.h @@ -126,7 +126,8 @@ extern "C" { XX( 53, ENOTEMPTY, "directory not empty") \ XX( 54, ENOSPC, "no space left on device") \ XX( 55, EIO, "i/o error") \ - XX( 56, EROFS, "read-only file system" ) + XX( 56, EROFS, "read-only file system" ) \ + XX( 57, ENODEV, "no such device" ) #define UV_ERRNO_GEN(val, name, s) UV_##name = val, diff --git a/src/unix/core.c b/src/unix/core.c index df5738b0b..2f8815966 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -451,6 +451,11 @@ int uv__accept(int sockfd) { while (1) { #if __linux__ + static int no_accept4; + + if (no_accept4) + goto skip; + peerfd = uv__accept4(sockfd, NULL, NULL, @@ -464,6 +469,9 @@ int uv__accept(int sockfd) { if (errno != ENOSYS) break; + + no_accept4 = 1; +skip: #endif peerfd = accept(sockfd, NULL, NULL); diff --git a/src/unix/dl.c b/src/unix/dl.c index 01796e3b8..9cc830b81 100644 --- a/src/unix/dl.c +++ b/src/unix/dl.c @@ -34,7 +34,7 @@ int uv_dlopen(const char* filename, uv_lib_t* lib) { dlerror(); /* Reset error status. */ lib->errmsg = NULL; lib->handle = dlopen(filename, RTLD_LAZY); - return uv__dlerror(lib); + return lib->handle ? 0 : uv__dlerror(lib); } diff --git a/src/unix/error.c b/src/unix/error.c index c2651a3a9..9fbb312ee 100644 --- a/src/unix/error.c +++ b/src/unix/error.c @@ -86,6 +86,7 @@ uv_err_code uv_translate_sys_error(int sys_errno) { case EADDRNOTAVAIL: return UV_EADDRNOTAVAIL; case ENOTDIR: return UV_ENOTDIR; case EISDIR: return UV_EISDIR; + case ENODEV: return UV_ENODEV; case ENOTCONN: return UV_ENOTCONN; case EEXIST: return UV_EEXIST; case EHOSTUNREACH: return UV_EHOSTUNREACH; diff --git a/src/unix/linux/syscalls.c b/src/unix/linux/syscalls.c index 793cb6c4c..a99a6da88 100644 --- a/src/unix/linux/syscalls.c +++ b/src/unix/linux/syscalls.c @@ -202,13 +202,25 @@ int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags) { #if __i386__ - unsigned long args[] = { - (unsigned long) fd, - (unsigned long) addr, - (unsigned long) addrlen, - (unsigned long) flags - }; - return syscall(__NR_socketcall, 18 /* SYS_ACCEPT4 */, args); + unsigned long args[4]; + int r; + + args[0] = (unsigned long) fd; + args[1] = (unsigned long) addr; + args[2] = (unsigned long) addrlen; + args[3] = (unsigned long) flags; + + r = syscall(__NR_socketcall, 18 /* SYS_ACCEPT4 */, args); + + /* socketcall() raises EINVAL when SYS_ACCEPT4 is not supported but so does + * a bad flags argument. Try to distinguish between the two cases. + */ + if (r == -1) + if (errno == EINVAL) + if ((flags & ~(UV__SOCK_CLOEXEC|UV__SOCK_NONBLOCK)) == 0) + errno = ENOSYS; + + return r; #elif __NR_accept4 return syscall(__NR_accept4, fd, addr, addrlen, flags); #else diff --git a/src/unix/stream.c b/src/unix/stream.c index 89790eabf..8c33ee09c 100644 --- a/src/unix/stream.c +++ b/src/unix/stream.c @@ -802,62 +802,51 @@ int uv__connect(uv_connect_t* req, uv_stream_t* stream, struct sockaddr* addr, int sockfd; int r; + if (stream->type != UV_TCP) + return uv__set_sys_error(stream->loop, ENOTSOCK); + + if (stream->connect_req) + return uv__set_sys_error(stream->loop, EALREADY); + if (stream->fd <= 0) { - if ((sockfd = uv__socket(addr->sa_family, SOCK_STREAM, 0)) == -1) { - uv__set_sys_error(stream->loop, errno); - return -1; - } + sockfd = uv__socket(addr->sa_family, SOCK_STREAM, 0); + + if (sockfd == -1) + return uv__set_sys_error(stream->loop, errno); if (uv__stream_open(stream, sockfd, UV_STREAM_READABLE | UV_STREAM_WRITABLE)) { close(sockfd); - return -2; + return -1; } } + stream->delayed_error = 0; + + do + r = connect(stream->fd, addr, addrlen); + while (r == -1 && errno == EINTR); + + if (r == -1) { + if (errno == EINPROGRESS) + ; /* not an error */ + else if (errno == ECONNREFUSED) + /* If we get a ECONNREFUSED wait until the next tick to report the + * error. Solaris wants to report immediately--other unixes want to + * wait. + */ + stream->delayed_error = errno; + else + return uv__set_sys_error(stream->loop, errno); + } + uv__req_init(stream->loop, req, UV_CONNECT); req->cb = cb; req->handle = stream; ngx_queue_init(&req->queue); - - if (stream->connect_req) { - uv__set_sys_error(stream->loop, EALREADY); - return -1; - } - - if (stream->type != UV_TCP) { - uv__set_sys_error(stream->loop, ENOTSOCK); - return -1; - } - stream->connect_req = req; - do { - r = connect(stream->fd, addr, addrlen); - } - while (r == -1 && errno == EINTR); - - stream->delayed_error = 0; - - if (r != 0 && errno != EINPROGRESS) { - switch (errno) { - /* If we get a ECONNREFUSED wait until the next tick to report the - * error. Solaris wants to report immediately--other unixes want to - * wait. - * - * XXX: do the same for ECONNABORTED? - */ - case ECONNREFUSED: - stream->delayed_error = errno; - break; - - default: - uv__set_sys_error(stream->loop, errno); - return -1; - } - } - uv__io_start(stream->loop, &stream->write_watcher); if (stream->delayed_error) diff --git a/src/win/error.c b/src/win/error.c index d7c4d5333..6490547dc 100644 --- a/src/win/error.c +++ b/src/win/error.c @@ -83,6 +83,7 @@ uv_err_code uv_translate_sys_error(int sys_errno) { case ERROR_SIGNAL_REFUSED: return UV_EIO; case ERROR_FILE_NOT_FOUND: return UV_ENOENT; case ERROR_INVALID_NAME: return UV_ENOENT; + case ERROR_INVALID_REPARSE_DATA: return UV_ENOENT; case ERROR_MOD_NOT_FOUND: return UV_ENOENT; case ERROR_PATH_NOT_FOUND: return UV_ENOENT; case ERROR_ACCESS_DENIED: return UV_EPERM; @@ -111,6 +112,7 @@ uv_err_code uv_translate_sys_error(int sys_errno) { case ERROR_OPERATION_ABORTED: return UV_EINTR; case WSAEINTR: return UV_EINTR; case ERROR_INVALID_DATA: return UV_EINVAL; + case ERROR_SYMLINK_NOT_SUPPORTED: return UV_EINVAL; case WSAEINVAL: return UV_EINVAL; case ERROR_CANT_RESOLVE_FILENAME: return UV_ELOOP; case ERROR_TOO_MANY_OPEN_FILES: return UV_EMFILE; diff --git a/src/win/fs.c b/src/win/fs.c index 64fc2939f..7d6b4f9d9 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -161,47 +161,147 @@ static int is_path_dir(const wchar_t* path) { } -static int get_reparse_point(HANDLE handle, int* target_length) { - void* buffer = NULL; - REPARSE_DATA_BUFFER* reparse_data; - DWORD bytes_returned; - int rv = 0; - - buffer = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); - if (!buffer) { - uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); - } +INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr, + int64_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; + char* target; + int target_len; + DWORD bytes; if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, buffer, - MAXIMUM_REPARSE_DATA_BUFFER_SIZE, - &bytes_returned, + sizeof buffer, + &bytes, NULL)) { - free(buffer); - return 0; + return -1; } - reparse_data = (REPARSE_DATA_BUFFER*)buffer; - if (reparse_data->ReparseTag == IO_REPARSE_TAG_SYMLINK) { - rv = 1; - if (target_length) { - *target_length = reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength / - sizeof(wchar_t); + /* 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) { - rv = 1; - if (target_length) { - *target_length = reparse_data->MountPointReparseBuffer.SubstituteNameLength / + /* Junction. */ + w_target = reparse_data->MountPointReparseBuffer.PathBuffer + + (reparse_data->MountPointReparseBuffer.SubstituteNameOffset / + sizeof(WCHAR)); + w_target_len = reparse_data->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t); + + /* 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; } - free(buffer); - return rv; + /* Compute the length of the target. */ + target_len = WideCharToMultiByte(CP_UTF8, + 0, + w_target, + w_target_len, + NULL, + 0, + NULL, + NULL); + if (target_len == 0) { + return -1; + } + + /* If requested, allocate memory and convert to UTF8. */ + if (target_ptr != NULL) { + int r; + target = (char*) malloc(target_len + 1); + if (target == NULL) { + SetLastError(ERROR_OUTOFMEMORY); + return -1; + } + + r = WideCharToMultiByte(CP_UTF8, + 0, + w_target, + w_target_len, + target, + target_len, + NULL, + NULL); + assert(r == target_len); + target[target_len] = '\0'; + + *target_ptr = target; + } + + if (target_len_ptr != NULL) { + *target_len_ptr = target_len; + } + + return 0; } @@ -455,11 +555,12 @@ void fs__unlink(uv_fs_t* req, const wchar_t* path) { return; } - is_dir_symlink = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && - get_reparse_point(handle, NULL); + is_dir_symlink = (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && + (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT); CloseHandle(handle); + /* Todo: very inefficient; fix this. */ if (is_dir_symlink) { fs__rmdir(req, path); } else { @@ -583,7 +684,6 @@ void fs__readdir(uv_fs_t* req, const wchar_t* path, int flags) { INLINE static int fs__stat_handle(HANDLE handle, uv_statbuf_t* statbuf) { - int target_length; BY_HANDLE_FILE_INFORMATION info; if (!GetFileInformationByHandle(handle, &info)) { @@ -600,28 +700,25 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_statbuf_t* statbuf) { statbuf->st_mode = 0; - if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && - get_reparse_point(handle, &target_length)) { - statbuf->st_mode = S_IFLNK; - /* Adjust for long path */ - statbuf->st_size = target_length - JUNCTION_PREFIX_LEN; + if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + if (fs__readlink_handle(handle, NULL, &statbuf->st_size) != 0) { + return -1; + } + statbuf->st_mode |= S_IFLNK; + } else if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + statbuf->st_mode |= _S_IFDIR; + statbuf->st_size = 0; } else { - if (info.dwFileAttributes & 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)); - } - - if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - statbuf->st_mode |= _S_IFDIR; - } else { - statbuf->st_mode |= _S_IFREG; - } - + statbuf->st_mode |= _S_IFREG; statbuf->st_size = ((int64_t) info.nFileSizeHigh << 32) + (int64_t) info.nFileSizeLow; + } + if (info.dwFileAttributes & 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)); } statbuf->st_mtime = FILETIME_TO_TIME_T(info.ftLastWriteTime); @@ -659,7 +756,16 @@ INLINE static void fs__stat(uv_fs_t* req, const wchar_t* path, int do_lstat) { } if (fs__stat_handle(handle, &req->stat) != 0) { - SET_REQ_WIN32_ERROR(req, GetLastError()); + 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(req, path, 0); + + } else { + /* Stat failed. */ + SET_REQ_WIN32_ERROR(req, GetLastError()); + } + CloseHandle(handle); return; } @@ -1099,117 +1205,31 @@ void fs__symlink(uv_fs_t* req, const wchar_t* path, const wchar_t* new_path, void fs__readlink(uv_fs_t* req, const wchar_t* path) { - int result = -1; - BOOL rv; - HANDLE symlink; - void* buffer = NULL; - DWORD bytes_returned; - REPARSE_DATA_BUFFER* reparse_data; - int utf8size; - wchar_t* substitute_name; - int substitute_name_length; + HANDLE handle; - symlink = CreateFileW(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_WIN32_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, + handle = CreateFileW(path, 0, - buffer, - MAXIMUM_REPARSE_DATA_BUFFER_SIZE, - &bytes_returned, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (!rv) { - result = -1; + if (handle == INVALID_HANDLE_VALUE) { SET_REQ_WIN32_ERROR(req, GetLastError()); - goto done; + return; } - reparse_data = (REPARSE_DATA_BUFFER*)buffer; - if (reparse_data->ReparseTag == IO_REPARSE_TAG_SYMLINK) { - substitute_name = reparse_data->SymbolicLinkReparseBuffer.PathBuffer + - (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset / - sizeof(wchar_t)); - substitute_name_length = - reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength / - sizeof(wchar_t); - } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { - substitute_name = reparse_data->MountPointReparseBuffer.PathBuffer + - (reparse_data->MountPointReparseBuffer.SubstituteNameOffset / - sizeof(wchar_t)); - substitute_name_length = - reparse_data->MountPointReparseBuffer.SubstituteNameLength / - sizeof(wchar_t); - } else { - result = -1; - /* something is seriously wrong */ + if (fs__readlink_handle(handle, (char**) &req->ptr, NULL) != 0) { SET_REQ_WIN32_ERROR(req, GetLastError()); - goto done; - } - - /* Strip off the leading \??\ from the substitute name buffer.*/ - if (wcsncmp(substitute_name, JUNCTION_PREFIX, JUNCTION_PREFIX_LEN) == 0) { - substitute_name += JUNCTION_PREFIX_LEN; - substitute_name_length -= JUNCTION_PREFIX_LEN; - } - - utf8size = uv_utf16_to_utf8(substitute_name, - substitute_name_length, - NULL, - 0); - if (!utf8size) { - result = -1; - SET_REQ_WIN32_ERROR(req, GetLastError()); - goto done; - } - - req->ptr = malloc(utf8size + 1); - if (!req->ptr) { - uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); - } - - utf8size = uv_utf16_to_utf8(substitute_name, - substitute_name_length, - (char*)req->ptr, - utf8size); - if (!utf8size) { - result = -1; - SET_REQ_WIN32_ERROR(req, GetLastError()); - goto done; + CloseHandle(handle); + return; } req->flags |= UV_FS_FREE_PTR; - ((char*)req->ptr)[utf8size] = '\0'; - result = 0; + SET_REQ_RESULT(req, 0); -done: - if (buffer) { - free(buffer); - } - - if (symlink != INVALID_HANDLE_VALUE) { - CloseHandle(symlink); - } - - SET_REQ_RESULT(req, result); + CloseHandle(handle); }