diff --git a/.gitignore b/.gitignore index c1329879..b6abc2ab 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ Makefile.in /test/run-benchmarks /test/run-benchmarks.exe /test/run-benchmarks.dSYM +test_file_* *.sln *.sln.cache diff --git a/CMakeLists.txt b/CMakeLists.txt index a34e5a1b..bf7990f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,8 @@ set(uv_test_sources test/test-fs-poll.c test/test-fs.c test/test-fs-readdir.c + test/test-fs-fd-hash.c + test/test-fs-open-flags.c test/test-get-currentexe.c test/test-get-loadavg.c test/test-get-memory.c diff --git a/Makefile.am b/Makefile.am index d213d8f4..9154c888 100644 --- a/Makefile.am +++ b/Makefile.am @@ -184,6 +184,8 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-fs-poll.c \ test/test-fs.c \ test/test-fs-readdir.c \ + test/test-fs-fd-hash.c \ + test/test-fs-open-flags.c \ test/test-fork.c \ test/test-getters-setters.c \ test/test-get-currentexe.c \ diff --git a/docs/src/fs.rst b/docs/src/fs.rst index 996624b6..fb8105fb 100644 --- a/docs/src/fs.rst +++ b/docs/src/fs.rst @@ -534,6 +534,14 @@ File open constants .. versionchanged:: 1.17.0 support is added for Windows. +.. c:macro:: UV_FS_O_FILEMAP + + Use a memory file mapping to access the file. When using this flag, the + file cannot be open multiple times concurrently. + + .. note:: + `UV_FS_O_FILEMAP` is only supported on Windows. + .. c:macro:: UV_FS_O_NOATIME Do not update the file access time when the file is read. diff --git a/include/uv/unix.h b/include/uv/unix.h index 6c93ee97..bf786c9b 100644 --- a/include/uv/unix.h +++ b/include/uv/unix.h @@ -481,6 +481,7 @@ typedef struct { #endif /* fs open() flags supported on other platforms: */ +#define UV_FS_O_FILEMAP 0 #define UV_FS_O_RANDOM 0 #define UV_FS_O_SHORT_LIVED 0 #define UV_FS_O_SEQUENTIAL 0 diff --git a/include/uv/win.h b/include/uv/win.h index acbd958b..9793eee3 100644 --- a/include/uv/win.h +++ b/include/uv/win.h @@ -668,6 +668,7 @@ typedef struct { #define UV_FS_O_APPEND _O_APPEND #define UV_FS_O_CREAT _O_CREAT #define UV_FS_O_EXCL _O_EXCL +#define UV_FS_O_FILEMAP 0x20000000 #define UV_FS_O_RANDOM _O_RANDOM #define UV_FS_O_RDONLY _O_RDONLY #define UV_FS_O_RDWR _O_RDWR diff --git a/src/win/core.c b/src/win/core.c index 91545db7..e9d0a581 100644 --- a/src/win/core.c +++ b/src/win/core.c @@ -204,6 +204,9 @@ static void uv_init(void) { /* Initialize winsock */ uv_winsock_init(); + /* Initialize FS */ + uv_fs_init(); + /* Initialize signal stuff */ uv_signals_init(); diff --git a/src/win/fs-fd-hash-inl.h b/src/win/fs-fd-hash-inl.h new file mode 100644 index 00000000..7a203d23 --- /dev/null +++ b/src/win/fs-fd-hash-inl.h @@ -0,0 +1,178 @@ +#ifndef UV_WIN_FS_FD_HASH_INL_H_ +#define UV_WIN_FS_FD_HASH_INL_H_ + +#include "uv.h" +#include "internal.h" + +/* Files are only inserted in uv__fd_hash when the UV_FS_O_FILEMAP flag is + * specified. Thus, when uv__fd_hash_get returns true, the file mapping in the + * info structure should be used for read/write operations. + * + * If the file is empty, the mapping field will be set to + * INVALID_HANDLE_VALUE. This is not an issue since the file mapping needs to + * be created anyway when the file size changes. + * + * Since file descriptors are sequential integers, the modulo operator is used + * as hashing function. For each bucket, a single linked list of arrays is + * kept to minimize allocations. A statically allocated memory buffer is kept + * for the first array in each bucket. */ + + +#define UV__FD_HASH_SIZE 256 +#define UV__FD_HASH_GROUP_SIZE 16 + +struct uv__fd_info_s { + int flags; + BOOLEAN is_directory; + HANDLE mapping; + LARGE_INTEGER size; + LARGE_INTEGER current_pos; +}; + +struct uv__fd_hash_entry_s { + uv_file fd; + struct uv__fd_info_s info; +}; + +struct uv__fd_hash_entry_group_s { + struct uv__fd_hash_entry_s entries[UV__FD_HASH_GROUP_SIZE]; + struct uv__fd_hash_entry_group_s* next; +}; + +struct uv__fd_hash_bucket_s { + size_t size; + struct uv__fd_hash_entry_group_s* data; +}; + + +static uv_mutex_t uv__fd_hash_mutex; + +static struct uv__fd_hash_entry_group_s + uv__fd_hash_entry_initial[UV__FD_HASH_SIZE * UV__FD_HASH_GROUP_SIZE]; +static struct uv__fd_hash_bucket_s uv__fd_hash[UV__FD_HASH_SIZE]; + + +INLINE static void uv__fd_hash_init(void) { + int i, err; + + err = uv_mutex_init(&uv__fd_hash_mutex); + if (err) { + uv_fatal_error(err, "uv_mutex_init"); + } + + for (i = 0; i < ARRAY_SIZE(uv__fd_hash); ++i) { + uv__fd_hash[i].size = 0; + uv__fd_hash[i].data = + uv__fd_hash_entry_initial + i * UV__FD_HASH_GROUP_SIZE; + } +} + +#define FIND_COMMON_VARIABLES \ + unsigned i; \ + unsigned bucket = fd % ARRAY_SIZE(uv__fd_hash); \ + struct uv__fd_hash_entry_s* entry_ptr = NULL; \ + struct uv__fd_hash_entry_group_s* group_ptr; \ + struct uv__fd_hash_bucket_s* bucket_ptr = &uv__fd_hash[bucket]; + +#define FIND_IN_GROUP_PTR(group_size) \ + do { \ + for (i = 0; i < group_size; ++i) { \ + if (group_ptr->entries[i].fd == fd) { \ + entry_ptr = &group_ptr->entries[i]; \ + break; \ + } \ + } \ + } while (0) + +#define FIND_IN_BUCKET_PTR() \ + do { \ + size_t first_group_size = bucket_ptr->size % UV__FD_HASH_GROUP_SIZE; \ + if (bucket_ptr->size != 0 && first_group_size == 0) \ + first_group_size = UV__FD_HASH_GROUP_SIZE; \ + group_ptr = bucket_ptr->data; \ + FIND_IN_GROUP_PTR(first_group_size); \ + for (group_ptr = group_ptr->next; \ + group_ptr != NULL && entry_ptr == NULL; \ + group_ptr = group_ptr->next) \ + FIND_IN_GROUP_PTR(UV__FD_HASH_GROUP_SIZE); \ + } while (0) + +INLINE static int uv__fd_hash_get(int fd, struct uv__fd_info_s* info) { + FIND_COMMON_VARIABLES + + uv_mutex_lock(&uv__fd_hash_mutex); + + FIND_IN_BUCKET_PTR(); + + if (entry_ptr != NULL) { + *info = entry_ptr->info; + } + + uv_mutex_unlock(&uv__fd_hash_mutex); + return entry_ptr != NULL; +} + +INLINE static void uv__fd_hash_add(int fd, struct uv__fd_info_s* info) { + FIND_COMMON_VARIABLES + + uv_mutex_lock(&uv__fd_hash_mutex); + + FIND_IN_BUCKET_PTR(); + + if (entry_ptr == NULL) { + i = bucket_ptr->size % UV__FD_HASH_GROUP_SIZE; + + if (bucket_ptr->size != 0 && i == 0) { + struct uv__fd_hash_entry_group_s* new_group_ptr = + uv__malloc(sizeof(*new_group_ptr)); + if (new_group_ptr == NULL) { + uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); + } + new_group_ptr->next = bucket_ptr->data; + bucket_ptr->data = new_group_ptr; + } + + bucket_ptr->size += 1; + entry_ptr = &bucket_ptr->data->entries[i]; + entry_ptr->fd = fd; + } + + entry_ptr->info = *info; + + uv_mutex_unlock(&uv__fd_hash_mutex); +} + +INLINE static int uv__fd_hash_remove(int fd, struct uv__fd_info_s* info) { + FIND_COMMON_VARIABLES + + uv_mutex_lock(&uv__fd_hash_mutex); + + FIND_IN_BUCKET_PTR(); + + if (entry_ptr != NULL) { + *info = entry_ptr->info; + + bucket_ptr->size -= 1; + + i = bucket_ptr->size % UV__FD_HASH_GROUP_SIZE; + if (entry_ptr != &bucket_ptr->data->entries[i]) { + *entry_ptr = bucket_ptr->data->entries[i]; + } + + if (bucket_ptr->size != 0 && + bucket_ptr->size % UV__FD_HASH_GROUP_SIZE == 0) { + struct uv__fd_hash_entry_group_s* old_group_ptr = bucket_ptr->data; + bucket_ptr->data = old_group_ptr->next; + uv__free(old_group_ptr); + } + } + + uv_mutex_unlock(&uv__fd_hash_mutex); + return entry_ptr != NULL; +} + +#undef FIND_COMMON_VARIABLES +#undef FIND_IN_GROUP_PTR +#undef FIND_IN_BUCKET_PTR + +#endif /* UV_WIN_FS_FD_HASH_INL_H_ */ diff --git a/src/win/fs.c b/src/win/fs.c index 01574219..15094121 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -34,6 +34,7 @@ #include "internal.h" #include "req-inl.h" #include "handle-inl.h" +#include "fs-fd-hash-inl.h" #include @@ -126,6 +127,8 @@ #define IS_LETTER(c) (((c) >= L'a' && (c) <= L'z') || \ ((c) >= L'A' && (c) <= L'Z')) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + const WCHAR JUNCTION_PREFIX[] = L"\\??\\"; const WCHAR JUNCTION_PREFIX_LEN = 4; @@ -137,6 +140,18 @@ const WCHAR UNC_PATH_PREFIX_LEN = 8; static int uv__file_symlink_usermode_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; +static DWORD uv__allocation_granularity; + + +void uv_fs_init(void) { + SYSTEM_INFO system_info; + + GetSystemInfo(&system_info); + uv__allocation_granularity = system_info.dwAllocationGranularity; + + uv__fd_hash_init(); +} + INLINE static int fs__capture_path(uv_fs_t* req, const char* path, const char* new_path, const int copy_path) { @@ -410,6 +425,27 @@ void fs__open(uv_fs_t* req) { HANDLE file; int fd, current_umask; int flags = req->fs.info.file_flags; + struct uv__fd_info_s fd_info; + + /* Adjust flags to be compatible with the memory file mapping. Save the + * original flags to emulate the correct behavior. */ + if (flags & UV_FS_O_FILEMAP) { + fd_info.flags = flags; + fd_info.current_pos.QuadPart = 0; + + if ((flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) == + UV_FS_O_WRONLY) { + /* CreateFileMapping always needs read access */ + flags = (flags & ~UV_FS_O_WRONLY) | UV_FS_O_RDWR; + } + + if (flags & UV_FS_O_APPEND) { + /* Clear the append flag and ensure RDRW mode */ + flags &= ~UV_FS_O_APPEND; + flags &= ~(UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); + flags |= UV_FS_O_RDWR; + } + } /* Obtain the active umask. umask() never fails and returns the previous * umask. */ @@ -440,7 +476,8 @@ void fs__open(uv_fs_t* req) { * 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. + * be deleted even whilst it's open, fixing issue + * https://github.com/nodejs/node-v0.x-archive/issues/1449. * We still support exclusive sharing mode, since it is necessary * for opening raw block devices, otherwise Windows will prevent * any attempt to write past the master boot record. @@ -579,11 +616,55 @@ void fs__open(uv_fs_t* req) { else if (GetLastError() != ERROR_SUCCESS) SET_REQ_WIN32_ERROR(req, GetLastError()); else - SET_REQ_WIN32_ERROR(req, UV_UNKNOWN); + SET_REQ_WIN32_ERROR(req, (DWORD) UV_UNKNOWN); CloseHandle(file); return; } + if (flags & UV_FS_O_FILEMAP) { + FILE_STANDARD_INFO file_info; + if (!GetFileInformationByHandleEx(file, + FileStandardInfo, + &file_info, + sizeof file_info)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + CloseHandle(file); + return; + } + fd_info.is_directory = file_info.Directory; + + if (fd_info.is_directory) { + fd_info.size.QuadPart = 0; + fd_info.mapping = INVALID_HANDLE_VALUE; + } else { + if (!GetFileSizeEx(file, &fd_info.size)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + CloseHandle(file); + return; + } + + if (fd_info.size.QuadPart == 0) { + fd_info.mapping = INVALID_HANDLE_VALUE; + } else { + DWORD flProtect = (fd_info.flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | + UV_FS_O_RDWR)) == UV_FS_O_RDONLY ? PAGE_READONLY : PAGE_READWRITE; + fd_info.mapping = CreateFileMapping(file, + NULL, + flProtect, + fd_info.size.HighPart, + fd_info.size.LowPart, + NULL); + if (fd_info.mapping == NULL) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + CloseHandle(file); + return; + } + } + } + + uv__fd_hash_add(fd, &fd_info); + } + SET_REQ_RESULT(req, fd); return; @@ -594,9 +675,16 @@ void fs__open(uv_fs_t* req) { void fs__close(uv_fs_t* req) { int fd = req->file.fd; int result; + struct uv__fd_info_s fd_info; VERIFY_FD(fd, req); + if (uv__fd_hash_remove(fd, &fd_info)) { + if (fd_info.mapping != INVALID_HANDLE_VALUE) { + CloseHandle(fd_info.mapping); + } + } + if (fd > 2) result = _close(fd); else @@ -614,6 +702,119 @@ void fs__close(uv_fs_t* req) { } +LONG fs__filemap_ex_filter(LONG excode, PEXCEPTION_POINTERS pep, + int* perror) { + if (excode != EXCEPTION_IN_PAGE_ERROR) { + return EXCEPTION_CONTINUE_SEARCH; + } + + assert(perror != NULL); + if (pep != NULL && pep->ExceptionRecord != NULL && + pep->ExceptionRecord->NumberParameters >= 3) { + NTSTATUS status = (NTSTATUS)pep->ExceptionRecord->ExceptionInformation[3]; + *perror = pRtlNtStatusToDosError(status); + if (*perror != ERROR_SUCCESS) { + return EXCEPTION_EXECUTE_HANDLER; + } + } + *perror = UV_UNKNOWN; + return EXCEPTION_EXECUTE_HANDLER; +} + + +void fs__read_filemap(uv_fs_t* req, struct uv__fd_info_s* fd_info) { + int fd = req->file.fd; /* VERIFY_FD done in fs__read */ + int rw_flags = fd_info->flags & + (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); + size_t read_size, done_read; + unsigned int index; + LARGE_INTEGER pos, end_pos; + size_t view_offset; + LARGE_INTEGER view_base; + void* view; + + if (rw_flags == UV_FS_O_WRONLY) { + SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED); + return; + } + if (fd_info->is_directory) { + SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FUNCTION); + return; + } + + if (req->fs.info.offset == -1) { + pos = fd_info->current_pos; + } else { + pos.QuadPart = req->fs.info.offset; + } + + /* Make sure we wont read past EOF. */ + if (pos.QuadPart >= fd_info->size.QuadPart) { + SET_REQ_RESULT(req, 0); + return; + } + + read_size = 0; + for (index = 0; index < req->fs.info.nbufs; ++index) { + read_size += req->fs.info.bufs[index].len; + } + read_size = (size_t) MIN((LONGLONG) read_size, + fd_info->size.QuadPart - pos.QuadPart); + if (read_size == 0) { + SET_REQ_RESULT(req, 0); + return; + } + + end_pos.QuadPart = pos.QuadPart + read_size; + + view_offset = pos.QuadPart % uv__allocation_granularity; + view_base.QuadPart = pos.QuadPart - view_offset; + view = MapViewOfFile(fd_info->mapping, + FILE_MAP_READ, + view_base.HighPart, + view_base.LowPart, + view_offset + read_size); + if (view == NULL) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + return; + } + + done_read = 0; + for (index = 0; + index < req->fs.info.nbufs && done_read < read_size; + ++index) { + int err = 0; + size_t this_read_size = MIN(req->fs.info.bufs[index].len, + read_size - done_read); + __try { + memcpy(req->fs.info.bufs[index].base, + (char*)view + view_offset + done_read, + this_read_size); + } + __except (fs__filemap_ex_filter(GetExceptionCode(), + GetExceptionInformation(), &err)) { + SET_REQ_WIN32_ERROR(req, err); + UnmapViewOfFile(view); + return; + } + done_read += this_read_size; + } + assert(done_read == read_size); + + if (!UnmapViewOfFile(view)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + return; + } + + if (req->fs.info.offset == -1) { + fd_info->current_pos = end_pos; + uv__fd_hash_add(fd, fd_info); + } + + SET_REQ_RESULT(req, read_size); + return; +} + void fs__read(uv_fs_t* req) { int fd = req->file.fd; int64_t offset = req->fs.info.offset; @@ -627,9 +828,15 @@ void fs__read(uv_fs_t* req) { LARGE_INTEGER original_position; LARGE_INTEGER zero_offset; int restore_position; + struct uv__fd_info_s fd_info; VERIFY_FD(fd, req); + if (uv__fd_hash_get(fd, &fd_info)) { + fs__read_filemap(req, &fd_info); + return; + } + zero_offset.QuadPart = 0; restore_position = 0; handle = uv__get_osfhandle(fd); @@ -686,6 +893,127 @@ void fs__read(uv_fs_t* req) { } +void fs__write_filemap(uv_fs_t* req, HANDLE file, + struct uv__fd_info_s* fd_info) { + int fd = req->file.fd; /* VERIFY_FD done in fs__write */ + int force_append = fd_info->flags & UV_FS_O_APPEND; + int rw_flags = fd_info->flags & + (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); + size_t write_size, done_write; + unsigned int index; + LARGE_INTEGER zero, pos, end_pos; + size_t view_offset; + LARGE_INTEGER view_base; + void* view; + FILETIME ft; + + if (rw_flags == UV_FS_O_RDONLY) { + SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED); + return; + } + if (fd_info->is_directory) { + SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FUNCTION); + return; + } + + write_size = 0; + for (index = 0; index < req->fs.info.nbufs; ++index) { + write_size += req->fs.info.bufs[index].len; + } + + if (write_size == 0) { + SET_REQ_RESULT(req, 0); + return; + } + + zero.QuadPart = 0; + if (force_append) { + pos = fd_info->size; + } else if (req->fs.info.offset == -1) { + pos = fd_info->current_pos; + } else { + pos.QuadPart = req->fs.info.offset; + } + + end_pos.QuadPart = pos.QuadPart + write_size; + + /* Recreate the mapping to enlarge the file if needed */ + if (end_pos.QuadPart > fd_info->size.QuadPart) { + if (fd_info->mapping != INVALID_HANDLE_VALUE) { + CloseHandle(fd_info->mapping); + } + + fd_info->mapping = CreateFileMapping(file, + NULL, + PAGE_READWRITE, + end_pos.HighPart, + end_pos.LowPart, + NULL); + if (fd_info->mapping == NULL) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + CloseHandle(file); + fd_info->mapping = INVALID_HANDLE_VALUE; + fd_info->size.QuadPart = 0; + fd_info->current_pos.QuadPart = 0; + uv__fd_hash_add(fd, fd_info); + return; + } + + fd_info->size = end_pos; + uv__fd_hash_add(fd, fd_info); + } + + view_offset = pos.QuadPart % uv__allocation_granularity; + view_base.QuadPart = pos.QuadPart - view_offset; + view = MapViewOfFile(fd_info->mapping, + FILE_MAP_WRITE, + view_base.HighPart, + view_base.LowPart, + view_offset + write_size); + if (view == NULL) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + return; + } + + done_write = 0; + for (index = 0; index < req->fs.info.nbufs; ++index) { + int err = 0; + __try { + memcpy((char*)view + view_offset + done_write, + req->fs.info.bufs[index].base, + req->fs.info.bufs[index].len); + } + __except (fs__filemap_ex_filter(GetExceptionCode(), + GetExceptionInformation(), &err)) { + SET_REQ_WIN32_ERROR(req, err); + UnmapViewOfFile(view); + return; + } + done_write += req->fs.info.bufs[index].len; + } + assert(done_write == write_size); + + if (!FlushViewOfFile(view, 0)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + UnmapViewOfFile(view); + return; + } + if (!UnmapViewOfFile(view)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + return; + } + + if (req->fs.info.offset == -1) { + fd_info->current_pos = end_pos; + uv__fd_hash_add(fd, fd_info); + } + + GetSystemTimeAsFileTime(&ft); + SetFileTime(file, NULL, NULL, &ft); + + SET_REQ_RESULT(req, done_write); +} + void fs__write(uv_fs_t* req) { int fd = req->file.fd; int64_t offset = req->fs.info.offset; @@ -698,6 +1026,7 @@ void fs__write(uv_fs_t* req) { LARGE_INTEGER original_position; LARGE_INTEGER zero_offset; int restore_position; + struct uv__fd_info_s fd_info; VERIFY_FD(fd, req); @@ -709,6 +1038,11 @@ void fs__write(uv_fs_t* req) { return; } + if (uv__fd_hash_get(fd, &fd_info)) { + fs__write_filemap(req, handle, &fd_info); + return; + } + if (offset != -1) { memset(&overlapped, 0, sizeof overlapped); overlapped_ptr = &overlapped; @@ -1532,6 +1866,7 @@ static void fs__fdatasync(uv_fs_t* req) { static void fs__ftruncate(uv_fs_t* req) { int fd = req->file.fd; HANDLE handle; + struct uv__fd_info_s fd_info = { 0 }; NTSTATUS status; IO_STATUS_BLOCK io_status; FILE_END_OF_FILE_INFORMATION eof_info; @@ -1540,6 +1875,17 @@ static void fs__ftruncate(uv_fs_t* req) { handle = uv__get_osfhandle(fd); + if (uv__fd_hash_get(fd, &fd_info)) { + if (fd_info.is_directory) { + SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED); + return; + } + + if (fd_info.mapping != INVALID_HANDLE_VALUE) { + CloseHandle(fd_info.mapping); + } + } + eof_info.EndOfFile.QuadPart = req->fs.info.offset; status = pNtSetInformationFile(handle, @@ -1552,6 +1898,43 @@ static void fs__ftruncate(uv_fs_t* req) { SET_REQ_RESULT(req, 0); } else { SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status)); + + if (fd_info.flags) { + CloseHandle(handle); + fd_info.mapping = INVALID_HANDLE_VALUE; + fd_info.size.QuadPart = 0; + fd_info.current_pos.QuadPart = 0; + uv__fd_hash_add(fd, &fd_info); + return; + } + } + + if (fd_info.flags) { + fd_info.size = eof_info.EndOfFile; + + if (fd_info.size.QuadPart == 0) { + fd_info.mapping = INVALID_HANDLE_VALUE; + } else { + DWORD flProtect = (fd_info.flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | + UV_FS_O_RDWR)) == UV_FS_O_RDONLY ? PAGE_READONLY : PAGE_READWRITE; + fd_info.mapping = CreateFileMapping(handle, + NULL, + flProtect, + fd_info.size.HighPart, + fd_info.size.LowPart, + NULL); + if (fd_info.mapping == NULL) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + CloseHandle(handle); + fd_info.mapping = INVALID_HANDLE_VALUE; + fd_info.size.QuadPart = 0; + fd_info.current_pos.QuadPart = 0; + uv__fd_hash_add(fd, &fd_info); + return; + } + } + + uv__fd_hash_add(fd, &fd_info); } } diff --git a/src/win/internal.h b/src/win/internal.h index 8f7d77c0..70ddaa53 100644 --- a/src/win/internal.h +++ b/src/win/internal.h @@ -240,6 +240,12 @@ void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle); int uv_translate_sys_error(int sys_errno); +/* + * FS + */ +void uv_fs_init(void); + + /* * FS Event */ diff --git a/test/test-fs-fd-hash.c b/test/test-fs-fd-hash.c new file mode 100644 index 00000000..8b4bc035 --- /dev/null +++ b/test/test-fs-fd-hash.c @@ -0,0 +1,133 @@ +/* Copyright libuv project 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. + */ + +#if defined(_WIN32) && !defined(USING_UV_SHARED) + +#include "uv.h" +#include "task.h" + +#include "../src/win/fs-fd-hash-inl.h" + + +#define HASH_MAX 1000000000 +#define HASH_INC (1000 * UV__FD_HASH_SIZE + 2) +#define BUCKET_MAX (UV__FD_HASH_SIZE * UV__FD_HASH_GROUP_SIZE * 10) +#define BUCKET_INC UV__FD_HASH_SIZE +#define FD_DIFF 9 + + +void assert_nonexistent(int fd) { + struct uv__fd_info_s info = { 0 }; + ASSERT(!uv__fd_hash_get(fd, &info)); + ASSERT(!uv__fd_hash_remove(fd, &info)); +} + +void assert_existent(int fd) { + struct uv__fd_info_s info = { 0 }; + ASSERT(uv__fd_hash_get(fd, &info)); + ASSERT(info.flags == fd + FD_DIFF); +} + +void assert_insertion(int fd) { + struct uv__fd_info_s info = { 0 }; + assert_nonexistent(fd); + info.flags = fd + FD_DIFF; + uv__fd_hash_add(fd, &info); + assert_existent(fd); +} + +void assert_removal(int fd) { + struct uv__fd_info_s info = { 0 }; + assert_existent(fd); + uv__fd_hash_remove(fd, &info); + ASSERT(info.flags == fd + FD_DIFF); + assert_nonexistent(fd); +} + + +/* Run a function for a set of values up to a very high number */ +#define RUN_HASH(function) \ + do { \ + for (fd = 0; fd < HASH_MAX; fd += HASH_INC) { \ + function(fd); \ + } \ + } while (0) + +/* Run a function for a set of values that will cause many collisions */ +#define RUN_COLLISIONS(function) \ + do { \ + for (fd = 1; fd < BUCKET_MAX; fd += BUCKET_INC) { \ + function(fd); \ + } \ + } while (0) + + +TEST_IMPL(fs_fd_hash) { + int fd; + + uv__fd_hash_init(); + + /* Empty table */ + RUN_HASH(assert_nonexistent); + RUN_COLLISIONS(assert_nonexistent); + + /* Fill up */ + RUN_HASH(assert_insertion); + RUN_COLLISIONS(assert_insertion); + + /* Full */ + RUN_HASH(assert_existent); + RUN_COLLISIONS(assert_existent); + + /* Update */ + { + struct uv__fd_info_s info = { 0 }; + info.flags = FD_DIFF + FD_DIFF; + uv__fd_hash_add(0, &info); + } + { + struct uv__fd_info_s info = { 0 }; + ASSERT(uv__fd_hash_get(0, &info)); + ASSERT(info.flags == FD_DIFF + FD_DIFF); + } + { + /* Leave as it was, will be again tested below */ + struct uv__fd_info_s info = { 0 }; + info.flags = FD_DIFF; + uv__fd_hash_add(0, &info); + } + + /* Remove all */ + RUN_HASH(assert_removal); + RUN_COLLISIONS(assert_removal); + + /* Empty table */ + RUN_HASH(assert_nonexistent); + RUN_COLLISIONS(assert_nonexistent); + + return 0; +} + +#else + +typedef int file_has_no_tests; /* ISO C forbids an empty translation unit. */ + +#endif /* ifndef _WIN32 */ diff --git a/test/test-fs-open-flags.c b/test/test-fs-open-flags.c new file mode 100644 index 00000000..fcef2fb0 --- /dev/null +++ b/test/test-fs-open-flags.c @@ -0,0 +1,435 @@ +/* Copyright libuv project 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. + */ + +#ifdef _WIN32 + +#include "uv.h" +#include "task.h" + +#if defined(__unix__) || defined(__POSIX__) || \ + defined(__APPLE__) || defined(__sun) || \ + defined(_AIX) || defined(__MVS__) || \ + defined(__HAIKU__) +# include /* unlink, rmdir */ +#else +# include +# define rmdir _rmdir +# define unlink _unlink +#endif + +static int flags; + +static uv_fs_t close_req; +static uv_fs_t mkdir_req; +static uv_fs_t open_req; +static uv_fs_t read_req; +static uv_fs_t rmdir_req; +static uv_fs_t unlink_req; +static uv_fs_t write_req; + +static char buf[32]; +static uv_buf_t iov; + +/* Opening the same file multiple times quickly can cause uv_fs_open to fail + * with EBUSY, so append an identifier to the file name for each operation */ +static int sid = 0; + +#define FILE_NAME_SIZE 128 +static char absent_file[FILE_NAME_SIZE]; +static char empty_file[FILE_NAME_SIZE]; +static char dummy_file[FILE_NAME_SIZE]; +static char empty_dir[] = "empty_dir"; + +static void setup() { + int r; + + /* empty_dir */ + r = uv_fs_rmdir(NULL, &rmdir_req, empty_dir, NULL); + ASSERT(r == 0 || r == UV_ENOENT); + ASSERT(rmdir_req.result == 0 || rmdir_req.result == UV_ENOENT); + uv_fs_req_cleanup(&rmdir_req); + + r = uv_fs_mkdir(NULL, &mkdir_req, empty_dir, 0755, NULL); + ASSERT(r == 0); + ASSERT(mkdir_req.result == 0); + uv_fs_req_cleanup(&mkdir_req); +} + +static void refresh() { + int r; + + /* absent_file */ + sprintf(absent_file, "test_file_%d", sid++); + + r = uv_fs_unlink(NULL, &unlink_req, absent_file, NULL); + ASSERT(r == 0 || r == UV_ENOENT); + ASSERT(unlink_req.result == 0 || unlink_req.result == UV_ENOENT); + uv_fs_req_cleanup(&unlink_req); + + /* empty_file */ + sprintf(empty_file, "test_file_%d", sid++); + + r = uv_fs_open(NULL, &open_req, empty_file, + UV_FS_O_TRUNC | UV_FS_O_CREAT | UV_FS_O_WRONLY, S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(open_req.result >= 0); + uv_fs_req_cleanup(&open_req); + + r = uv_fs_close(NULL, &close_req, open_req.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + /* dummy_file */ + sprintf(dummy_file, "test_file_%d", sid++); + + r = uv_fs_open(NULL, &open_req, dummy_file, + UV_FS_O_TRUNC | UV_FS_O_CREAT | UV_FS_O_WRONLY, S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(open_req.result >= 0); + uv_fs_req_cleanup(&open_req); + + iov = uv_buf_init("a", 1); + r = uv_fs_write(NULL, &write_req, open_req.result, &iov, 1, -1, NULL); + ASSERT(r == 1); + ASSERT(write_req.result == 1); + uv_fs_req_cleanup(&write_req); + + r = uv_fs_close(NULL, &close_req, open_req.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); +} + +static void cleanup() { + unlink(absent_file); + unlink(empty_file); + unlink(dummy_file); +} + +static void openFail(char *file, int error) { + int r; + + refresh(); + + r = uv_fs_open(NULL, &open_req, file, flags, S_IWUSR | S_IRUSR, NULL); + ASSERT(r == error); + ASSERT(open_req.result == error); + uv_fs_req_cleanup(&open_req); + + /* Ensure the first call does not create the file */ + r = uv_fs_open(NULL, &open_req, file, flags, S_IWUSR | S_IRUSR, NULL); + ASSERT(r == error); + ASSERT(open_req.result == error); + uv_fs_req_cleanup(&open_req); + + cleanup(); +} + +static void refreshOpen(char *file) { + int r; + + refresh(); + + r = uv_fs_open(NULL, &open_req, file, flags, S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(open_req.result >= 0); + uv_fs_req_cleanup(&open_req); +} + +static void writeExpect(char *file, char *expected, int size) { + int r; + + refreshOpen(file); + + iov = uv_buf_init("b", 1); + r = uv_fs_write(NULL, &write_req, open_req.result, &iov, 1, -1, NULL); + ASSERT(r == 1); + ASSERT(write_req.result == 1); + uv_fs_req_cleanup(&write_req); + + iov = uv_buf_init("c", 1); + r = uv_fs_write(NULL, &write_req, open_req.result, &iov, 1, -1, NULL); + ASSERT(r == 1); + ASSERT(write_req.result == 1); + uv_fs_req_cleanup(&write_req); + + r = uv_fs_close(NULL, &close_req, open_req.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + /* Check contents */ + r = uv_fs_open(NULL, &open_req, file, UV_FS_O_RDONLY, S_IWUSR | S_IRUSR, NULL); + ASSERT(r >= 0); + ASSERT(open_req.result >= 0); + uv_fs_req_cleanup(&open_req); + + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &read_req, open_req.result, &iov, 1, -1, NULL); + ASSERT(r == size); + ASSERT(read_req.result == size); + ASSERT(strncmp(buf, expected, size) == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_close(NULL, &close_req, open_req.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + cleanup(); +} + +static void writeFail(char *file, int error) { + int r; + + refreshOpen(file); + + iov = uv_buf_init("z", 1); + r = uv_fs_write(NULL, &write_req, open_req.result, &iov, 1, -1, NULL); + ASSERT(r == error); + ASSERT(write_req.result == error); + uv_fs_req_cleanup(&write_req); + + iov = uv_buf_init("z", 1); + r = uv_fs_write(NULL, &write_req, open_req.result, &iov, 1, -1, NULL); + ASSERT(r == error); + ASSERT(write_req.result == error); + uv_fs_req_cleanup(&write_req); + + r = uv_fs_close(NULL, &close_req, open_req.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + cleanup(); +} + +static void readExpect(char *file, char *expected, int size) { + int r; + + refreshOpen(file); + + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &read_req, open_req.result, &iov, 1, -1, NULL); + ASSERT(r == size); + ASSERT(read_req.result == size); + ASSERT(strncmp(buf, expected, size) == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_close(NULL, &close_req, open_req.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + cleanup(); +} + +static void readFail(char *file, int error) { + int r; + + refreshOpen(file); + + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &read_req, open_req.result, &iov, 1, -1, NULL); + ASSERT(r == error); + ASSERT(read_req.result == error); + uv_fs_req_cleanup(&read_req); + + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &read_req, open_req.result, &iov, 1, -1, NULL); + ASSERT(r == error); + ASSERT(read_req.result == error); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_close(NULL, &close_req, open_req.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + cleanup(); +} + +static void fs_open_flags(int add_flags) { + /* Follow the order from + * https://github.com/nodejs/node/blob/1a96abe849/lib/internal/fs/utils.js#L329-L354 + */ + + /* r */ + flags = add_flags | UV_FS_O_RDONLY; + openFail(absent_file, UV_ENOENT); + writeFail(empty_file, UV_EPERM); + readExpect(empty_file, "", 0); + writeFail(dummy_file, UV_EPERM); + readExpect(dummy_file, "a", 1); + writeFail(empty_dir, UV_EPERM); + readFail(empty_dir, UV_EISDIR); + + /* rs */ + flags = add_flags | UV_FS_O_RDONLY | UV_FS_O_SYNC; + openFail(absent_file, UV_ENOENT); + writeFail(empty_file, UV_EPERM); + readExpect(empty_file, "", 0); + writeFail(dummy_file, UV_EPERM); + readExpect(dummy_file, "a", 1); + writeFail(empty_dir, UV_EPERM); + readFail(empty_dir, UV_EISDIR); + + /* r+ */ + flags = add_flags | UV_FS_O_RDWR; + openFail(absent_file, UV_ENOENT); + writeExpect(empty_file, "bc", 2); + readExpect(empty_file, "", 0); + writeExpect(dummy_file, "bc", 2); + readExpect(dummy_file, "a", 1); + writeFail(empty_dir, UV_EISDIR); + readFail(empty_dir, UV_EISDIR); + + /* rs+ */ + flags = add_flags | UV_FS_O_RDWR | UV_FS_O_SYNC; + openFail(absent_file, UV_ENOENT); + writeExpect(empty_file, "bc", 2); + readExpect(empty_file, "", 0); + writeExpect(dummy_file, "bc", 2); + readExpect(dummy_file, "a", 1); + writeFail(empty_dir, UV_EISDIR); + readFail(empty_dir, UV_EISDIR); + + /* w */ + flags = add_flags | UV_FS_O_TRUNC | UV_FS_O_CREAT | UV_FS_O_WRONLY; + writeExpect(absent_file, "bc", 2); + readFail(absent_file, UV_EPERM); + writeExpect(empty_file, "bc", 2); + readFail(empty_file, UV_EPERM); + writeExpect(dummy_file, "bc", 2); + readFail(dummy_file, UV_EPERM); + openFail(empty_dir, UV_EISDIR); + + /* wx */ + flags = add_flags | UV_FS_O_TRUNC | UV_FS_O_CREAT | UV_FS_O_WRONLY | + UV_FS_O_EXCL; + writeExpect(absent_file, "bc", 2); + readFail(absent_file, UV_EPERM); + openFail(empty_file, UV_EEXIST); + openFail(dummy_file, UV_EEXIST); + openFail(empty_dir, UV_EEXIST); + + /* w+ */ + flags = add_flags | UV_FS_O_TRUNC | UV_FS_O_CREAT | UV_FS_O_RDWR; + writeExpect(absent_file, "bc", 2); + readExpect(absent_file, "", 0); + writeExpect(empty_file, "bc", 2); + readExpect(empty_file, "", 0); + writeExpect(dummy_file, "bc", 2); + readExpect(dummy_file, "", 0); + openFail(empty_dir, UV_EISDIR); + + /* wx+ */ + flags = add_flags | UV_FS_O_TRUNC | UV_FS_O_CREAT | UV_FS_O_RDWR | + UV_FS_O_EXCL; + writeExpect(absent_file, "bc", 2); + readExpect(absent_file, "", 0); + openFail(empty_file, UV_EEXIST); + openFail(dummy_file, UV_EEXIST); + openFail(empty_dir, UV_EEXIST); + + /* a */ + flags = add_flags | UV_FS_O_APPEND | UV_FS_O_CREAT | UV_FS_O_WRONLY; + writeExpect(absent_file, "bc", 2); + readFail(absent_file, UV_EPERM); + writeExpect(empty_file, "bc", 2); + readFail(empty_file, UV_EPERM); + writeExpect(dummy_file, "abc", 3); + readFail(dummy_file, UV_EPERM); + writeFail(empty_dir, UV_EISDIR); + readFail(empty_dir, UV_EPERM); + + /* ax */ + flags = add_flags | UV_FS_O_APPEND | UV_FS_O_CREAT | UV_FS_O_WRONLY | + UV_FS_O_EXCL; + writeExpect(absent_file, "bc", 2); + readFail(absent_file, UV_EPERM); + openFail(empty_file, UV_EEXIST); + openFail(dummy_file, UV_EEXIST); + openFail(empty_dir, UV_EEXIST); + + /* as */ + flags = add_flags | UV_FS_O_APPEND | UV_FS_O_CREAT | UV_FS_O_WRONLY | + UV_FS_O_SYNC; + writeExpect(absent_file, "bc", 2); + readFail(absent_file, UV_EPERM); + writeExpect(empty_file, "bc", 2); + readFail(empty_file, UV_EPERM); + writeExpect(dummy_file, "abc", 3); + readFail(dummy_file, UV_EPERM); + writeFail(empty_dir, UV_EISDIR); + readFail(empty_dir, UV_EPERM); + + /* a+ */ + flags = add_flags | UV_FS_O_APPEND | UV_FS_O_CREAT | UV_FS_O_RDWR; + writeExpect(absent_file, "bc", 2); + readExpect(absent_file, "", 0); + writeExpect(empty_file, "bc", 2); + readExpect(empty_file, "", 0); + writeExpect(dummy_file, "abc", 3); + readExpect(dummy_file, "a", 1); + writeFail(empty_dir, UV_EISDIR); + readFail(empty_dir, UV_EISDIR); + + /* ax+ */ + flags = add_flags | UV_FS_O_APPEND | UV_FS_O_CREAT | UV_FS_O_RDWR | + UV_FS_O_EXCL; + writeExpect(absent_file, "bc", 2); + readExpect(absent_file, "", 0); + openFail(empty_file, UV_EEXIST); + openFail(dummy_file, UV_EEXIST); + openFail(empty_dir, UV_EEXIST); + + /* as+ */ + flags = add_flags | UV_FS_O_APPEND | UV_FS_O_CREAT | UV_FS_O_RDWR | + UV_FS_O_SYNC; + writeExpect(absent_file, "bc", 2); + readExpect(absent_file, "", 0); + writeExpect(empty_file, "bc", 2); + readExpect(empty_file, "", 0); + writeExpect(dummy_file, "abc", 3); + readExpect(dummy_file, "a", 1); + writeFail(empty_dir, UV_EISDIR); + readFail(empty_dir, UV_EISDIR); +} +TEST_IMPL(fs_open_flags) { + setup(); + + fs_open_flags(0); + fs_open_flags(UV_FS_O_FILEMAP); + + /* Cleanup. */ + rmdir(empty_dir); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#else + +typedef int file_has_no_tests; /* ISO C forbids an empty translation unit. */ + +#endif /* ifndef _WIN32 */ diff --git a/test/test-fs.c b/test/test-fs.c index 2cf8f287..35a992d8 100644 --- a/test/test-fs.c +++ b/test/test-fs.c @@ -847,7 +847,7 @@ TEST_IMPL(fs_file_async) { } -TEST_IMPL(fs_file_sync) { +static void fs_file_sync(int add_flags) { int r; /* Setup. */ @@ -856,8 +856,8 @@ TEST_IMPL(fs_file_sync) { loop = uv_default_loop(); - r = uv_fs_open(loop, &open_req1, "test_file", O_WRONLY | O_CREAT, - S_IWUSR | S_IRUSR, NULL); + r = uv_fs_open(loop, &open_req1, "test_file", + O_WRONLY | O_CREAT | add_flags, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); @@ -873,7 +873,7 @@ TEST_IMPL(fs_file_sync) { ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); - r = uv_fs_open(NULL, &open_req1, "test_file", O_RDWR, 0, NULL); + r = uv_fs_open(NULL, &open_req1, "test_file", O_RDWR | add_flags, 0, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); @@ -900,7 +900,8 @@ TEST_IMPL(fs_file_sync) { ASSERT(rename_req.result == 0); uv_fs_req_cleanup(&rename_req); - r = uv_fs_open(NULL, &open_req1, "test_file2", O_RDONLY, 0, NULL); + r = uv_fs_open(NULL, &open_req1, "test_file2", O_RDONLY | add_flags, 0, + NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); @@ -926,13 +927,17 @@ TEST_IMPL(fs_file_sync) { /* Cleanup */ unlink("test_file"); unlink("test_file2"); +} +TEST_IMPL(fs_file_sync) { + fs_file_sync(0); + fs_file_sync(UV_FS_O_FILEMAP); MAKE_VALGRIND_HAPPY(); return 0; } -TEST_IMPL(fs_file_write_null_buffer) { +static void fs_file_write_null_buffer(int add_flags) { int r; /* Setup. */ @@ -940,8 +945,8 @@ TEST_IMPL(fs_file_write_null_buffer) { loop = uv_default_loop(); - r = uv_fs_open(NULL, &open_req1, "test_file", O_WRONLY | O_CREAT, - S_IWUSR | S_IRUSR, NULL); + r = uv_fs_open(NULL, &open_req1, "test_file", + O_WRONLY | O_CREAT | add_flags, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); @@ -958,6 +963,10 @@ TEST_IMPL(fs_file_write_null_buffer) { uv_fs_req_cleanup(&close_req); unlink("test_file"); +} +TEST_IMPL(fs_file_write_null_buffer) { + fs_file_write_null_buffer(0); + fs_file_write_null_buffer(UV_FS_O_FILEMAP); MAKE_VALGRIND_HAPPY(); return 0; @@ -1470,7 +1479,7 @@ TEST_IMPL(fs_chmod) { uv_run(loop, UV_RUN_DEFAULT); ASSERT(fchmod_cb_count == 1); - close(file); + uv_fs_close(loop, &req, file, NULL); /* * Run the loop just to check we don't have make any extraneous uv_ref() @@ -1513,7 +1522,7 @@ TEST_IMPL(fs_unlink_readonly) { ASSERT(req.result == sizeof(test_buf)); uv_fs_req_cleanup(&req); - close(file); + uv_fs_close(loop, &req, file, NULL); /* Make the file read-only */ r = uv_fs_chmod(NULL, &req, "test_file", 0400, NULL); @@ -1572,7 +1581,7 @@ TEST_IMPL(fs_unlink_archive_readonly) { ASSERT(req.result == sizeof(test_buf)); uv_fs_req_cleanup(&req); - close(file); + uv_fs_close(loop, &req, file, NULL); /* Make the file read-only and clear archive flag */ r = SetFileAttributes("test_file", FILE_ATTRIBUTE_READONLY); @@ -1722,7 +1731,7 @@ TEST_IMPL(fs_link) { ASSERT(req.result == sizeof(test_buf)); uv_fs_req_cleanup(&req); - close(file); + uv_fs_close(loop, &req, file, NULL); /* sync link */ r = uv_fs_link(NULL, &req, "test_file", "test_file_link", NULL); @@ -1764,7 +1773,7 @@ TEST_IMPL(fs_link) { ASSERT(req.result >= 0); ASSERT(strcmp(buf, test_buf) == 0); - close(link); + uv_fs_close(loop, &req, link, NULL); /* * Run the loop just to check we don't have make any extraneous uv_ref() @@ -1871,7 +1880,7 @@ TEST_IMPL(fs_symlink) { ASSERT(req.result == sizeof(test_buf)); uv_fs_req_cleanup(&req); - close(file); + uv_fs_close(loop, &req, file, NULL); /* sync symlink */ r = uv_fs_symlink(NULL, &req, "test_file", "test_file_symlink", 0, NULL); @@ -1909,7 +1918,7 @@ TEST_IMPL(fs_symlink) { ASSERT(req.result >= 0); ASSERT(strcmp(buf, test_buf) == 0); - close(link); + uv_fs_close(loop, &req, link, NULL); r = uv_fs_symlink(NULL, &req, @@ -1971,7 +1980,7 @@ TEST_IMPL(fs_symlink) { ASSERT(req.result >= 0); ASSERT(strcmp(buf, test_buf) == 0); - close(link); + uv_fs_close(loop, &req, link, NULL); r = uv_fs_symlink(NULL, &req, @@ -2293,7 +2302,7 @@ TEST_IMPL(fs_utime) { ASSERT(r >= 0); ASSERT(req.result >= 0); uv_fs_req_cleanup(&req); - close(r); + uv_fs_close(loop, &req, r, NULL); atime = mtime = 400497753; /* 1982-09-10 11:22:33 */ @@ -2388,7 +2397,7 @@ TEST_IMPL(fs_futime) { ASSERT(r >= 0); ASSERT(req.result >= 0); uv_fs_req_cleanup(&req); - close(r); + uv_fs_close(loop, &req, r, NULL); atime = mtime = 400497753; /* 1982-09-10 11:22:33 */ @@ -2583,7 +2592,7 @@ TEST_IMPL(fs_open_dir) { } -TEST_IMPL(fs_file_open_append) { +static void fs_file_open_append(int add_flags) { int r; /* Setup. */ @@ -2591,8 +2600,8 @@ TEST_IMPL(fs_file_open_append) { loop = uv_default_loop(); - r = uv_fs_open(NULL, &open_req1, "test_file", O_WRONLY | O_CREAT, - S_IWUSR | S_IRUSR, NULL); + r = uv_fs_open(NULL, &open_req1, "test_file", + O_WRONLY | O_CREAT | add_flags, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); @@ -2608,7 +2617,8 @@ TEST_IMPL(fs_file_open_append) { ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); - r = uv_fs_open(NULL, &open_req1, "test_file", O_RDWR | O_APPEND, 0, NULL); + r = uv_fs_open(NULL, &open_req1, "test_file", + O_RDWR | O_APPEND | add_flags, 0, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); @@ -2624,7 +2634,8 @@ TEST_IMPL(fs_file_open_append) { ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); - r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY, S_IRUSR, NULL); + r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY | add_flags, + S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); @@ -2646,6 +2657,10 @@ TEST_IMPL(fs_file_open_append) { /* Cleanup */ unlink("test_file"); +} +TEST_IMPL(fs_file_open_append) { + fs_file_open_append(0); + fs_file_open_append(UV_FS_O_FILEMAP); MAKE_VALGRIND_HAPPY(); return 0; @@ -2721,13 +2736,13 @@ TEST_IMPL(fs_rename_to_existing_file) { } -TEST_IMPL(fs_read_bufs) { +static void fs_read_bufs(int add_flags) { char scratch[768]; uv_buf_t bufs[4]; ASSERT(0 <= uv_fs_open(NULL, &open_req1, "test/fixtures/lorem_ipsum.txt", - O_RDONLY, 0, NULL)); + O_RDONLY | add_flags, 0, NULL)); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); @@ -2769,13 +2784,17 @@ TEST_IMPL(fs_read_bufs) { ASSERT(0 == uv_fs_close(NULL, &close_req, open_req1.result, NULL)); ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); +} +TEST_IMPL(fs_read_bufs) { + fs_read_bufs(0); + fs_read_bufs(UV_FS_O_FILEMAP); MAKE_VALGRIND_HAPPY(); return 0; } -TEST_IMPL(fs_read_file_eof) { +static void fs_read_file_eof(int add_flags) { #if defined(__CYGWIN__) || defined(__MSYS__) RETURN_SKIP("Cygwin pread at EOF may (incorrectly) return data!"); #endif @@ -2786,8 +2805,8 @@ TEST_IMPL(fs_read_file_eof) { loop = uv_default_loop(); - r = uv_fs_open(NULL, &open_req1, "test_file", O_WRONLY | O_CREAT, - S_IWUSR | S_IRUSR, NULL); + r = uv_fs_open(NULL, &open_req1, "test_file", + O_WRONLY | O_CREAT | add_flags, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); @@ -2803,7 +2822,8 @@ TEST_IMPL(fs_read_file_eof) { ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); - r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY, 0, NULL); + r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY | add_flags, 0, + NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); @@ -2830,13 +2850,17 @@ TEST_IMPL(fs_read_file_eof) { /* Cleanup */ unlink("test_file"); +} +TEST_IMPL(fs_read_file_eof) { + fs_read_file_eof(0); + fs_read_file_eof(UV_FS_O_FILEMAP); MAKE_VALGRIND_HAPPY(); return 0; } -TEST_IMPL(fs_write_multiple_bufs) { +static void fs_write_multiple_bufs(int add_flags) { uv_buf_t iovs[2]; int r; @@ -2845,8 +2869,8 @@ TEST_IMPL(fs_write_multiple_bufs) { loop = uv_default_loop(); - r = uv_fs_open(NULL, &open_req1, "test_file", O_WRONLY | O_CREAT, - S_IWUSR | S_IRUSR, NULL); + r = uv_fs_open(NULL, &open_req1, "test_file", + O_WRONLY | O_CREAT | add_flags, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); @@ -2863,7 +2887,8 @@ TEST_IMPL(fs_write_multiple_bufs) { ASSERT(close_req.result == 0); uv_fs_req_cleanup(&close_req); - r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY, 0, NULL); + r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY | add_flags, 0, + NULL); ASSERT(r >= 0); ASSERT(open_req1.result >= 0); uv_fs_req_cleanup(&open_req1); @@ -2919,13 +2944,17 @@ TEST_IMPL(fs_write_multiple_bufs) { /* Cleanup */ unlink("test_file"); +} +TEST_IMPL(fs_write_multiple_bufs) { + fs_write_multiple_bufs(0); + fs_write_multiple_bufs(UV_FS_O_FILEMAP); MAKE_VALGRIND_HAPPY(); return 0; } -TEST_IMPL(fs_write_alotof_bufs) { +static void fs_write_alotof_bufs(int add_flags) { size_t iovcount; size_t iovmax; uv_buf_t* iovs; @@ -2947,7 +2976,7 @@ TEST_IMPL(fs_write_alotof_bufs) { r = uv_fs_open(NULL, &open_req1, "test_file", - O_RDWR | O_CREAT, + O_RDWR | O_CREAT | add_flags, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); @@ -2976,7 +3005,17 @@ TEST_IMPL(fs_write_alotof_bufs) { iovs[index] = uv_buf_init(buffer + index * sizeof(test_buf), sizeof(test_buf)); - ASSERT(lseek(open_req1.result, 0, SEEK_SET) == 0); + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + ASSERT(close_req.result == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY | add_flags, 0, + NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + r = uv_fs_read(NULL, &read_req, open_req1.result, iovs, iovcount, -1, NULL); if (iovcount > iovmax) iovcount = iovmax; @@ -3012,13 +3051,17 @@ TEST_IMPL(fs_write_alotof_bufs) { /* Cleanup */ unlink("test_file"); free(iovs); +} +TEST_IMPL(fs_write_alotof_bufs) { + fs_write_alotof_bufs(0); + fs_write_alotof_bufs(UV_FS_O_FILEMAP); MAKE_VALGRIND_HAPPY(); return 0; } -TEST_IMPL(fs_write_alotof_bufs_with_offset) { +static void fs_write_alotof_bufs_with_offset(int add_flags) { size_t iovcount; size_t iovmax; uv_buf_t* iovs; @@ -3045,7 +3088,7 @@ TEST_IMPL(fs_write_alotof_bufs_with_offset) { r = uv_fs_open(NULL, &open_req1, "test_file", - O_RDWR | O_CREAT, + O_RDWR | O_CREAT | add_flags, S_IWUSR | S_IRUSR, NULL); ASSERT(r >= 0); @@ -3124,6 +3167,10 @@ TEST_IMPL(fs_write_alotof_bufs_with_offset) { /* Cleanup */ unlink("test_file"); free(iovs); +} +TEST_IMPL(fs_write_alotof_bufs_with_offset) { + fs_write_alotof_bufs_with_offset(0); + fs_write_alotof_bufs_with_offset(UV_FS_O_FILEMAP); MAKE_VALGRIND_HAPPY(); return 0; @@ -3539,6 +3586,146 @@ TEST_IMPL(fs_file_pos_after_op_with_offset) { return 0; } +#ifdef _WIN32 +static void fs_file_pos_common() { + int r; + + iov = uv_buf_init("abc", 3); + r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r == 3); + uv_fs_req_cleanup(&write_req); + + /* Read with offset should not change the position */ + iov = uv_buf_init(buf, 1); + r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, 1, NULL); + ASSERT(r == 1); + ASSERT(buf[0] == 'b'); + uv_fs_req_cleanup(&read_req); + + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&read_req); + + /* Write without offset should change the position */ + iov = uv_buf_init("d", 1); + r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r == 1); + uv_fs_req_cleanup(&write_req); + + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&read_req); +} + +static void fs_file_pos_close_check(const char *contents, int size) { + int r; + + /* Close */ + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + + /* Confirm file contents */ + r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY, 0, NULL); + ASSERT(r >= 0); + ASSERT(open_req1.result >= 0); + uv_fs_req_cleanup(&open_req1); + + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r == size); + ASSERT(strncmp(buf, contents, size) == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + + /* Cleanup */ + unlink("test_file"); +} + +static void fs_file_pos_write(int add_flags) { + int r; + + /* Setup. */ + unlink("test_file"); + + r = uv_fs_open(NULL, + &open_req1, + "test_file", + O_TRUNC | O_CREAT | O_RDWR | add_flags, + S_IWUSR | S_IRUSR, + NULL); + ASSERT(r > 0); + uv_fs_req_cleanup(&open_req1); + + fs_file_pos_common(); + + /* Write with offset should not change the position */ + iov = uv_buf_init("e", 1); + r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, 1, NULL); + ASSERT(r == 1); + uv_fs_req_cleanup(&write_req); + + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&read_req); + + fs_file_pos_close_check("aecd", 4); +} +TEST_IMPL(fs_file_pos_write) { + fs_file_pos_write(0); + fs_file_pos_write(UV_FS_O_FILEMAP); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +static void fs_file_pos_append(int add_flags) { + int r; + + /* Setup. */ + unlink("test_file"); + + r = uv_fs_open(NULL, + &open_req1, + "test_file", + O_APPEND | O_CREAT | O_RDWR | add_flags, + S_IWUSR | S_IRUSR, + NULL); + ASSERT(r > 0); + uv_fs_req_cleanup(&open_req1); + + fs_file_pos_common(); + + /* Write with offset appends (ignoring offset) + * but does not change the position */ + iov = uv_buf_init("e", 1); + r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, 1, NULL); + ASSERT(r == 1); + uv_fs_req_cleanup(&write_req); + + iov = uv_buf_init(buf, sizeof(buf)); + r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); + ASSERT(r == 1); + ASSERT(buf[0] == 'e'); + uv_fs_req_cleanup(&read_req); + + fs_file_pos_close_check("abcde", 5); +} +TEST_IMPL(fs_file_pos_append) { + fs_file_pos_append(0); + fs_file_pos_append(UV_FS_O_FILEMAP); + + MAKE_VALGRIND_HAPPY(); + return 0; +} +#endif + TEST_IMPL(fs_null_req) { /* Verify that all fs functions return UV_EINVAL when the request is NULL. */ int r; diff --git a/test/test-list.h b/test/test-list.h index a48f6f38..3c5f21b9 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -321,6 +321,10 @@ TEST_DECLARE (fs_symlink_dir) #ifdef _WIN32 TEST_DECLARE (fs_symlink_junction) TEST_DECLARE (fs_non_symlink_reparse_point) +TEST_DECLARE (fs_open_flags) +#endif +#if defined(_WIN32) && !defined(USING_UV_SHARED) +TEST_DECLARE (fs_fd_hash) #endif TEST_DECLARE (fs_utime) TEST_DECLARE (fs_futime) @@ -370,6 +374,8 @@ TEST_DECLARE (fs_file_pos_after_op_with_offset) TEST_DECLARE (fs_null_req) TEST_DECLARE (fs_read_dir) #ifdef _WIN32 +TEST_DECLARE (fs_file_pos_write) +TEST_DECLARE (fs_file_pos_append) TEST_DECLARE (fs_exclusive_sharing_mode) TEST_DECLARE (fs_file_flag_no_buffering) TEST_DECLARE (fs_open_readonly_acl) @@ -912,6 +918,10 @@ TASK_LIST_START #ifdef _WIN32 TEST_ENTRY (fs_symlink_junction) TEST_ENTRY (fs_non_symlink_reparse_point) + TEST_ENTRY (fs_open_flags) +#endif +#if defined(_WIN32) && !defined(USING_UV_SHARED) + TEST_ENTRY (fs_fd_hash) #endif TEST_ENTRY (fs_stat_missing_path) TEST_ENTRY (fs_read_bufs) @@ -957,6 +967,8 @@ TASK_LIST_START TEST_ENTRY (fs_null_req) TEST_ENTRY (fs_read_dir) #ifdef _WIN32 + TEST_ENTRY (fs_file_pos_write) + TEST_ENTRY (fs_file_pos_append) TEST_ENTRY (fs_exclusive_sharing_mode) TEST_ENTRY (fs_file_flag_no_buffering) TEST_ENTRY (fs_open_readonly_acl) diff --git a/test/test.gyp b/test/test.gyp index a4083e91..6158a2b8 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -35,6 +35,8 @@ 'test-fs-readdir.c', 'test-fs-copyfile.c', 'test-fs-event.c', + 'test-fs-fd-hash.c', + 'test-fs-open-flags.c', 'test-fs-poll.c', 'test-getters-setters.c', 'test-get-currentexe.c',