diff --git a/include/uv-private/uv-win.h b/include/uv-private/uv-win.h index 3a80f55a..35c9c194 100644 --- a/include/uv-private/uv-win.h +++ b/include/uv-private/uv-win.h @@ -247,6 +247,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); #define UV_FS_PRIVATE_FIELDS \ int flags; \ + int last_error; \ struct _stat stat; \ void* arg0; \ union { \ diff --git a/include/uv.h b/include/uv.h index 4f4c7ce7..894c98fb 100644 --- a/include/uv.h +++ b/include/uv.h @@ -948,8 +948,14 @@ int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb); +/* + * This flag can be used with uv_fs_symlink on Windows + * to specify whether path argument points to a directory. + */ +#define UV_FS_SYMLINK_DIR 0x0001 + int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, - const char* new_path, uv_fs_cb cb); + const char* new_path, int flags, uv_fs_cb cb); int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); diff --git a/src/unix/fs.c b/src/unix/fs.c index fc4edbb2..5598ac0f 100644 --- a/src/unix/fs.c +++ b/src/unix/fs.c @@ -490,7 +490,7 @@ int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, - const char* new_path, uv_fs_cb cb) { + const char* new_path, int flags, uv_fs_cb cb) { WRAP_EIO(UV_FS_SYMLINK, eio_symlink, symlink, ARGS2(path, new_path)) } diff --git a/src/win/fs.c b/src/win/fs.c index 63d569b4..29b703e9 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #define UV_FS_FREE_ARG1 0x0004 #define UV_FS_FREE_PTR 0x0008 #define UV_FS_CLEANEDUP 0x0010 +#define UV_FS_LAST_ERROR_SET 0x0020 #define STRDUP_ARG(req, i) \ req->arg##i = (void*)strdup((const char*)req->arg##i); \ @@ -70,6 +72,16 @@ uv_ref((loop)); +#define SET_UV_LAST_ERROR_FROM_REQ(req) \ + if (req->flags & UV_FS_LAST_ERROR_SET) { \ + uv_set_sys_error(req->loop, req->last_error); \ + } + +#define SET_REQ_LAST_ERROR(req, error) \ + req->last_error = error; \ + req->flags |= UV_FS_LAST_ERROR_SET; + + void uv_fs_init() { _fmode = _O_BINARY; } @@ -86,6 +98,7 @@ static void uv_fs_req_init_async(uv_loop_t* loop, uv_fs_t* req, req->result = 0; req->ptr = NULL; req->errorno = 0; + req->last_error = 0; memset(&req->overlapped, 0, sizeof(req->overlapped)); } @@ -187,6 +200,7 @@ void fs__readdir(uv_fs_t* req, const char* path, int flags) { if(dir == INVALID_HANDLE_VALUE) { result = -1; + SET_REQ_LAST_ERROR(req, GetLastError()); goto done; } @@ -267,6 +281,9 @@ void fs__rename(uv_fs_t* req, const char* path, const char* new_path) { void fs__fsync(uv_fs_t* req, uv_file file) { int result = FlushFileBuffers((HANDLE)_get_osfhandle(file)) ? 0 : -1; + if (result == -1) { + SET_REQ_LAST_ERROR(req, GetLastError()); + } SET_REQ_RESULT(req, result); } @@ -383,6 +400,50 @@ void fs__futime(uv_fs_t* req, uv_file file, double atime, double mtime) { } +void fs__link(uv_fs_t* req, const char* path, const char* new_path) { + int result = CreateHardLinkA(new_path, path, NULL) ? 0 : -1; + if (result == -1) { + SET_REQ_LAST_ERROR(req, GetLastError()); + } + SET_REQ_RESULT(req, result); +} + + +void fs__symlink(uv_fs_t* req, const char* path, const char* new_path, + int flags) { + int result; + if (pCreateSymbolicLinkA) { + result = pCreateSymbolicLinkA(new_path, + path, + flags & UV_FS_SYMLINK_DIR ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) ? 0 : -1; + if (result == -1) { + SET_REQ_LAST_ERROR(req, GetLastError()); + } + } else { + result = -1; + errno = ENOTSUP; + } + + SET_REQ_RESULT(req, result); +} + + +void fs__readlink(uv_fs_t* req, const char* path) { + int result = -1; + assert(0 && "implement me"); + + /* TODO: the link path must be returned in a req->ptr buffer, + * which need to be alloce'd here. + * Just do this (it'll take care of freeing the buffer). + * req->ptr = malloc(...); + * req->flags |= UV_FS_FREE_PTR; + * Also result needs to contain the length of the string. + */ + + SET_REQ_RESULT(req, result); +} + + void fs__nop(uv_fs_t* req) { req->result = 0; } @@ -464,6 +525,15 @@ static DWORD WINAPI uv_fs_thread_proc(void* parameter) { case UV_FS_FUTIME: fs__futime(req, (uv_file)req->arg0, req->arg4, req->arg5); break; + case UV_FS_LINK: + fs__link(req, (const char*)req->arg0, (const char*)req->arg1); + break; + case UV_FS_SYMLINK: + fs__symlink(req, (const char*)req->arg0, (const char*)req->arg1, (int)req->arg2); + break; + case UV_FS_READLINK: + fs__readlink(req, (const char*)req->arg0); + break; case UV_FS_CHOWN: case UV_FS_FCHOWN: fs__nop(req); @@ -488,6 +558,7 @@ int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, } else { uv_fs_req_init_sync(loop, req, UV_FS_OPEN); fs__open(req, path, flags, mode); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -502,6 +573,7 @@ int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { } else { uv_fs_req_init_sync(loop, req, UV_FS_CLOSE); fs__close(req, file); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -517,6 +589,7 @@ int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, } else { uv_fs_req_init_sync(loop, req, UV_FS_READ); fs__read(req, file, buf, length, offset); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -532,6 +605,7 @@ int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, } else { uv_fs_req_init_sync(loop, req, UV_FS_WRITE); fs__write(req, file, buf, length, offset); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -548,6 +622,7 @@ int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, } else { uv_fs_req_init_sync(loop, req, UV_FS_UNLINK); fs__unlink(req, path); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -564,6 +639,7 @@ int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, } else { uv_fs_req_init_sync(loop, req, UV_FS_MKDIR); fs__mkdir(req, path, mode); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -579,6 +655,7 @@ int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { } else { uv_fs_req_init_sync(loop, req, UV_FS_RMDIR); fs__rmdir(req, path); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -595,6 +672,7 @@ int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, } else { uv_fs_req_init_sync(loop, req, UV_FS_READDIR); fs__readdir(req, path, flags); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -603,22 +681,54 @@ int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) { - assert(0 && "implement me"); - return -1; + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_LINK, cb); + WRAP_REQ_ARGS2(req, path, new_path); + STRDUP_ARG(req, 0); + STRDUP_ARG(req, 1); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_LINK); + fs__link(req, path, new_path); + SET_UV_LAST_ERROR_FROM_REQ(req); + } + + return 0; } int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, - const char* new_path, uv_fs_cb cb) { - assert(0 && "implement me"); - return -1; + const char* new_path, int flags, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_SYMLINK, cb); + WRAP_REQ_ARGS3(req, path, new_path, flags); + STRDUP_ARG(req, 0); + STRDUP_ARG(req, 1); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_SYMLINK); + fs__symlink(req, path, new_path, flags); + SET_UV_LAST_ERROR_FROM_REQ(req); + } + + return 0; } int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { - assert(0 && "implement me"); - return -1; + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_READLINK, cb); + WRAP_REQ_ARGS1(req, path); + STRDUP_ARG(req, 0); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_READLINK); + fs__readlink(req, path); + SET_UV_LAST_ERROR_FROM_REQ(req); + } + + return 0; } @@ -632,6 +742,7 @@ int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, int uid, } else { uv_fs_req_init_sync(loop, req, UV_FS_CHOWN); fs__nop(req); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -647,6 +758,7 @@ int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, int uid, } else { uv_fs_req_init_sync(loop, req, UV_FS_FCHOWN); fs__nop(req); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -684,6 +796,7 @@ int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { if (path2) { free(path2); } + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -722,6 +835,7 @@ int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { if (path2) { free(path2); } + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -736,6 +850,7 @@ int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { } else { uv_fs_req_init_sync(loop, req, UV_FS_FSTAT); fs__fstat(req, file); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -753,6 +868,7 @@ int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, } else { uv_fs_req_init_sync(loop, req, UV_FS_RENAME); fs__rename(req, path, new_path); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -767,6 +883,7 @@ int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { } else { uv_fs_req_init_sync(loop, req, UV_FS_FDATASYNC); fs__fsync(req, file); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -781,6 +898,7 @@ int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { } else { uv_fs_req_init_sync(loop, req, UV_FS_FSYNC); fs__fsync(req, file); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -796,6 +914,7 @@ int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, } else { uv_fs_req_init_sync(loop, req, UV_FS_FTRUNCATE); fs__ftruncate(req, file, offset); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -811,6 +930,7 @@ int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, } else { uv_fs_req_init_sync(loop, req, UV_FS_SENDFILE); fs__sendfile(req, out_fd, in_fd, in_offset, length); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -827,6 +947,7 @@ int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, } else { uv_fs_req_init_sync(loop, req, UV_FS_CHMOD); fs__chmod(req, path, mode); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -842,6 +963,7 @@ int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, } else { uv_fs_req_init_sync(loop, req, UV_FS_FCHMOD); fs__fchmod(req, file, mode); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -860,6 +982,7 @@ int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, } else { uv_fs_req_init_sync(loop, req, UV_FS_UTIME); fs__utime(req, path, atime, mtime); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -877,6 +1000,7 @@ int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, } else { uv_fs_req_init_sync(loop, req, UV_FS_FUTIME); fs__futime(req, file, atime, mtime); + SET_UV_LAST_ERROR_FROM_REQ(req); } return 0; @@ -885,6 +1009,7 @@ int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, void uv_process_fs_req(uv_loop_t* loop, uv_fs_t* req) { assert(req->cb); + SET_UV_LAST_ERROR_FROM_REQ(req); req->cb(req); } diff --git a/src/win/winapi.c b/src/win/winapi.c index bf4d5e38..4a58c145 100644 --- a/src/win/winapi.c +++ b/src/win/winapi.c @@ -31,6 +31,7 @@ sNtQueryInformationFile pNtQueryInformationFile; sNtSetInformationFile pNtSetInformationFile; sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes; +sCreateSymbolicLinkA pCreateSymbolicLinkA; void uv_winapi_init() { @@ -74,4 +75,7 @@ void uv_winapi_init() { pSetFileCompletionNotificationModes = (sSetFileCompletionNotificationModes) GetProcAddress(kernel32_module, "SetFileCompletionNotificationModes"); + + pCreateSymbolicLinkA = (sCreateSymbolicLinkA) + GetProcAddress(kernel32_module, "CreateSymbolicLinkA"); } diff --git a/src/win/winapi.h b/src/win/winapi.h index 9feee8bb..859dd562 100644 --- a/src/win/winapi.h +++ b/src/win/winapi.h @@ -4186,6 +4186,8 @@ typedef NTSTATUS (NTAPI *sNtSetInformationFile) #define FILE_SKIP_COMPLETION_PORT_ON_SUCCESS 0x1 #define FILE_SKIP_SET_EVENT_ON_HANDLE 0x2 +#define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1 + #ifdef __MINGW32__ typedef struct _OVERLAPPED_ENTRY { ULONG_PTR lpCompletionKey; @@ -4207,6 +4209,11 @@ typedef BOOL (WINAPI* sSetFileCompletionNotificationModes) (HANDLE FileHandle, UCHAR Flags); +typedef BOOLEAN (WINAPI* sCreateSymbolicLinkA) + (LPCSTR lpSymlinkFileName, + LPCSTR lpTargetFileName, + DWORD dwFlags); + /* Ntapi function pointers */ extern sRtlNtStatusToDosError pRtlNtStatusToDosError; @@ -4217,5 +4224,6 @@ extern sNtSetInformationFile pNtSetInformationFile; /* Kernel32 function pointers */ extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; extern sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes; +extern sCreateSymbolicLinkA pCreateSymbolicLinkA; #endif /* UV_WIN_WINAPI_H_ */ diff --git a/test/test-fs.c b/test/test-fs.c index 27f91732..9a57174f 100644 --- a/test/test-fs.c +++ b/test/test-fs.c @@ -25,6 +25,7 @@ #include "uv.h" #include "task.h" +#include #include /* memset */ #include #include @@ -62,6 +63,8 @@ static int chmod_cb_count; static int fchmod_cb_count; static int chown_cb_count; static int fchown_cb_count; +static int link_cb_count; +static int symlink_cb_count; static uv_loop_t* loop; @@ -109,6 +112,22 @@ void check_permission(const char* filename, int mode) { } +static void link_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_LINK); + ASSERT(req->result == 0); + link_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void symlink_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_SYMLINK); + ASSERT(req->result == 0); + symlink_cb_count++; + uv_fs_req_cleanup(req); +} + + static void fchmod_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_FCHMOD); ASSERT(req->result == 0); @@ -819,5 +838,190 @@ TEST_IMPL(fs_chown) { /* Cleanup. */ unlink("test_file"); + return 0; +} + + +TEST_IMPL(fs_link) { + int r; + uv_fs_t req; + uv_file file; + uv_file link; + + /* Setup. */ + unlink("test_file"); + unlink("test_file_link"); + unlink("test_file_link2"); + + uv_init(); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r == 0); + ASSERT(req.result != -1); + file = req.result; + uv_fs_req_cleanup(&req); + + r = uv_fs_write(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL); + ASSERT(r == 0); + ASSERT(req.result == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + + close(file); + + /* sync link */ + r = uv_fs_link(loop, &req, "test_file", "test_file_link", NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_open(loop, &req, "test_file_link", O_RDWR, 0, NULL); + ASSERT(r == 0); + ASSERT(req.result != -1); + link = req.result; + uv_fs_req_cleanup(&req); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL); + ASSERT(r == 0); + ASSERT(req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + + close(link); + + /* async link */ + r = uv_fs_link(loop, &req, "test_file", "test_file_link2", link_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(link_cb_count == 1); + + r = uv_fs_open(loop, &req, "test_file_link2", O_RDWR, 0, NULL); + ASSERT(r == 0); + ASSERT(req.result != -1); + link = req.result; + uv_fs_req_cleanup(&req); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL); + ASSERT(r == 0); + ASSERT(req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + + close(link); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop); + + /* Cleanup. */ + unlink("test_file"); + unlink("test_file_link"); + unlink("test_file_link2"); + + return 0; +} + + +TEST_IMPL(fs_symlink) { + int r; + uv_fs_t req; + uv_file file; + uv_file link; + + /* Setup. */ + unlink("test_file"); + unlink("test_file_symlink"); + unlink("test_file_symlink2"); + + uv_init(); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r == 0); + ASSERT(req.result != -1); + file = req.result; + uv_fs_req_cleanup(&req); + + r = uv_fs_write(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL); + ASSERT(r == 0); + ASSERT(req.result == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + + close(file); + + /* sync symlink */ + r = uv_fs_symlink(loop, &req, "test_file", "test_file_symlink", 0, NULL); + ASSERT(r == 0); +#ifdef _WIN32 + if (req.result == -1) { + if (req.errorno == ENOTSUP) { + /* + * Windows doesn't support symlinks on older versions. + * We just pass the test and bail out early if we get ENOTSUP. + */ + return 0; + } else if (uv_last_error(loop).sys_errno_ == ERROR_PRIVILEGE_NOT_HELD) { + /* + * Creating a symlink is only allowed when running elevated. + * We pass the test and bail out early if we get ERROR_PRIVILEGE_NOT_HELD. + */ + return 0; + } + } +#endif + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_open(loop, &req, "test_file_symlink", O_RDWR, 0, NULL); + ASSERT(r == 0); + ASSERT(req.result != -1); + link = req.result; + uv_fs_req_cleanup(&req); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL); + ASSERT(r == 0); + ASSERT(req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + + close(link); + + /* async link */ + r = uv_fs_symlink(loop, &req, "test_file", "test_file_symlink2", 0, symlink_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(symlink_cb_count == 1); + + r = uv_fs_open(loop, &req, "test_file_symlink2", O_RDWR, 0, NULL); + ASSERT(r == 0); + ASSERT(req.result != -1); + link = req.result; + uv_fs_req_cleanup(&req); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL); + ASSERT(r == 0); + ASSERT(req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + + close(link); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop); + + /* Cleanup. */ + unlink("test_file"); + unlink("test_file_symlink"); + unlink("test_file_symlink2"); + return 0; } \ No newline at end of file diff --git a/test/test-list.h b/test/test-list.h index 94cf03c6..fe8f1ed0 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -79,6 +79,8 @@ TEST_DECLARE (fs_async_sendfile) TEST_DECLARE (fs_fstat) TEST_DECLARE (fs_chmod) TEST_DECLARE (fs_chown) +TEST_DECLARE (fs_link) +TEST_DECLARE (fs_symlink) TEST_DECLARE (threadpool_queue_work_simple) #ifdef _WIN32 TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) @@ -185,6 +187,8 @@ TASK_LIST_START TEST_ENTRY (fs_fstat) TEST_ENTRY (fs_chmod) TEST_ENTRY (fs_chown) + TEST_ENTRY (fs_link) + TEST_ENTRY (fs_symlink) TEST_ENTRY (threadpool_queue_work_simple)