static bool create_symlink (const char * dst_path, const char * src_path, bool dst_is_dir) { #ifndef _WIN32 (void) dst_is_dir; return symlink (src_path, dst_path) != -1; #else wchar_t * wide_src_path; wchar_t * wide_dst_path; bool ret = false; wide_src_path = tr_win32_utf8_to_native (src_path, -1); wide_dst_path = tr_win32_utf8_to_native (dst_path, -1); ret = CreateSymbolicLinkW (wide_dst_path, wide_src_path, dst_is_dir ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0); tr_free (wide_dst_path); tr_free (wide_src_path); return ret; #endif }
bool tr_sys_path_is_same (const char * path1, const char * path2, tr_error ** error) { bool ret = false; wchar_t * wide_path1 = NULL; wchar_t * wide_path2 = NULL; HANDLE handle1 = INVALID_HANDLE_VALUE; HANDLE handle2 = INVALID_HANDLE_VALUE; BY_HANDLE_FILE_INFORMATION fi1, fi2; assert (path1 != NULL); assert (path2 != NULL); wide_path1 = tr_win32_utf8_to_native (path1, -1); if (wide_path1 == NULL) goto fail; wide_path2 = tr_win32_utf8_to_native (path2, -1); if (wide_path2 == NULL) goto fail; handle1 = CreateFileW (wide_path1, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle1 == INVALID_HANDLE_VALUE) goto fail; handle2 = CreateFileW (wide_path2, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle2 == INVALID_HANDLE_VALUE) goto fail; /* TODO: Use GetFileInformationByHandleEx on >= Server 2012 */ if (!GetFileInformationByHandle (handle1, &fi1) || !GetFileInformationByHandle (handle2, &fi2)) goto fail; ret = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber && fi1.nFileIndexHigh == fi2.nFileIndexHigh && fi1.nFileIndexLow == fi2.nFileIndexLow; goto cleanup; fail: set_system_error_if_file_found (error, GetLastError ()); cleanup: CloseHandle (handle2); CloseHandle (handle1); tr_free (wide_path2); tr_free (wide_path1); return ret; }
bool tr_sys_path_remove (const char * path, tr_error ** error) { bool ret = false; wchar_t * wide_path; assert (path != NULL); wide_path = tr_win32_utf8_to_native (path, -1); if (wide_path != NULL) { const DWORD attributes = GetFileAttributesW (wide_path); if (attributes != INVALID_FILE_ATTRIBUTES) { if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) ret = RemoveDirectoryW (wide_path); else ret = DeleteFileW (wide_path); } } if (!ret) set_system_error (error, GetLastError ()); tr_free (wide_path); return ret; }
bool tr_sys_path_rename (const char * src_path, const char * dst_path, tr_error ** error) { bool ret = false; wchar_t * wide_src_path; wchar_t * wide_dst_path; assert (src_path != NULL); assert (dst_path != NULL); wide_src_path = tr_win32_utf8_to_native (src_path, -1); wide_dst_path = tr_win32_utf8_to_native (dst_path, -1); if (wide_src_path != NULL && wide_dst_path != NULL) { DWORD flags = MOVEFILE_REPLACE_EXISTING; DWORD attributes; attributes = GetFileAttributesW (wide_src_path); if (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { flags = 0; } else { attributes = GetFileAttributesW (wide_dst_path); if (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) flags = 0; } ret = MoveFileExW (wide_src_path, wide_dst_path, flags); } if (!ret) set_system_error (error, GetLastError ()); tr_free (wide_dst_path); tr_free (wide_src_path); return ret; }
bool tr_sys_path_get_info (const char * path, int flags, tr_sys_path_info * info, tr_error ** error) { bool ret = false; wchar_t * wide_path; assert (path != NULL); assert (info != NULL); wide_path = tr_win32_utf8_to_native (path, -1); if ((flags & TR_SYS_PATH_NO_FOLLOW) == 0) { HANDLE handle = INVALID_HANDLE_VALUE; if (wide_path != NULL) handle = CreateFileW (wide_path, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle != INVALID_HANDLE_VALUE) { tr_error * my_error = NULL; ret = get_file_info (handle, info, &my_error); if (!ret) tr_error_propagate (error, &my_error); CloseHandle (handle); } else { set_system_error (error, GetLastError ()); } } else { WIN32_FILE_ATTRIBUTE_DATA attributes; if (wide_path != NULL) ret = GetFileAttributesExW (wide_path, GetFileExInfoStandard, &attributes); if (ret) stat_to_sys_path_info (attributes.dwFileAttributes, attributes.nFileSizeLow, attributes.nFileSizeHigh, &attributes.ftLastWriteTime, info); else set_system_error (error, GetLastError ()); } tr_free (wide_path); return ret; }
static bool create_hardlink (const char * dst_path, const char * src_path) { #ifndef _WIN32 return link (src_path, dst_path) != -1; #else wchar_t * wide_src_path = tr_win32_utf8_to_native (src_path, -1); wchar_t * wide_dst_path = tr_win32_utf8_to_native (dst_path, -1); bool ret = CreateHardLinkW (wide_dst_path, wide_src_path, NULL); tr_free (wide_dst_path); tr_free (wide_src_path); return ret; #endif }
char * tr_sys_path_resolve (const char * path, tr_error ** error) { char * ret = NULL; wchar_t * wide_path; wchar_t * wide_ret = NULL; HANDLE handle; DWORD wide_ret_size; assert (path != NULL); wide_path = tr_win32_utf8_to_native (path, -1); if (wide_path == NULL) goto fail; handle = CreateFileW (wide_path, FILE_READ_EA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle == INVALID_HANDLE_VALUE) goto fail; wide_ret_size = GetFinalPathNameByHandleW (handle, NULL, 0, 0); if (wide_ret_size == 0) goto fail; wide_ret = tr_new (wchar_t, wide_ret_size); if (GetFinalPathNameByHandleW (handle, wide_ret, wide_ret_size, 0) != wide_ret_size - 1) goto fail; /* Resolved path always begins with "\\?\", so skip those first four chars. */ ret = tr_win32_native_to_utf8 (wide_ret + 4, -1); if (ret != NULL) goto cleanup; fail: set_system_error (error, GetLastError ()); tr_free (ret); ret = NULL; cleanup: tr_free (wide_ret); tr_free (wide_path); if (handle != INVALID_HANDLE_VALUE) CloseHandle (handle); return ret; }
bool tr_sys_path_exists (const char * path, tr_error ** error) { bool ret = false; wchar_t * wide_path; HANDLE handle = INVALID_HANDLE_VALUE; assert (path != NULL); wide_path = tr_win32_utf8_to_native (path, -1); if (wide_path != NULL) { DWORD attributes = GetFileAttributesW (wide_path); if (attributes != INVALID_FILE_ATTRIBUTES) { if (attributes & FILE_ATTRIBUTE_REPARSE_POINT) { handle = CreateFileW (wide_path, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); ret = handle != INVALID_HANDLE_VALUE; } else { ret = true; } } } if (!ret) set_system_error_if_file_found (error, GetLastError ()); if (handle != INVALID_HANDLE_VALUE) CloseHandle (handle); tr_free (wide_path); return ret; }
tr_watchdir_backend * tr_watchdir_win32_new (tr_watchdir_t handle) { const char * const path = tr_watchdir_get_path (handle); wchar_t * wide_path; tr_watchdir_win32 * backend; backend = tr_new0 (tr_watchdir_win32, 1); backend->base.free_func = &tr_watchdir_win32_free; backend->fd = INVALID_HANDLE_VALUE; backend->notify_pipe[0] = backend->notify_pipe[1] = TR_BAD_SOCKET; if ((wide_path = tr_win32_utf8_to_native (path, -1)) == NULL) { log_error ("Failed to convert \"%s\" to native path", path); goto fail; } if ((backend->fd = CreateFileW (wide_path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL)) == INVALID_HANDLE_VALUE) { log_error ("Failed to open directory \"%s\"", path); goto fail; } tr_free (wide_path); wide_path = NULL; backend->overlapped.Pointer = handle; if (!ReadDirectoryChangesW (backend->fd, backend->buffer, sizeof (backend->buffer), FALSE, WIN32_WATCH_MASK, NULL, &backend->overlapped, NULL)) { log_error ("Failed to read directory changes"); goto fail; } if (evutil_socketpair (AF_INET, SOCK_STREAM, 0, backend->notify_pipe) == -1) { log_error ("Failed to create notify pipe: %s", tr_strerror (errno)); goto fail; } if ((backend->event = bufferevent_socket_new (tr_watchdir_get_event_base (handle), backend->notify_pipe[0], 0)) == NULL) { log_error ("Failed to create event buffer: %s", tr_strerror (errno)); goto fail; } bufferevent_setwatermark (backend->event, EV_READ, sizeof (FILE_NOTIFY_INFORMATION), 0); bufferevent_setcb (backend->event, &tr_watchdir_win32_on_event, NULL, NULL, handle); bufferevent_enable (backend->event, EV_READ); if ((backend->thread = (HANDLE) _beginthreadex (NULL, 0, &tr_watchdir_win32_thread, handle, 0, NULL)) == NULL) { log_error ("Failed to create thread"); goto fail; } /* Perform an initial scan on the directory */ if (event_base_once (tr_watchdir_get_event_base (handle), -1, EV_TIMEOUT, &tr_watchdir_win32_on_first_scan, handle, NULL) == -1) log_error ("Failed to perform initial scan: %s", tr_strerror (errno)); return BACKEND_DOWNCAST (backend); fail: tr_watchdir_win32_free (BACKEND_DOWNCAST (backend)); tr_free (wide_path); return NULL; }