diff --git a/channels/drive/client/drive_file.c b/channels/drive/client/drive_file.c index 7aec1c280..f1ad8bfc6 100644 --- a/channels/drive/client/drive_file.c +++ b/channels/drive/client/drive_file.c @@ -7,6 +7,8 @@ * Copyright 2012 Gerald Richter * Copyright 2015 Thincast Technologies GmbH * Copyright 2015 DI (FH) Martin Haimberger + * Copyright 2016 Inuvika Inc. + * Copyright 2016 David PHAM-VAN * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,6 +61,8 @@ #ifdef _WIN32 #pragma comment(lib, "Shlwapi.lib") #include +#else +#include #endif #include "drive_file.h" @@ -507,18 +511,23 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN { char* s = NULL; mode_t m; - UINT64 size; + INT64 size; int status; char* fullpath; - struct STAT st; -#if defined(__linux__) && !defined(ANDROID) || defined(sun) - struct timespec tv[2]; -#else - struct timeval tv[2]; -#endif - UINT64 LastWriteTime; + ULARGE_INTEGER liCreationTime; + ULARGE_INTEGER liLastAccessTime; + ULARGE_INTEGER liLastWriteTime; + ULARGE_INTEGER liChangeTime; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + FILETIME* pftCreationTime = NULL; + FILETIME* pftLastAccessTime = NULL; + FILETIME* pftLastWriteTime = NULL; UINT32 FileAttributes; UINT32 FileNameLength; + HANDLE hFd; + LARGE_INTEGER liSize; m = 0; @@ -526,55 +535,73 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN { case FileBasicInformation: /* http://msdn.microsoft.com/en-us/library/cc232094.aspx */ - Stream_Seek_UINT64(input); /* CreationTime */ - Stream_Seek_UINT64(input); /* LastAccessTime */ - Stream_Read_UINT64(input, LastWriteTime); - Stream_Seek_UINT64(input); /* ChangeTime */ + Stream_Read_UINT64(input, liCreationTime.QuadPart); + Stream_Read_UINT64(input, liLastAccessTime.QuadPart); + Stream_Read_UINT64(input, liLastWriteTime.QuadPart); + Stream_Read_UINT64(input, liChangeTime.QuadPart); Stream_Read_UINT32(input, FileAttributes); - if (FSTAT(file->fd, &st) != 0) + if (!PathFileExistsA(file->fullpath)) return FALSE; - - tv[0].tv_sec = st.st_atime; - tv[1].tv_sec = (LastWriteTime > 0 ? FILE_TIME_RDP_TO_SYSTEM(LastWriteTime) : st.st_mtime); -#ifndef WIN32 - /* TODO on win32 */ -#ifdef ANDROID - tv[0].tv_usec = 0; - tv[1].tv_usec = 0; - utimes(file->fullpath, tv); -#elif defined (__linux__) || defined (sun) - tv[0].tv_nsec = 0; - tv[1].tv_nsec = 0; - futimens(file->fd, tv); -#else - tv[0].tv_usec = 0; - tv[1].tv_usec = 0; - futimes(file->fd, tv); -#endif - - if (FileAttributes > 0) + hFd = CreateFileA(file->fullpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFd == INVALID_HANDLE_VALUE) { - m = st.st_mode; - if ((FileAttributes & FILE_ATTRIBUTE_READONLY) == 0) - m |= S_IWUSR; - else - m &= ~S_IWUSR; - if (m != st.st_mode) - fchmod(file->fd, st.st_mode); + WLog_ERR(TAG, "Unable to set file time %s to %d", file->fullpath); + return FALSE; } -#endif + if (liCreationTime.QuadPart != 0) + { + ftCreationTime.dwHighDateTime = liCreationTime.HighPart; + ftCreationTime.dwLowDateTime = liCreationTime.LowPart; + pftCreationTime = &ftCreationTime; + } + if (liLastAccessTime.QuadPart != 0) + { + ftLastAccessTime.dwHighDateTime = liLastAccessTime.HighPart; + ftLastAccessTime.dwLowDateTime = liLastAccessTime.LowPart; + pftLastAccessTime = &ftLastAccessTime; + } + if (liLastWriteTime.QuadPart != 0) + { + ftLastWriteTime.dwHighDateTime = liLastWriteTime.HighPart; + ftLastWriteTime.dwLowDateTime = liLastWriteTime.LowPart; + pftLastWriteTime = &ftLastWriteTime; + } + if (liChangeTime.QuadPart != 0 && liChangeTime.QuadPart > liLastWriteTime.QuadPart) + { + ftLastWriteTime.dwHighDateTime = liChangeTime.HighPart; + ftLastWriteTime.dwLowDateTime = liChangeTime.LowPart; + pftLastWriteTime = &ftLastWriteTime; + } + if (!SetFileTime(hFd, pftCreationTime, pftLastAccessTime, pftLastWriteTime)) + { + WLog_ERR(TAG, "Unable to set file time %s to %d", file->fullpath); + CloseHandle(hFd); + return FALSE; + } + CloseHandle(hFd); break; case FileEndOfFileInformation: /* http://msdn.microsoft.com/en-us/library/cc232067.aspx */ case FileAllocationInformation: /* http://msdn.microsoft.com/en-us/library/cc232076.aspx */ - Stream_Read_UINT64(input, size); -#ifndef _WIN32 - if (ftruncate(file->fd, size) != 0) + Stream_Read_INT64(input, size); + + hFd = CreateFileA(file->fullpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFd == INVALID_HANDLE_VALUE) + { + WLog_ERR(TAG, "Unable to truncate %s to %d", file->fullpath, size); return FALSE; -#endif + } + liSize.QuadPart = size; + if (SetFilePointerEx(hFd, liSize, NULL, FILE_BEGIN) == 0) + { + WLog_ERR(TAG, "Unable to truncate %s to %d", file->fullpath, size); + CloseHandle(hFd); + return FALSE; + } + CloseHandle(hFd); break; case FileDispositionInformation: diff --git a/channels/drive/client/drive_file.h b/channels/drive/client/drive_file.h index b5bd14118..d287eb4ee 100644 --- a/channels/drive/client/drive_file.h +++ b/channels/drive/client/drive_file.h @@ -7,6 +7,8 @@ * Copyright 2012 Gerald Richter * Copyright 2015 Thincast Technologies GmbH * Copyright 2015 DI (FH) Martin Haimberger + * Copyright 2016 Inuvika Inc. + * Copyright 2016 David PHAM-VAN * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -84,8 +86,6 @@ typedef UINT32 mode_t; #define FILE_TIME_SYSTEM_TO_RDP(_t) \ (((UINT64)(_t) + EPOCH_DIFF) * 10000000LL) -#define FILE_TIME_RDP_TO_SYSTEM(_t) \ - (((_t) == 0LL || (_t) == (UINT64)(-1LL)) ? 0 : (time_t)((_t) / 10000000LL - EPOCH_DIFF)) #define FILE_ATTR_SYSTEM_TO_RDP(_f, _st) ( \ (S_ISDIR(_st.st_mode) ? FILE_ATTRIBUTE_DIRECTORY : 0) | \ diff --git a/channels/rdpdr/client/CMakeLists.txt b/channels/rdpdr/client/CMakeLists.txt index 488c174c2..4074eb6f4 100644 --- a/channels/rdpdr/client/CMakeLists.txt +++ b/channels/rdpdr/client/CMakeLists.txt @@ -2,6 +2,8 @@ # FreeRDP cmake build script # # Copyright 2012 Marc-Andre Moreau +# Copyright 2016 Inuvika Inc. +# Copyright 2016 David PHAM-VAN # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,6 +34,10 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE target_link_libraries(${MODULE_NAME} winpr freerdp) +if(APPLE AND (NOT IOS)) + find_library(CORESERVICES_LIBRARY CoreServices) + target_link_libraries(${MODULE_NAME} ${CORESERVICES_LIBRARY}) +endif() set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client") diff --git a/channels/rdpdr/client/rdpdr_main.c b/channels/rdpdr/client/rdpdr_main.c index 87ee6fbdb..d18cc5b1b 100644 --- a/channels/rdpdr/client/rdpdr_main.c +++ b/channels/rdpdr/client/rdpdr_main.c @@ -46,6 +46,15 @@ #include #endif +#ifdef __MACOSX__ +#include +#include +#include +#include +#include +#include +#endif + #ifdef HAVE_UNISTD_H #include #endif @@ -103,6 +112,45 @@ static UINT rdpdr_send_device_list_remove_request(rdpdrPlugin* rdpdr, UINT32 cou #ifdef _WIN32 +BOOL check_path(char* path) +{ + UINT type = GetDriveTypeA(path); + if (!(type == DRIVE_REMOVABLE || type == DRIVE_CDROM || type == DRIVE_REMOTE)) + return FALSE; + return GetVolumeInformationA(path, NULL, 0, NULL, NULL, NULL, NULL, 0); +} + +void first_hotplug(rdpdrPlugin *rdpdr) +{ + int i; + char drive_path[5] = { 'c', ':', '\\', '\0' }; + + DWORD unitmask = GetLogicalDrives(); + + for (i = 0; i < 26; i++) + { + if (unitmask & 0x01) + { + RDPDR_DRIVE* drive; + + drive_path[0] = 'A' + i; + drive_path[1] = ':'; + + if (check_path(drive_path)) + { + drive = (RDPDR_DRIVE*)malloc(sizeof(RDPDR_DRIVE)); + ZeroMemory(drive, sizeof(RDPDR_DRIVE)); + drive->Type = RDPDR_DTYP_FILESYSTEM; + drive->Path = _strdup(drive_path); + drive_path[1] = '\0'; + drive->Name = _strdup(drive_path); + devman_load_device_service(rdpdr->devman, (RDPDR_DEVICE *)drive, rdpdr->rdpcontext); + } + } + unitmask = unitmask >> 1; + } +} + LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { rdpdrPlugin *rdpdr; @@ -131,17 +179,21 @@ LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) RDPDR_DRIVE* drive; drive_path[0] = 'A' + i; + drive_path[1] = ':'; - drive = (RDPDR_DRIVE*) malloc(sizeof(RDPDR_DRIVE)); - ZeroMemory(drive, sizeof(RDPDR_DRIVE)); + if (check_path(drive_path)) + { + drive = (RDPDR_DRIVE*) malloc(sizeof(RDPDR_DRIVE)); + ZeroMemory(drive, sizeof(RDPDR_DRIVE)); - drive->Type = RDPDR_DTYP_FILESYSTEM; + drive->Type = RDPDR_DTYP_FILESYSTEM; - drive->Path = _strdup(drive_path); - drive_path[1] = '\0'; - drive->Name = _strdup(drive_path); - devman_load_device_service(rdpdr->devman, (RDPDR_DEVICE *)drive, rdpdr->rdpcontext); - rdpdr_send_device_list_announce_request(rdpdr, TRUE); + drive->Path = _strdup(drive_path); + drive_path[1] = '\0'; + drive->Name = _strdup(drive_path); + devman_load_device_service(rdpdr->devman, (RDPDR_DEVICE *)drive, rdpdr->rdpcontext); + rdpdr_send_device_list_announce_request(rdpdr, TRUE); + } } unitmask = unitmask >> 1; } @@ -275,6 +327,242 @@ static UINT drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr) return error; } +#elif __MACOSX__ + +#define MAX_USB_DEVICES 100 + +typedef struct _hotplug_dev +{ + char* path; + BOOL to_add; +} hotplug_dev; + + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT handle_hotplug(rdpdrPlugin* rdpdr) +{ + struct dirent *pDirent; + DIR *pDir; + char fullpath[PATH_MAX]; + char* szdir = (char*)"/Volumes"; + struct stat buf; + hotplug_dev dev_array[MAX_USB_DEVICES]; + int count; + DEVICE_DRIVE_EXT *device_ext; + ULONG_PTR *keys; + int i, j; + int size = 0; + UINT error; + UINT32 ids[1]; + + pDir = opendir (szdir); + if (pDir == NULL) + { + printf ("Cannot open directory\n"); + return ERROR_OPEN_FAILED; + } + + while ((pDirent = readdir(pDir)) != NULL) + { + if (pDirent->d_name[0] != '.') + { + sprintf(fullpath, "%s/%s", szdir, pDirent->d_name); + lstat(fullpath, &buf); + if(S_ISDIR(buf.st_mode)) + { + dev_array[size].path = _strdup(fullpath); + if (!dev_array[size].path) + { + closedir (pDir); + error = CHANNEL_RC_NO_MEMORY; + goto cleanup; + } + dev_array[size++].to_add = TRUE; + } + } + } + closedir (pDir); + + /* delete removed devices */ + count = ListDictionary_GetKeys(rdpdr->devman->devices, &keys); + + for (j = 0; j < count; j++) + { + BOOL dev_found = FALSE; + + device_ext = (DEVICE_DRIVE_EXT *)ListDictionary_GetItemValue(rdpdr->devman->devices, (void *)keys[j]); + if (!device_ext) + continue; + + if (device_ext->path == NULL) + continue; + + /* not plugable device */ + if (strstr(device_ext->path, "/Volumes/") == NULL) + continue; + + for (i = 0; i < size; i++) + { + if (strstr(device_ext->path, dev_array[i].path) != NULL) + { + dev_found = TRUE; + dev_array[i].to_add = FALSE; + break; + } + } + + if (!dev_found) + { + devman_unregister_device(rdpdr->devman, (void *)keys[j]); + ids[0] = keys[j]; + if ((error = rdpdr_send_device_list_remove_request(rdpdr, 1, ids))) + { + WLog_ERR(TAG, "rdpdr_send_device_list_remove_request failed with error %lu!", error); + goto cleanup; + } + } + } + + /* add new devices */ + for (i = 0; i < size; i++) + { + RDPDR_DRIVE* drive; + + if (dev_array[i].to_add) + { + char* name; + + drive = (RDPDR_DRIVE*) calloc(1, sizeof(RDPDR_DRIVE)); + if (!drive) + { + WLog_ERR(TAG, "calloc failed!"); + error = CHANNEL_RC_NO_MEMORY; + goto cleanup; + } + + drive->Type = RDPDR_DTYP_FILESYSTEM; + + drive->Path = dev_array[i].path; + dev_array[i].path = NULL; + + name = strrchr(drive->Path, '/') + 1; + drive->Name = _strdup(name); + if (!drive->Name) + { + WLog_ERR(TAG, "_strdup failed!"); + free(drive->Path); + free(drive); + error = CHANNEL_RC_NO_MEMORY; + goto cleanup; + } + if ((error = devman_load_device_service(rdpdr->devman, (RDPDR_DEVICE *)drive, rdpdr->rdpcontext))) + { + WLog_ERR(TAG, "devman_load_device_service failed!"); + free(drive->Path); + free(drive->Name); + free(drive); + error = CHANNEL_RC_NO_MEMORY; + goto cleanup; + } + } + } + +cleanup: + for (i = 0; i < size; i++) + free (dev_array[i].path); + + return error; + } + + +static void drive_hotplug_fsevent_callback(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, + size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], + const FSEventStreamEventId eventIds[]) +{ + rdpdrPlugin* rdpdr; + int i; + UINT error; + char **paths = (char**)eventPaths; + + rdpdr = (rdpdrPlugin*) clientCallBackInfo; + + for (i=0; irunLoop = CFRunLoopGetCurrent(); + FSEventStreamScheduleWithRunLoop(fsev, rdpdr->runLoop, kCFRunLoopDefaultMode); + FSEventStreamStart(fsev); + CFRunLoopRun(); + FSEventStreamStop(fsev); + FSEventStreamRelease(fsev); + + ExitThread(CHANNEL_RC_OK); + return NULL; +} + + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr) +{ + UINT error; + if (rdpdr->hotplugThread) + { + CFRunLoopStop(rdpdr->runLoop); + + if (WaitForSingleObject(rdpdr->hotplugThread, INFINITE) == WAIT_FAILED) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error); + return error; + } + rdpdr->hotplugThread = NULL; + } + return CHANNEL_RC_OK; +} + #else #define MAX_USB_DEVICES 100 @@ -506,7 +794,16 @@ cleanup: for (i = 0; i < size; i++) free (dev_array[i].path); - return error ? error : rdpdr_send_device_list_announce_request(rdpdr, TRUE); + return error; +} + +void first_hotplug(rdpdrPlugin *rdpdr) +{ + UINT error; + if ((error = handle_hotplug(rdpdr))) + { + WLog_ERR(TAG, "handle_hotplug failed with error %lu!", error); + } } static void* drive_hotplug_thread_func(void* arg) @@ -516,7 +813,7 @@ static void* drive_hotplug_thread_func(void* arg) fd_set rfds; struct timeval tv; int rv; - UINT error; + UINT error = 0; DWORD status; rdpdr = (rdpdrPlugin*) arg; @@ -542,12 +839,6 @@ static void* drive_hotplug_thread_func(void* arg) tv.tv_sec = 1; tv.tv_usec = 0; - if ((error = handle_hotplug(rdpdr))) - { - WLog_ERR(TAG, "handle_hotplug failed with error %lu!", error); - goto out; - } - while ((rv = select(mfd+1, NULL, NULL, &rfds, &tv)) >= 0) { status = WaitForSingleObject(rdpdr->stopEvent, 0); @@ -568,6 +859,8 @@ static void* drive_hotplug_thread_func(void* arg) WLog_ERR(TAG, "handle_hotplug failed with error %lu!", error); goto out; } + else + rdpdr_send_device_list_announce_request(rdpdr, TRUE); } FD_ZERO(&rfds); @@ -643,6 +936,7 @@ static UINT rdpdr_process_connect(rdpdrPlugin* rdpdr) if (device->Name && (strcmp(device->Name, "*") == 0)) { + first_hotplug(rdpdr); if (!(rdpdr->hotplugThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) drive_hotplug_thread_func, rdpdr, 0, NULL))) { diff --git a/channels/rdpdr/client/rdpdr_main.h b/channels/rdpdr/client/rdpdr_main.h index 7f93acbd3..6ecb945cd 100644 --- a/channels/rdpdr/client/rdpdr_main.h +++ b/channels/rdpdr/client/rdpdr_main.h @@ -6,6 +6,8 @@ * Copyright 2010-2012 Marc-Andre Moreau * Copyright 2015 Thincast Technologies GmbH * Copyright 2015 DI (FH) Martin Haimberger + * Copyright 2016 Inuvika Inc. + * Copyright 2016 David PHAM-VAN * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +38,10 @@ #include #include +#ifdef __MACOSX__ +#include +#endif + #define TAG CHANNELS_TAG("rdpdr.client") typedef struct rdpdr_plugin rdpdrPlugin; @@ -64,6 +70,8 @@ struct rdpdr_plugin HANDLE hotplugThread; #ifdef _WIN32 HWND hotplug_wnd; +#elif __MACOSX__ + CFRunLoopRef runLoop; #else HANDLE stopEvent; #endif