diff --git a/Makefile b/Makefile index adaed6f1e7..51a8b3ea92 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ out/Debug/node: $(MAKE) -C out BUILDTYPE=Debug out/Makefile: common.gypi deps/uv/uv.gyp deps/http_parser/http_parser.gyp deps/zlib/zlib.gyp deps/v8/build/common.gypi deps/v8/tools/gyp/v8.gyp node.gyp config.gypi - tools/gyp_node -f make + $(PYTHON) tools/gyp_node -f make install: all out/Release/node tools/installer.js install $(DESTDIR) diff --git a/configure b/configure index 5e75bf4b8a..1adfbdfc7a 100755 --- a/configure +++ b/configure @@ -241,24 +241,24 @@ def target_arch(): def compiler_version(): try: - proc = subprocess.Popen([CC, '-v'], stderr=subprocess.PIPE) + proc = subprocess.Popen(CC.split() + ['-v'], stderr=subprocess.PIPE) except OSError: - return None + return (False, False, None) lines = proc.communicate()[1].split('\n') version_line = None for i, line in enumerate(lines): if 'version' in line: version_line = line if not version_line: - return None + return (False, False, None) version = version_line.split("version")[1].strip().split()[0].split(".") if not version: - return None + return (False, False, None) return ('LLVM' in version_line, 'clang' in CC, tuple(version)) def configure_node(o): # TODO add gdb - o['variables']['node_prefix'] = options.prefix if options.prefix else '' + o['variables']['node_prefix'] = os.path.expanduser(options.prefix or '') o['variables']['node_install_npm'] = b(not options.without_npm) o['variables']['node_install_waf'] = b(not options.without_waf) o['variables']['host_arch'] = host_arch() @@ -327,7 +327,6 @@ def configure_v8(o): o['libraries'] += ['-lv8'] if options.shared_v8_includes: o['include_dirs'] += [options.shared_v8_includes] - o['variables']['node_shared_v8_includes'] = options.shared_v8_includes def configure_openssl(o): @@ -398,7 +397,8 @@ write('config.mk', "# Do not edit. Generated by the configure script.\n" + ("BUILDTYPE=%s\n" % ('Debug' if options.debug else 'Release'))) if os.name == 'nt': - subprocess.call(['python', 'tools/gyp_node', '-f', 'msvs', - '-G', 'msvs_version=2010']) + gyp_args = ['-f', 'msvs', '-G', 'msvs_version=2010'] else: - subprocess.call(['tools/gyp_node', '-f', 'make']) + gyp_args = ['-f', 'make'] + +subprocess.call([sys.executable, 'tools/gyp_node'] + gyp_args) diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index 69e0db2f8e..dbea113a3e 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/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/deps/uv/src/unix/dl.c b/deps/uv/src/unix/dl.c index 01796e3b8d..9cc830b815 100644 --- a/deps/uv/src/unix/dl.c +++ b/deps/uv/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/deps/uv/src/unix/error.c b/deps/uv/src/unix/error.c index c2651a3a9e..9fbb312eef 100644 --- a/deps/uv/src/unix/error.c +++ b/deps/uv/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/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index 89790eabfd..8c33ee09c5 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/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; } } - 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; + stream->delayed_error = 0; - do { + 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; - } + 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); + stream->connect_req = req; + uv__io_start(stream->loop, &stream->write_watcher); if (stream->delayed_error) diff --git a/deps/uv/src/unix/sunos.c b/deps/uv/src/unix/sunos.c index 261d69d47b..b95a89b456 100644 --- a/deps/uv/src/unix/sunos.c +++ b/deps/uv/src/unix/sunos.c @@ -332,14 +332,6 @@ uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { lookup_instance = 0; while ((ksp = kstat_lookup(kc, (char *)"cpu_info", lookup_instance, NULL))) { if (kstat_read(kc, ksp, NULL) == -1) { - /* - * It is deeply annoying, but some kstats can return errors - * under otherwise routine conditions. (ACPI is one - * offender; there are surely others.) To prevent these - * fouled kstats from completely ruining our day, we assign - * an "error" member to the return value that consists of - * the strerror(). - */ cpu_info->speed = 0; cpu_info->model = NULL; } else { diff --git a/deps/uv/src/win/error.c b/deps/uv/src/win/error.c index e95f39a10e..6490547dc6 100644 --- a/deps/uv/src/win/error.c +++ b/deps/uv/src/win/error.c @@ -83,9 +83,11 @@ 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; + case ERROR_PRIVILEGE_NOT_HELD: return UV_EPERM; case ERROR_NOACCESS: return UV_EACCES; case WSAEACCES: return UV_EACCES; case ERROR_ADDRESS_ALREADY_ASSOCIATED: return UV_EADDRINUSE; @@ -110,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/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c index 64fc2939ff..7d6b4f9d99 100644 --- a/deps/uv/src/win/fs.c +++ b/deps/uv/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; - } 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_REPARSE_POINT) { + if (fs__readlink_handle(handle, NULL, &statbuf->st_size) != 0) { + return -1; } - - if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - statbuf->st_mode |= _S_IFDIR; - } else { - statbuf->st_mode |= _S_IFREG; - } - + statbuf->st_mode |= S_IFLNK; + } else if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + statbuf->st_mode |= _S_IFDIR; + statbuf->st_size = 0; + } else { + 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; - - 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"); - } + HANDLE handle; - 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; - SET_REQ_WIN32_ERROR(req, GetLastError()); - goto done; - } - - 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 */ - 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; + if (handle == INVALID_HANDLE_VALUE) { SET_REQ_WIN32_ERROR(req, GetLastError()); - goto done; - } - - req->ptr = malloc(utf8size + 1); - if (!req->ptr) { - uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + return; } - utf8size = uv_utf16_to_utf8(substitute_name, - substitute_name_length, - (char*)req->ptr, - utf8size); - if (!utf8size) { - result = -1; + if (fs__readlink_handle(handle, (char**) &req->ptr, NULL) != 0) { SET_REQ_WIN32_ERROR(req, GetLastError()); - goto done; + CloseHandle(handle); + return; } req->flags |= UV_FS_FREE_PTR; - ((char*)req->ptr)[utf8size] = '\0'; - result = 0; - -done: - if (buffer) { - free(buffer); - } - - if (symlink != INVALID_HANDLE_VALUE) { - CloseHandle(symlink); - } + SET_REQ_RESULT(req, 0); - SET_REQ_RESULT(req, result); + CloseHandle(handle); } diff --git a/deps/uv/test/test-fs.c b/deps/uv/test/test-fs.c index 36b68f3033..4b6d847f45 100644 --- a/deps/uv/test/test-fs.c +++ b/deps/uv/test/test-fs.c @@ -1224,10 +1224,10 @@ TEST_IMPL(fs_symlink) { * 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) { + } else if (uv_last_error(loop).code == UV_EPERM) { /* * Creating a symlink is only allowed when running elevated. - * We pass the test and bail out early if we get ERROR_PRIVILEGE_NOT_HELD. + * We pass the test and bail out early if we get UV_EPERM. */ return 0; } diff --git a/doc/api/child_process.markdown b/doc/api/child_process.markdown index 716a9013e6..99f72db7f6 100644 --- a/doc/api/child_process.markdown +++ b/doc/api/child_process.markdown @@ -404,7 +404,7 @@ file: err = fs.openSync('./out.log', 'a'); var child = spawn('prg', [], { - detached: 'true', + detached: true, stdio: [ 'ignore', out, err ] }); diff --git a/doc/api/fs.markdown b/doc/api/fs.markdown index 5abef14ba7..170d2eb0dc 100644 --- a/doc/api/fs.markdown +++ b/doc/api/fs.markdown @@ -355,13 +355,7 @@ without waiting for the callback. For this scenario, ## fs.writeSync(fd, buffer, offset, length, position) -Synchronous version of buffer-based `fs.write()`. Returns the number of bytes -written. - -## fs.writeSync(fd, str, position, [encoding]) - -Synchronous version of string-based `fs.write()`. `encoding` defaults to -`'utf8'`. Returns the number of _bytes_ written. +Synchronous version of `fs.write()`. Returns the number of bytes written. ## fs.read(fd, buffer, offset, length, position, [callback]) @@ -380,13 +374,7 @@ The callback is given the three arguments, `(err, bytesRead, buffer)`. ## fs.readSync(fd, buffer, offset, length, position) -Synchronous version of buffer-based `fs.read`. Returns the number of -`bytesRead`. - -## fs.readSync(fd, length, position, encoding) - -Legacy synchronous version of string-based `fs.read`. Returns an array with the -data from the file specified and number of bytes read, `[string, bytesRead]`. +Synchronous version of `fs.read`. Returns the number of `bytesRead`. ## fs.readFile(filename, [encoding], [callback]) diff --git a/doc/blog/release/node-v0.8.0.md b/doc/blog/release/node-v0.8.0.md index 460b3335d0..118668fc92 100644 --- a/doc/blog/release/node-v0.8.0.md +++ b/doc/blog/release/node-v0.8.0.md @@ -48,10 +48,10 @@ Wrote 16384 byte buffers: 83.97010664203543 mB/s Wrote 65536 byte buffers: 97.4184120798831 mB/s # 0.8.0, writes -Wrote 1024 byte buffers: 61.236987140232706 mB/s -Wrote 4096 byte buffers: 109.05125408942203 mB/s -Wrote 16384 byte buffers: 182.18254691200585 mB/s -Wrote 65536 byte buffers: 181.91740949608877 mB/s +Wrote 1024 byte buffers: 61.236987140232706 mB/s +215.19% +Wrote 4096 byte buffers: 109.05125408942203 mB/s +82.55% +Wrote 16384 byte buffers: 182.18254691200585 mB/s +116.96% +Wrote 65536 byte buffers: 181.91740949608877 mB/s +86.74% # v0.6.19, reads Read 1024 byte buffers: 29.96883241428914 mB/s @@ -60,10 +60,10 @@ Read 16384 byte buffers: 165.7550140891762 mB/s Read 65536 byte buffers: 266.73779674579885 mB/s # v0.8.0, reads -Read 1024 byte buffers: 57.63688760806916 mB/s -Read 4096 byte buffers: 136.7801942278758 mB/s -Read 16384 byte buffers: 244.8579823702253 mB/s -Read 65536 byte buffers: 302.2974607013301 mB/s +Read 1024 byte buffers: 57.63688760806916 mB/s +92.32% +Read 4096 byte buffers: 136.7801942278758 mB/s +119.40% +Read 16384 byte buffers: 244.8579823702253 mB/s +47.72% +Read 65536 byte buffers: 302.2974607013301 mB/s +13.33% ``` The difference is not small. If you are writing network programs with @@ -78,9 +78,9 @@ read the file 110948 times (higher is better) 11093.69 reads per sec (higher is better) # v0.8.0 -read the file 158193 times (higher is better) -63217.16 ns per read (lower is better) -15818.48 reads per sec (higher is better) +read the file 158193 times (higher is better) +42.58% +63217.16 ns per read (lower is better) -29.87% +15818.48 reads per sec (higher is better) +42.59% ``` And of course, the ubiquitous 'hello, world' http server benchmark got @@ -91,21 +91,21 @@ $ TYPE=bytes LENGTH=123 bash benchmark/http.sh 2>&1 | grep Req # 0.6.19 Requests per second: 3317.24 [#/sec] (mean) # 0.8.0 -Requests per second: 3795.34 [#/sec] (mean) +Requests per second: 3795.34 [#/sec] (mean) +14.41% $ TYPE=bytes LENGTH=1024 bash benchmark/http.sh 2>&1 | grep Req # v0.6.19 Requests per second: 3258.42 [#/sec] (mean) # 0.8.0 -Requests per second: 3585.62 [#/sec] (mean) +Requests per second: 3585.62 [#/sec] (mean) +10.04% $ TYPE=bytes LENGTH=123456 bash benchmark/http.sh 2>&1 | grep Req # v0.6.19 Requests per second: 218.51 [#/sec] (mean) # 0.8.0 -Requests per second: 749.17 [#/sec] (mean) +Requests per second: 749.17 [#/sec] (mean) +242.85% ``` The difference with Unicode responses is even more pronounced: @@ -115,19 +115,19 @@ $ TYPE=unicode LENGTH=1024 bash benchmark/http.sh 2>&1 | grep Req # v0.6.19 Requests per second: 3228.23 [#/sec] (mean) # v0.8.0 -Requests per second: 3317.60 [#/sec] (mean) +Requests per second: 3317.60 [#/sec] (mean) +2.77% $ TYPE=unicode LENGTH=12345 bash benchmark/http.sh 2>&1 | grep Req # v0.6.19 Requests per second: 1703.96 [#/sec] (mean) # v0.8.0 -Requests per second: 2431.61 [#/sec] (mean) +Requests per second: 2431.61 [#/sec] (mean) +42.70% $ TYPE=unicode LENGTH=55555 bash benchmark/http.sh 2>&1 | grep Req #v0.6.19 Requests per second: 161.65 [#/sec] (mean) #v0.8.0 -Requests per second: 980.38 [#/sec] (mean) +Requests per second: 980.38 [#/sec] (mean) +506.48% $ TYPE=unicode LENGTH=99999 bash benchmark/http.sh 2>&1 | grep Req # v0.6.19 @@ -379,3 +379,6 @@ fc07b475d943f7681e1904d6d7d666b41874a6fa x64/node.exe 686c60d5ae5dad7fcffcdc88049c63b2cd23cffc x64/node.lib 75549cffab0c11107348a66ab0d94d4897bd6a27 x64/node.pdb ``` + +Edited by Tim Oxley to provide percentage differences in the +benchmarks. diff --git a/lib/fs.js b/lib/fs.js index 8bfc10cdd3..4aac1923a5 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -966,10 +966,12 @@ if (isWindows) { var nextPartRe = /(.*?)(?:[\/]+|$)/g; } -// Regex to split a windows path into three parts: [*, device, slash, -// tail] windows-only -var splitDeviceRe = - /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?([\s\S]*?)$/; +// Regex to find the device root, including trailing slash. E.g. 'c:\\'. +if (isWindows) { + var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/; +} else { + var splitRootRe = /^[\/]*/; +} fs.realpathSync = function realpathSync(p, cache) { // make p is absolute @@ -984,13 +986,30 @@ fs.realpathSync = function realpathSync(p, cache) { knownHard = {}; // current character position in p - var pos = 0; + var pos; // the partial path so far, including a trailing slash if any - var current = ''; - // the partial path without a trailing slash - var base = ''; + var current; + // the partial path without a trailing slash (except when pointing at a root) + var base; // the partial path scanned in the previous round, with slash - var previous = ''; + var previous; + + start(); + + function start() { + // Skip over roots + var m = splitRootRe.exec(p); + pos = m[0].length; + current = m[0]; + base = m[0]; + previous = ''; + + // On windows, check that the root exists. On unix there is no need. + if (isWindows && !knownHard[base]) { + fs.lstatSync(base); + knownHard[base] = true; + } + } // walk down the path, swapping out linked pathparts for their real // values @@ -1004,16 +1023,8 @@ fs.realpathSync = function realpathSync(p, cache) { base = previous + result[1]; pos = nextPartRe.lastIndex; - // continue if not a symlink, or if root - var isRoot = !base; - if (isWindows) { - // if it doens't have a tail, then it's the root. - var split = base.match(splitDeviceRe); - if (split) { - isRoot = !split[2]; - } - } - if (isRoot || knownHard[base] || (cache && cache[base] === base)) { + // continue if not a symlink + if (knownHard[base] || (cache && cache[base] === base)) { continue; } @@ -1050,8 +1061,7 @@ fs.realpathSync = function realpathSync(p, cache) { // resolve the link, then start over p = pathModule.resolve(resolvedLink, p.slice(pos)); - pos = 0; - previous = base = current = ''; + start(); } if (cache) cache[original] = p; @@ -1070,7 +1080,7 @@ fs.realpath = function realpath(p, cache, cb) { p = pathModule.resolve(p); if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { - return cb(null, cache[p]); + return process.nextTick(cb.bind(null, null, cache[p])); } var original = p, @@ -1078,17 +1088,38 @@ fs.realpath = function realpath(p, cache, cb) { knownHard = {}; // current character position in p - var pos = 0; + var pos; // the partial path so far, including a trailing slash if any - var current = ''; - // the partial path without a trailing slash - var base = ''; + var current; + // the partial path without a trailing slash (except when pointing at a root) + var base; // the partial path scanned in the previous round, with slash - var previous = ''; + var previous; + + start(); + + function start() { + // Skip over roots + var m = splitRootRe.exec(p); + pos = m[0].length; + current = m[0]; + base = m[0]; + previous = ''; + + // On windows, check that the root exists. On unix there is no need. + if (isWindows && !knownHard[base]) { + fs.lstat(base, function (err) { + if (err) return cb(err); + knownHard[base] = true; + LOOP(); + }); + } else { + process.nextTick(LOOP); + } + } // walk down the path, swapping out linked pathparts for their real // values - LOOP(); function LOOP() { // stop if scanned past end of path if (pos >= p.length) { @@ -1104,16 +1135,8 @@ fs.realpath = function realpath(p, cache, cb) { base = previous + result[1]; pos = nextPartRe.lastIndex; - // continue if not a symlink, or if root - var isRoot = !base; - if (isWindows) { - // if it doens't have a tail, then it's the root. - var split = base.match(splitDeviceRe); - if (split) { - isRoot = !split[2]; - } - } - if (isRoot || knownHard[base] || (cache && cache[base] === base)) { + // continue if not a symlink + if (knownHard[base] || (cache && cache[base] === base)) { return process.nextTick(LOOP); } @@ -1163,13 +1186,9 @@ fs.realpath = function realpath(p, cache, cb) { } function gotResolvedLink(resolvedLink) { - // resolve the link, then start over p = pathModule.resolve(resolvedLink, p.slice(pos)); - pos = 0; - previous = base = current = ''; - - return process.nextTick(LOOP); + start(); } }; diff --git a/lib/sys.js b/lib/sys.js index 276b0f189c..cf98a073e6 100644 --- a/lib/sys.js +++ b/lib/sys.js @@ -1 +1,24 @@ -throw new Error('The "sys" module is now called "util".'); +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +// the sys module was renamed to 'util'. +// this shim remains to keep old programs working. +module.exports = require('util'); diff --git a/lib/util.js b/lib/util.js index 580aa81192..65888920c8 100644 --- a/lib/util.js +++ b/lib/util.js @@ -175,6 +175,17 @@ function stylizeNoColor(str, styleType) { } +function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + function formatValue(ctx, value, recurseTimes) { // Provide a hook for user-specified inspect functions. // Check that value is an object with an inspect function on it @@ -193,8 +204,12 @@ function formatValue(ctx, value, recurseTimes) { } // Look up the keys of the object. - var visibleKeys = Object.keys(value); - var keys = ctx.showHidden ? Object.getOwnPropertyNames(value) : visibleKeys; + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } // Some type of object without properties can be shortcutted. if (keys.length === 0) { @@ -334,7 +349,7 @@ function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { str = ctx.stylize('[Setter]', 'special'); } } - if (visibleKeys.indexOf(key) < 0) { + if (!visibleKeys.hasOwnProperty(key)) { name = '[' + key + ']'; } if (!str) { diff --git a/node.gyp b/node.gyp index 0816442e47..933812e3df 100644 --- a/node.gyp +++ b/node.gyp @@ -177,12 +177,7 @@ '<(SHARED_INTERMEDIATE_DIR)/node_etw_provider.rc', ] } ], - [ 'node_shared_v8=="true"', { - 'sources': [ - '<(node_shared_v8_includes)/v8.h', - '<(node_shared_v8_includes)/v8-debug.h', - ], - }, { + [ 'node_shared_v8=="false"', { 'sources': [ 'deps/v8/include/v8.h', 'deps/v8/include/v8-debug.h', diff --git a/test/simple/test-fs-realpath.js b/test/simple/test-fs-realpath.js index d154e4fcd9..2f126ea300 100644 --- a/test/simple/test-fs-realpath.js +++ b/test/simple/test-fs-realpath.js @@ -515,10 +515,13 @@ function test_lying_cache_liar(cb) { var rps = fs.realpathSync(bluff, cache); assert.equal(cache[bluff], rps); var nums = path.resolve('/1/2/3/4/5/6/7'); + var called = false; // no sync cb calling! fs.realpath(nums, cache, function(er, rp) { + called = true; assert.equal(cache[nums], rp); if (--n === 0) cb(); }); + assert(called === false); var test = path.resolve('/a/b/c/d'), expect = path.resolve('/a/b/d'); diff --git a/test/simple/test-regress-GH-3542.js b/test/simple/test-regress-GH-3542.js new file mode 100644 index 0000000000..90e0b78501 --- /dev/null +++ b/test/simple/test-regress-GH-3542.js @@ -0,0 +1,54 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +// This test is only relevant on Windows. +if (process.platform !== 'win32') { + return process.exit(0); +} + +var common = require('../common.js'), + assert = require('assert'), + fs = require('fs'), + path = require('path'), + succeeded = 0; + +function test(p) { + var result = fs.realpathSync(p); + assert.strictEqual(result, path.resolve(p)); + + fs.realpath(p, function(err, result) { + assert.ok(!err); + assert.strictEqual(result, path.resolve(p)); + succeeded++; + }); +} + +test('//localhost/c$/windows/system32'); +test('//localhost/c$/windows'); +test('//localhost/c$/') +test('\\\\localhost\\c$') +test('c:\\'); +test('c:'); +test(process.env.windir); + +process.on('exit', function() { + assert.strictEqual(succeeded, 7); +}); \ No newline at end of file diff --git a/tools/blog/generate.js b/tools/blog/generate.js index a9c69b56ad..130f8d4ecf 100644 --- a/tools/blog/generate.js +++ b/tools/blog/generate.js @@ -170,9 +170,18 @@ function buildFeeds(data) { } // filter non-latest release notices out of main feeds. + // still show the first stable release of the family, since + // it usually is an important milestone with benchmarks and stuff. var main = posts.filter(function(post) { - if (post.version && post.family && post !== releases[post.family][0]) { - return false; + if (post.version && post.family) { + var ver = semver.parse(post.version) + if (+ver[2] % 2 === 0 && +ver[3] === 0) { + // 0.x.0, where x is event + return true; + } + if (post.version && post.family && post !== releases[post.family][0]) { + return false; + } } return true; });