From d821869a670e80650b32d40f72dbf5ba37a29830 Mon Sep 17 00:00:00 2001 From: Vic Lee Date: Sat, 6 Aug 2011 16:24:36 +0800 Subject: [PATCH 01/10] libfreerdp-utils: enhance thread module to simplify threading codes. --- include/freerdp/utils/thread.h | 42 +++++++++++------ libfreerdp-utils/CMakeLists.txt | 1 + libfreerdp-utils/svc_plugin.c | 72 ++++++++--------------------- libfreerdp-utils/thread.c | 81 +++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 69 deletions(-) create mode 100644 libfreerdp-utils/thread.c diff --git a/include/freerdp/utils/thread.h b/include/freerdp/utils/thread.h index 82f6192c2..8dab3bd86 100644 --- a/include/freerdp/utils/thread.h +++ b/include/freerdp/utils/thread.h @@ -20,22 +20,34 @@ #ifndef __THREAD_UTILS_H #define __THREAD_UTILS_H -#ifdef _WIN32 - -#define freerdp_thread_create(_proc, _arg) do { \ - DWORD thread; \ - CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_proc, _arg, 0, &thread); \ - while (0) - -#else - +#include +#include +#include +#ifndef _WIN32 #include -#define freerdp_thread_create(_proc, _arg) do { \ - pthread_t thread; \ - pthread_create(&thread, 0, _proc, _arg); \ - pthread_detach(thread); \ - } while (0) - #endif +typedef struct _freerdp_thread freerdp_thread; +struct _freerdp_thread +{ + freerdp_mutex* mutex; + + struct wait_obj* signals[5]; + int num_signals; + + int status; +}; + +freerdp_thread* freerdp_thread_new(void); +void freerdp_thread_start(freerdp_thread* thread, void* func, void* arg); +void freerdp_thread_stop(freerdp_thread* thread); + +#define freerdp_thread_wait(_t) wait_obj_select(_t->signals, _t->num_signals, -1) +#define freerdp_thread_is_stopped(_t) wait_obj_is_set(_t->signals[0]) +#define freerdp_thread_quit(_t) _t->status = -1 +#define freerdp_thread_signal(_t) wait_obj_set(_t->signals[1]) +#define freerdp_thread_reset(_t) wait_obj_clear(_t->signals[1]) +#define freerdp_thread_lock(_t) freerdp_mutex_lock(_t->mutex) +#define freerdp_thread_unlock(_t) freerdp_mutex_unlock(_t->mutex) + #endif /* __THREAD_UTILS_H */ diff --git a/libfreerdp-utils/CMakeLists.txt b/libfreerdp-utils/CMakeLists.txt index 0a0b619e0..1cd5c6cb2 100644 --- a/libfreerdp-utils/CMakeLists.txt +++ b/libfreerdp-utils/CMakeLists.txt @@ -32,6 +32,7 @@ set(FREERDP_UTILS_SRCS semaphore.c stream.c svc_plugin.c + thread.c unicode.c wait_obj.c) diff --git a/libfreerdp-utils/svc_plugin.c b/libfreerdp-utils/svc_plugin.c index 5aee54e1c..3cdb69a84 100644 --- a/libfreerdp-utils/svc_plugin.c +++ b/libfreerdp-utils/svc_plugin.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -30,7 +29,6 @@ #include #include #include -#include #include #include @@ -77,12 +75,7 @@ struct rdp_svc_plugin_private STREAM* data_in; LIST* data_in_list; - freerdp_mutex* data_in_mutex; - - struct wait_obj* signals[5]; - int num_signals; - - int thread_status; + freerdp_thread* thread; }; static rdpSvcPlugin* svc_plugin_find_by_init_handle(void* init_handle) @@ -176,11 +169,11 @@ static void svc_plugin_process_received(rdpSvcPlugin* plugin, void* pData, uint3 item = xnew(svc_data_in_item); item->data_in = data_in; - freerdp_mutex_lock(plugin->priv->data_in_mutex); + freerdp_thread_lock(plugin->priv->thread); list_enqueue(plugin->priv->data_in_list, item); - freerdp_mutex_unlock(plugin->priv->data_in_mutex); + freerdp_thread_unlock(plugin->priv->thread); - wait_obj_set(plugin->priv->signals[1]); + freerdp_thread_signal(plugin->priv->thread); } } @@ -191,11 +184,11 @@ static void svc_plugin_process_event(rdpSvcPlugin* plugin, FRDP_EVENT* event_in) item = xnew(svc_data_in_item); item->event_in = event_in; - freerdp_mutex_lock(plugin->priv->data_in_mutex); + freerdp_thread_lock(plugin->priv->thread); list_enqueue(plugin->priv->data_in_list, item); - freerdp_mutex_unlock(plugin->priv->data_in_mutex); + freerdp_thread_unlock(plugin->priv->thread); - wait_obj_set(plugin->priv->signals[1]); + freerdp_thread_signal(plugin->priv->thread); } static void svc_plugin_open_event(uint32 openHandle, uint32 event, void* pData, uint32 dataLength, @@ -233,12 +226,12 @@ static void svc_plugin_process_data_in(rdpSvcPlugin* plugin) while (1) { /* terminate signal */ - if (wait_obj_is_set(plugin->priv->signals[0])) + if (freerdp_thread_is_stopped(plugin->priv->thread)) break; - freerdp_mutex_lock(plugin->priv->data_in_mutex); + freerdp_thread_lock(plugin->priv->thread); item = list_dequeue(plugin->priv->data_in_list); - freerdp_mutex_unlock(plugin->priv->data_in_mutex); + freerdp_thread_unlock(plugin->priv->thread); if (item != NULL) { @@ -264,22 +257,16 @@ static void* svc_plugin_thread_func(void* arg) while (1) { - wait_obj_select(plugin->priv->signals, plugin->priv->num_signals, -1); + freerdp_thread_wait(plugin->priv->thread); - /* terminate signal */ - if (wait_obj_is_set(plugin->priv->signals[0])) + if (freerdp_thread_is_stopped(plugin->priv->thread)) break; - /* data_in signal */ - if (wait_obj_is_set(plugin->priv->signals[1])) - { - wait_obj_clear(plugin->priv->signals[1]); - /* process data in */ - svc_plugin_process_data_in(plugin); - } + freerdp_thread_reset(plugin->priv->thread); + svc_plugin_process_data_in(plugin); } - plugin->priv->thread_status = -1; + freerdp_thread_quit(plugin->priv->thread); DEBUG_SVC("out"); @@ -299,45 +286,22 @@ static void svc_plugin_process_connected(rdpSvcPlugin* plugin, void* pData, uint } plugin->priv->data_in_list = list_new(); - plugin->priv->data_in_mutex = freerdp_mutex_new(); + plugin->priv->thread = freerdp_thread_new(); - /* terminate signal */ - plugin->priv->signals[plugin->priv->num_signals++] = wait_obj_new(); - /* data_in signal */ - plugin->priv->signals[plugin->priv->num_signals++] = wait_obj_new(); - - plugin->priv->thread_status = 1; - - freerdp_thread_create(svc_plugin_thread_func, plugin); + freerdp_thread_start(plugin->priv->thread, svc_plugin_thread_func, plugin); } static void svc_plugin_process_terminated(rdpSvcPlugin* plugin) { svc_data_in_item* item; - struct timespec ts; - int i; - wait_obj_set(plugin->priv->signals[0]); - i = 0; - ts.tv_sec = 0; - ts.tv_nsec = 10000000; - while (plugin->priv->thread_status > 0 && i < 1000) - { - i++; - nanosleep(&ts, NULL); - } + freerdp_thread_stop(plugin->priv->thread); plugin->channel_entry_points.pVirtualChannelClose(plugin->priv->open_handle); xfree(plugin->channel_entry_points.pExtendedData); svc_plugin_remove(plugin); - for (i = 0; i < plugin->priv->num_signals; i++) - wait_obj_free(plugin->priv->signals[i]); - plugin->priv->num_signals = 0; - - freerdp_mutex_free(plugin->priv->data_in_mutex); - while ((item = list_dequeue(plugin->priv->data_in_list)) != NULL) svc_data_in_item_free(item); list_free(plugin->priv->data_in_list); diff --git a/libfreerdp-utils/thread.c b/libfreerdp-utils/thread.c new file mode 100644 index 000000000..f6b838004 --- /dev/null +++ b/libfreerdp-utils/thread.c @@ -0,0 +1,81 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Thread Utils + * + * Copyright 2011 Vic Lee + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +freerdp_thread* freerdp_thread_new(void) +{ + freerdp_thread* thread; + + thread = xnew(freerdp_thread); + thread->mutex = freerdp_mutex_new(); + thread->signals[0] = wait_obj_new(); + thread->signals[1] = wait_obj_new(); + thread->num_signals = 2; + + return thread; +} + +void freerdp_thread_start(freerdp_thread* thread, void* func, void* arg) +{ + thread->status = 1; + +#ifdef _WIN32 + { + DWORD th; + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, arg, 0, &th); + } +#else + { + pthread_t th; + pthread_create(&th, 0, func, arg); + pthread_detach(th); + } +#endif +} + +void freerdp_thread_stop(freerdp_thread* thread) +{ + struct timespec ts; + int i; + + wait_obj_set(thread->signals[0]); + i = 0; + ts.tv_sec = 0; + ts.tv_nsec = 10000000; + while (thread->status > 0 && i < 1000) + { + i++; + nanosleep(&ts, NULL); + } + + for (i = 0; i < thread->num_signals; i++) + wait_obj_free(thread->signals[i]); + thread->num_signals = 0; + + freerdp_mutex_free(thread->mutex); + thread->mutex = NULL; + + xfree(thread); +} From 9c64e5b08d7d98535f3e25123f800fb98ab9bf25 Mon Sep 17 00:00:00 2001 From: Vic Lee Date: Sat, 6 Aug 2011 16:25:14 +0800 Subject: [PATCH 02/10] drdynvc: remove unused header --- channels/drdynvc/drdynvc_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/channels/drdynvc/drdynvc_main.c b/channels/drdynvc/drdynvc_main.c index d482435a4..e1b7fb078 100644 --- a/channels/drdynvc/drdynvc_main.c +++ b/channels/drdynvc/drdynvc_main.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include From 49458e73637bac594f7e0118dcc463cbd867f082 Mon Sep 17 00:00:00 2001 From: Vic Lee Date: Sun, 7 Aug 2011 11:21:42 +0800 Subject: [PATCH 03/10] rdpdr/disk: add create/close irp processing. --- channels/rdpdr/disk/CMakeLists.txt | 2 + channels/rdpdr/disk/disk_file.c | 235 +++++++++++++++++++++++++++++ channels/rdpdr/disk/disk_file.h | 45 ++++++ channels/rdpdr/disk/disk_main.c | 223 +++++++++++++++++++++++---- 4 files changed, 474 insertions(+), 31 deletions(-) create mode 100644 channels/rdpdr/disk/disk_file.c create mode 100644 channels/rdpdr/disk/disk_file.h diff --git a/channels/rdpdr/disk/CMakeLists.txt b/channels/rdpdr/disk/CMakeLists.txt index 654621279..5a44c8185 100644 --- a/channels/rdpdr/disk/CMakeLists.txt +++ b/channels/rdpdr/disk/CMakeLists.txt @@ -18,6 +18,8 @@ # limitations under the License. set(DISK_SRCS + disk_file.c + disk_file.h disk_main.c ) diff --git a/channels/rdpdr/disk/disk_file.c b/channels/rdpdr/disk/disk_file.c new file mode 100644 index 000000000..61c9f4ba4 --- /dev/null +++ b/channels/rdpdr/disk/disk_file.c @@ -0,0 +1,235 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * File System Virtual Channel + * + * Copyright 2010-2011 Marc-Andre Moreau + * Copyright 2010-2011 Vic Lee + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif + +#include "rdpdr_constants.h" +#include "rdpdr_types.h" +#include "disk_file.h" + +static char* disk_file_get_fullpath(const char* base_path, const char* path) +{ + char* fullpath; + int len; + int i; + + fullpath = xmalloc(strlen(base_path) + strlen(path) + 1); + strcpy(fullpath, base_path); + strcat(fullpath, path); + len = strlen(fullpath); + for (i = 0; i < len; i++) + { + if (fullpath[i] == '\\') + fullpath[i] = '/'; + } + if (len > 0 && fullpath[len - 1] == '/') + fullpath[len - 1] = '\0'; + + return fullpath; +} + +static boolean disk_file_remove_dir(const char* path) +{ + DIR* dir; + struct dirent* pdirent; + struct stat st; + char* p; + boolean ret; + + dir = opendir(path); + if (dir == NULL) + return False; + + pdirent = readdir(dir); + while (pdirent) + { + if (strcmp(pdirent->d_name, ".") == 0 || strcmp(pdirent->d_name, "..") == 0) + { + pdirent = readdir(dir); + continue; + } + + p = xmalloc(strlen(path) + strlen(pdirent->d_name) + 2); + sprintf(p, "%s/%s", path, pdirent->d_name); + if (stat(p, &st) != 0) + { + DEBUG_WARN("stat %s failed.", p); + ret = False; + } + else if (S_ISDIR(st.st_mode)) + { + ret = disk_file_remove_dir(p); + } + else if (unlink(p) < 0) + { + DEBUG_WARN("unlink %s failed.", p); + ret = False; + } + else + ret = True; + xfree(p); + + if (!ret) + break; + + pdirent = readdir(dir); + } + + closedir(dir); + if (ret) + { + if (rmdir(path) < 0) + { + DEBUG_WARN("rmdir %s failed.", path); + ret = False; + } + } + + return ret; +} + +boolean disk_file_init(DISK_FILE* file, uint32 DesiredAccess, uint32 CreateDisposition, uint32 CreateOptions) +{ + const static int mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH; + struct stat st; + boolean exists; + int oflag = 0; + + if (stat(file->fullpath, &st) == 0) + { + file->is_dir = (S_ISDIR(st.st_mode) ? True : False); + exists = True; + } + else + { + file->is_dir = ((CreateOptions & FILE_DIRECTORY_FILE) ? True : False); + if (file->is_dir) + { + if (mkdir(file->fullpath, mode) != 0) + return False; + } + exists = False; + } + if (file->is_dir) + { + file->dir = opendir(file->fullpath); + if (file->dir == NULL) + return False; + } + else + { + switch (CreateDisposition) + { + case FILE_SUPERSEDE: + oflag = O_TRUNC | O_CREAT; + break; + case FILE_OPEN: + break; + case FILE_CREATE: + oflag = O_CREAT | O_EXCL; + break; + case FILE_OPEN_IF: + oflag = O_CREAT; + break; + case FILE_OVERWRITE: + oflag = O_TRUNC; + break; + case FILE_OVERWRITE_IF: + oflag = O_TRUNC | O_CREAT; + break; + default: + break; + } + if (!exists && (CreateOptions & FILE_DELETE_ON_CLOSE)) + file->delete_pending = 1; + + if ((DesiredAccess & GENERIC_ALL) + || (DesiredAccess & GENERIC_WRITE) + || (DesiredAccess & FILE_WRITE_DATA) + || (DesiredAccess & FILE_APPEND_DATA)) + { + oflag |= O_RDWR; + } + else + { + oflag |= O_RDONLY; + } + + file->fd = open(file->fullpath, oflag, mode); + if (file->fd == -1) + return False; + } + + return True; +} + +DISK_FILE* disk_file_new(const char* base_path, const char* path, uint32 id, + uint32 DesiredAccess, uint32 CreateDisposition, uint32 CreateOptions) +{ + DISK_FILE* file; + + file = xnew(DISK_FILE); + file->id = id; + file->fullpath = disk_file_get_fullpath(base_path, path); + file->fd = -1; + + if (!disk_file_init(file, DesiredAccess, CreateDisposition, CreateOptions)) + { + disk_file_free(file); + return NULL; + } + + return file; +} + +void disk_file_free(DISK_FILE* file) +{ + if (file->fd != -1) + close(file->fd); + if (file->dir != NULL) + closedir(file->dir); + + if (file->delete_pending) + { + if (file->is_dir) + disk_file_remove_dir(file->fullpath); + else + unlink(file->fullpath); + } + + xfree(file->fullpath); + xfree(file->pattern); + xfree(file); +} diff --git a/channels/rdpdr/disk/disk_file.h b/channels/rdpdr/disk/disk_file.h new file mode 100644 index 000000000..de3463e50 --- /dev/null +++ b/channels/rdpdr/disk/disk_file.h @@ -0,0 +1,45 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * File System Virtual Channel + * + * Copyright 2010-2011 Marc-Andre Moreau + * Copyright 2010-2011 Vic Lee + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DISK_FILE_H +#define __DISK_FILE_H + +#include +#include +#include + +typedef struct _DISK_FILE DISK_FILE; +struct _DISK_FILE +{ + uint32 id; + uint32 attr; + boolean is_dir; + int fd; + DIR* dir; + char* fullpath; + char* pattern; + boolean delete_pending; +}; + +DISK_FILE* disk_file_new(const char* base_path, const char* path, uint32 id, + uint32 DesiredAccess, uint32 CreateDisposition, uint32 CreateOptions); +void disk_file_free(DISK_FILE* file); + +#endif /* __DISK_FILE_H */ diff --git a/channels/rdpdr/disk/disk_main.c b/channels/rdpdr/disk/disk_main.c index def18e2f1..00dd7a376 100644 --- a/channels/rdpdr/disk/disk_main.c +++ b/channels/rdpdr/disk/disk_main.c @@ -22,41 +22,16 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include #include #include #include #include +#include #include -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include -#endif - #include "rdpdr_constants.h" #include "rdpdr_types.h" - -typedef struct _FILE_INFO FILE_INFO; -struct _FILE_INFO -{ - uint32 file_id; - uint32 file_attr; - boolean is_dir; - int fd; - DIR* dir; - char* fullpath; - char* pattern; - boolean delete_pending; -}; +#include "disk_file.h" typedef struct _DISK_DEVICE DISK_DEVICE; struct _DISK_DEVICE @@ -65,19 +40,200 @@ struct _DISK_DEVICE char* path; LIST* files; + + LIST* irp_list; + freerdp_thread* thread; }; -void disk_irp_request(DEVICE* device, IRP* irp) +static DISK_FILE* disk_get_file_by_id(DISK_DEVICE* disk, uint32 id) { - DISK_DEVICE* disk = (DISK_DEVICE*)device; + LIST_ITEM* item; + DISK_FILE* file; - IFCALL(irp->Complete, irp); + for (item = disk->files->head; item; item = item->next) + { + file = (DISK_FILE*)item->data; + if (file->id == id) + return file; + } + return NULL; } -void disk_free(DEVICE* device) +static void disk_process_irp_create(DISK_DEVICE* disk, IRP* irp) +{ + DISK_FILE* file; + uint32 DesiredAccess; + uint32 CreateDisposition; + uint32 CreateOptions; + uint32 PathLength; + UNICONV* uniconv; + char* path; + uint32 FileId; + uint8 Information; + + stream_read_uint32(irp->input, DesiredAccess); + stream_seek(irp->input, 16); /* AllocationSize(8), FileAttributes(4), SharedAccess(4) */ + stream_read_uint32(irp->input, CreateDisposition); + stream_read_uint32(irp->input, CreateOptions); + stream_read_uint32(irp->input, PathLength); + + uniconv = freerdp_uniconv_new(); + path = freerdp_uniconv_in(uniconv, stream_get_tail(irp->input), PathLength); + freerdp_uniconv_free(uniconv); + + FileId = irp->devman->id_sequence++; + file = disk_file_new(disk->path, path, FileId, + DesiredAccess, CreateDisposition, CreateOptions); + + if (file == NULL) + { + irp->IoStatus = STATUS_UNSUCCESSFUL; + FileId = 0; + Information = 0; + + DEBUG_WARN("failed to create %s.", path); + } + else + { + list_enqueue(disk->files, file); + + switch (CreateDisposition) + { + case FILE_SUPERSEDE: + case FILE_OPEN: + case FILE_CREATE: + case FILE_OVERWRITE: + Information = FILE_SUPERSEDED; + break; + case FILE_OPEN_IF: + Information = FILE_OPENED; + break; + case FILE_OVERWRITE_IF: + Information = FILE_OVERWRITTEN; + break; + default: + Information = 0; + break; + } + DEBUG_SVC("%s(%d) created.", file->fullpath, file->id); + } + + stream_write_uint32(irp->output, FileId); + stream_write_uint8(irp->output, Information); + + xfree(path); + + irp->Complete(irp); +} + +static void disk_process_irp_close(DISK_DEVICE* disk, IRP* irp) +{ + DISK_FILE* file; + + file = disk_get_file_by_id(disk, irp->FileId); + + if (file == NULL) + { + irp->IoStatus = STATUS_UNSUCCESSFUL; + + DEBUG_WARN("FileId %d not valid.", irp->FileId); + } + else + { + DEBUG_SVC("%s(%d) closed.", file->fullpath, file->id); + + list_remove(disk->files, file); + disk_file_free(file); + } + + stream_write(irp->output, "\0\0\0\0\0", 5); /* Padding(5) */ +} + +static void disk_process_irp(DISK_DEVICE* disk, IRP* irp) +{ + switch (irp->MajorFunction) + { + case IRP_MJ_CREATE: + disk_process_irp_create(disk, irp); + break; + + case IRP_MJ_CLOSE: + disk_process_irp_close(disk, irp); + break; + + default: + DEBUG_WARN("MajorFunction 0x%X not supported", irp->MajorFunction); + irp->IoStatus = STATUS_NOT_SUPPORTED; + irp->Complete(irp); + break; + } +} + +static void disk_process_irp_list(DISK_DEVICE* disk) +{ + IRP* irp; + + while (1) + { + if (freerdp_thread_is_stopped(disk->thread)) + break; + + freerdp_thread_lock(disk->thread); + irp = (IRP*)list_dequeue(disk->irp_list); + freerdp_thread_unlock(disk->thread); + + if (irp == NULL) + break; + + disk_process_irp(disk, irp); + } +} + +static void* disk_thread_func(void* arg) +{ + DISK_DEVICE* disk = (DISK_DEVICE*)arg; + + while (1) + { + freerdp_thread_wait(disk->thread); + + if (freerdp_thread_is_stopped(disk->thread)) + break; + + freerdp_thread_reset(disk->thread); + disk_process_irp_list(disk); + } + + freerdp_thread_quit(disk->thread); + + return NULL; +} + +static void disk_irp_request(DEVICE* device, IRP* irp) { DISK_DEVICE* disk = (DISK_DEVICE*)device; + freerdp_thread_lock(disk->thread); + list_enqueue(disk->irp_list, irp); + freerdp_thread_unlock(disk->thread); + + freerdp_thread_signal(disk->thread); +} + +static void disk_free(DEVICE* device) +{ + DISK_DEVICE* disk = (DISK_DEVICE*)device; + IRP* irp; + DISK_FILE* file; + + freerdp_thread_stop(disk->thread); + + while ((irp = (IRP*)list_dequeue(disk->irp_list)) != NULL) + irp->Discard(irp); + list_free(disk->irp_list); + + while ((file = (DISK_FILE*)list_dequeue(disk->files)) != NULL) + disk_file_free(file); list_free(disk->files); xfree(disk); } @@ -109,7 +265,12 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints) disk->path = path; disk->files = list_new(); + disk->irp_list = list_new(); + disk->thread = freerdp_thread_new(); + pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)disk); + + freerdp_thread_start(disk->thread, disk_thread_func, disk); } return 0; From 419a8163d65642b01c9ca8b1974773393f2db00c Mon Sep 17 00:00:00 2001 From: Vic Lee Date: Sun, 7 Aug 2011 13:11:52 +0800 Subject: [PATCH 04/10] rdpdr/disk: add read/write irp processing. --- channels/rdpdr/disk/disk_file.c | 46 ++++++++++++++++ channels/rdpdr/disk/disk_file.h | 4 ++ channels/rdpdr/disk/disk_main.c | 95 +++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+) diff --git a/channels/rdpdr/disk/disk_file.c b/channels/rdpdr/disk/disk_file.c index 61c9f4ba4..bb939c33b 100644 --- a/channels/rdpdr/disk/disk_file.c +++ b/channels/rdpdr/disk/disk_file.c @@ -233,3 +233,49 @@ void disk_file_free(DISK_FILE* file) xfree(file->pattern); xfree(file); } + +boolean disk_file_seek(DISK_FILE* file, uint64 Offset) +{ + if (file->is_dir || file->fd == -1) + return False; + + if (lseek(file->fd, Offset, SEEK_SET) == (off_t)-1) + return False; + + return True; +} + +boolean disk_file_read(DISK_FILE* file, uint8* buffer, uint32* Length) +{ + ssize_t r; + + if (file->is_dir || file->fd == -1) + return False; + + r = read(file->fd, buffer, *Length); + if (r < 0) + return False; + *Length = (uint32)r; + + return True; +} + +boolean disk_file_write(DISK_FILE* file, uint8* buffer, uint32 Length) +{ + ssize_t r; + + + if (file->is_dir || file->fd == -1) + return False; + + while (Length > 0) + { + r = write(file->fd, buffer, Length); + if (r == -1) + return False; + Length -= r; + buffer += r; + } + + return True; +} diff --git a/channels/rdpdr/disk/disk_file.h b/channels/rdpdr/disk/disk_file.h index de3463e50..03c86ee8a 100644 --- a/channels/rdpdr/disk/disk_file.h +++ b/channels/rdpdr/disk/disk_file.h @@ -42,4 +42,8 @@ DISK_FILE* disk_file_new(const char* base_path, const char* path, uint32 id, uint32 DesiredAccess, uint32 CreateDisposition, uint32 CreateOptions); void disk_file_free(DISK_FILE* file); +boolean disk_file_seek(DISK_FILE* file, uint64 Offset); +boolean disk_file_read(DISK_FILE* file, uint8* buffer, uint32* Length); +boolean disk_file_write(DISK_FILE* file, uint8* buffer, uint32 Length); + #endif /* __DISK_FILE_H */ diff --git a/channels/rdpdr/disk/disk_main.c b/channels/rdpdr/disk/disk_main.c index 00dd7a376..6d30f643f 100644 --- a/channels/rdpdr/disk/disk_main.c +++ b/channels/rdpdr/disk/disk_main.c @@ -149,6 +149,93 @@ static void disk_process_irp_close(DISK_DEVICE* disk, IRP* irp) stream_write(irp->output, "\0\0\0\0\0", 5); /* Padding(5) */ } +static void disk_process_irp_read(DISK_DEVICE* disk, IRP* irp) +{ + DISK_FILE* file; + uint32 Length; + uint64 Offset; + uint8* buffer = NULL; + + stream_read_uint32(irp->input, Length); + stream_read_uint64(irp->input, Offset); + + file = disk_get_file_by_id(disk, irp->FileId); + + if (file == NULL) + { + irp->IoStatus = STATUS_UNSUCCESSFUL; + Length = 0; + + DEBUG_WARN("FileId %d not valid.", irp->FileId); + } + else if (!disk_file_seek(file, Offset)) + { + irp->IoStatus = STATUS_UNSUCCESSFUL; + Length = 0; + + DEBUG_WARN("seek %s(%d) failed.", file->fullpath, file->id); + } + else + { + buffer = (uint8*)xmalloc(Length); + if (!disk_file_read(file, buffer, &Length)) + { + irp->IoStatus = STATUS_UNSUCCESSFUL; + xfree(buffer); + buffer = NULL; + Length = 0; + + DEBUG_WARN("read %s(%d) failed.", file->fullpath, file->id); + } + } + + stream_write_uint32(irp->output, Length); + if (Length > 0) + { + stream_check_size(irp->output, Length); + stream_write(irp->output, buffer, Length); + } + xfree(buffer); +} + +static void disk_process_irp_write(DISK_DEVICE* disk, IRP* irp) +{ + DISK_FILE* file; + uint32 Length; + uint64 Offset; + + stream_read_uint32(irp->input, Length); + stream_read_uint64(irp->input, Offset); + stream_seek(irp->input, 20); /* Padding */ + + file = disk_get_file_by_id(disk, irp->FileId); + + if (file == NULL) + { + irp->IoStatus = STATUS_UNSUCCESSFUL; + Length = 0; + + DEBUG_WARN("FileId %d not valid.", irp->FileId); + } + else if (!disk_file_seek(file, Offset)) + { + irp->IoStatus = STATUS_UNSUCCESSFUL; + Length = 0; + + DEBUG_WARN("seek %s(%d) failed.", file->fullpath, file->id); + } + else if (!disk_file_write(file, stream_get_tail(irp->input), Length)) + { + irp->IoStatus = STATUS_UNSUCCESSFUL; + Length = 0; + + DEBUG_WARN("write %s(%d) failed.", file->fullpath, file->id); + } + + stream_write_uint32(irp->output, Length); + stream_write_uint8(irp->output, 0); /* Padding */ +} + static void disk_process_irp(DISK_DEVICE* disk, IRP* irp) { switch (irp->MajorFunction) @@ -161,6 +248,14 @@ static void disk_process_irp(DISK_DEVICE* disk, IRP* irp) disk_process_irp_close(disk, irp); break; + case IRP_MJ_READ: + disk_process_irp_read(disk, irp); + break; + + case IRP_MJ_WRITE: + disk_process_irp_write(disk, irp); + break; + default: DEBUG_WARN("MajorFunction 0x%X not supported", irp->MajorFunction); irp->IoStatus = STATUS_NOT_SUPPORTED; From 9d349a2667d5e97afc68e4d966376199484fde77 Mon Sep 17 00:00:00 2001 From: Vic Lee Date: Sun, 7 Aug 2011 15:06:11 +0800 Subject: [PATCH 05/10] libfreerdp-utils/stream: fix stream_write_uint64. --- include/freerdp/utils/stream.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/freerdp/utils/stream.h b/include/freerdp/utils/stream.h index 0f0843487..5f3e2f0cc 100644 --- a/include/freerdp/utils/stream.h +++ b/include/freerdp/utils/stream.h @@ -91,14 +91,14 @@ void stream_extend(STREAM* stream); *_s->p++ = ((_v) >> 16) & 0xFF; \ *_s->p++ = ((_v) >> 24) & 0xFF; } while (0) #define stream_write_uint64(_s, _v) do { \ - *_s->p++ = (_v) & 0xFF; \ - *_s->p++ = ((_v) >> 8) & 0xFF; \ - *_s->p++ = ((_v) >> 16) & 0xFF; \ - *_s->p++ = ((_v) >> 24) & 0xFF; \ - *_s->p++ = ((_v) >> 32) & 0xFF; \ - *_s->p++ = ((_v) >> 40) & 0xFF; \ - *_s->p++ = ((_v) >> 48) & 0xFF; \ - *_s->p++ = ((_v) >> 56) & 0xFF; } while (0) + *_s->p++ = (uint64)(_v) & 0xFF; \ + *_s->p++ = ((uint64)(_v) >> 8) & 0xFF; \ + *_s->p++ = ((uint64)(_v) >> 16) & 0xFF; \ + *_s->p++ = ((uint64)(_v) >> 24) & 0xFF; \ + *_s->p++ = ((uint64)(_v) >> 32) & 0xFF; \ + *_s->p++ = ((uint64)(_v) >> 40) & 0xFF; \ + *_s->p++ = ((uint64)(_v) >> 48) & 0xFF; \ + *_s->p++ = ((uint64)(_v) >> 56) & 0xFF; } while (0) #define stream_write(_s, _b, _n) do { \ memcpy(_s->p, (_b), (_n)); \ _s->p += (_n); \ From 12215d0e0a2db8106a561cc45a329c1f08222779 Mon Sep 17 00:00:00 2001 From: Vic Lee Date: Sun, 7 Aug 2011 15:17:06 +0800 Subject: [PATCH 06/10] rdpdr/disk: add query file/fs information irp processing. --- channels/rdpdr/disk/disk_file.c | 69 +++++++++++++++++- channels/rdpdr/disk/disk_file.h | 3 +- channels/rdpdr/disk/disk_main.c | 124 ++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+), 2 deletions(-) diff --git a/channels/rdpdr/disk/disk_file.c b/channels/rdpdr/disk/disk_file.c index bb939c33b..3fad16acc 100644 --- a/channels/rdpdr/disk/disk_file.c +++ b/channels/rdpdr/disk/disk_file.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #ifdef HAVE_UNISTD_H @@ -39,6 +40,17 @@ #include "rdpdr_types.h" #include "disk_file.h" +#define FILE_TIME_SYSTEM_TO_RDP(_t) \ + (((uint64)(_t) + 11644473600LL) * 10000000LL) +#define FILE_TIME_RDP_TO_SYSTEM(_t) \ + (((_t) == 0LL || (_t) == (uint64)(-1LL)) ? 0 : (time_t)((_t) / 10000000LL - 11644473600LL)) + +#define FILE_ATTR_SYSTEM_TO_RDP(_f, _st) ( \ + (S_ISDIR(_st.st_mode) ? FILE_ATTRIBUTE_DIRECTORY : 0) | \ + (_f->filename[0] == '.' ? FILE_ATTRIBUTE_HIDDEN : 0) | \ + (_f->delete_pending ? FILE_ATTRIBUTE_TEMPORARY : 0) | \ + (st.st_mode & S_IWUSR ? 0 : FILE_ATTRIBUTE_READONLY)) + static char* disk_file_get_fullpath(const char* base_path, const char* path) { char* fullpath; @@ -203,6 +215,11 @@ DISK_FILE* disk_file_new(const char* base_path, const char* path, uint32 id, file = xnew(DISK_FILE); file->id = id; file->fullpath = disk_file_get_fullpath(base_path, path); + file->filename = strrchr(file->fullpath, '/'); + if (file->filename == NULL) + file->filename = file->fullpath; + else + file->filename += 1; file->fd = -1; if (!disk_file_init(file, DesiredAccess, CreateDisposition, CreateOptions)) @@ -264,7 +281,6 @@ boolean disk_file_write(DISK_FILE* file, uint8* buffer, uint32 Length) { ssize_t r; - if (file->is_dir || file->fd == -1) return False; @@ -279,3 +295,54 @@ boolean disk_file_write(DISK_FILE* file, uint8* buffer, uint32 Length) return True; } + +boolean disk_file_query_information(DISK_FILE* file, uint32 FsInformationClass, STREAM* output) +{ + struct stat st; + + if (stat(file->fullpath, &st) != 0) + { + stream_write_uint32(output, 0); /* Length */ + return False; + } + switch (FsInformationClass) + { + case FileBasicInformation: + /* http://msdn.microsoft.com/en-us/library/cc232094.aspx */ + stream_write_uint32(output, 40); /* Length */ + stream_check_size(output, 40); + stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */ + stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_atime)); /* LastAccessTime */ + stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* LastWriteTime */ + stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_ctime)); /* ChangeTime */ + stream_write_uint32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */ + stream_write_uint32(output, 0); /* Reserved */ + break; + + case FileStandardInformation: + /* http://msdn.microsoft.com/en-us/library/cc232088.aspx */ + stream_write_uint32(output, 24); /* Length */ + stream_check_size(output, 24); + stream_write_uint64(output, st.st_size); /* AllocationSize */ + stream_write_uint64(output, st.st_size); /* EndOfFile */ + stream_write_uint32(output, st.st_nlink); /* NumberOfLinks */ + stream_write_uint8(output, file->delete_pending ? 1 : 0); /* DeletePending */ + stream_write_uint8(output, file->is_dir ? 1 : 0); /* Directory */ + stream_write_uint16(output, 0); /* Reserved */ + break; + + case FileAttributeTagInformation: + /* http://msdn.microsoft.com/en-us/library/cc232093.aspx */ + stream_write_uint32(output, 8); /* Length */ + stream_check_size(output, 8); + stream_write_uint32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */ + stream_write_uint32(output, 0); /* ReparseTag */ + break; + + default: + stream_write_uint32(output, 0); /* Length */ + DEBUG_WARN("invalid FsInformationClass %d", FsInformationClass); + return False; + } + return True; +} diff --git a/channels/rdpdr/disk/disk_file.h b/channels/rdpdr/disk/disk_file.h index 03c86ee8a..8441647e9 100644 --- a/channels/rdpdr/disk/disk_file.h +++ b/channels/rdpdr/disk/disk_file.h @@ -29,11 +29,11 @@ typedef struct _DISK_FILE DISK_FILE; struct _DISK_FILE { uint32 id; - uint32 attr; boolean is_dir; int fd; DIR* dir; char* fullpath; + char* filename; char* pattern; boolean delete_pending; }; @@ -45,5 +45,6 @@ void disk_file_free(DISK_FILE* file); boolean disk_file_seek(DISK_FILE* file, uint64 Offset); boolean disk_file_read(DISK_FILE* file, uint8* buffer, uint32* Length); boolean disk_file_write(DISK_FILE* file, uint8* buffer, uint32 Length); +boolean disk_file_query_information(DISK_FILE* file, uint32 FsInformationClass, STREAM* output); #endif /* __DISK_FILE_H */ diff --git a/channels/rdpdr/disk/disk_main.c b/channels/rdpdr/disk/disk_main.c index 6d30f643f..162a97bef 100644 --- a/channels/rdpdr/disk/disk_main.c +++ b/channels/rdpdr/disk/disk_main.c @@ -147,6 +147,8 @@ static void disk_process_irp_close(DISK_DEVICE* disk, IRP* irp) } stream_write(irp->output, "\0\0\0\0\0", 5); /* Padding(5) */ + + irp->Complete(irp); } static void disk_process_irp_read(DISK_DEVICE* disk, IRP* irp) @@ -187,6 +189,10 @@ static void disk_process_irp_read(DISK_DEVICE* disk, IRP* irp) DEBUG_WARN("read %s(%d) failed.", file->fullpath, file->id); } + else + { + DEBUG_SVC("read %u-%u from %s(%d).", Offset, Offset + Length, file->fullpath, file->id); + } } stream_write_uint32(irp->output, Length); @@ -196,6 +202,8 @@ static void disk_process_irp_read(DISK_DEVICE* disk, IRP* irp) stream_write(irp->output, buffer, Length); } xfree(buffer); + + irp->Complete(irp); } static void disk_process_irp_write(DISK_DEVICE* disk, IRP* irp) @@ -231,9 +239,117 @@ static void disk_process_irp_write(DISK_DEVICE* disk, IRP* irp) DEBUG_WARN("write %s(%d) failed.", file->fullpath, file->id); } + else + { + DEBUG_SVC("write %u-%u to %s(%d).", Offset, Offset + Length, file->fullpath, file->id); + } stream_write_uint32(irp->output, Length); stream_write_uint8(irp->output, 0); /* Padding */ + + irp->Complete(irp); +} + +static void disk_process_irp_query_information(DISK_DEVICE* disk, IRP* irp) +{ + DISK_FILE* file; + uint32 FsInformationClass; + + stream_read_uint32(irp->input, FsInformationClass); + + file = disk_get_file_by_id(disk, irp->FileId); + + if (file == NULL) + { + irp->IoStatus = STATUS_UNSUCCESSFUL; + + DEBUG_WARN("FileId %d not valid.", irp->FileId); + } + else if (!disk_file_query_information(file, FsInformationClass, irp->output)) + { + irp->IoStatus = STATUS_UNSUCCESSFUL; + + DEBUG_WARN("query_information %s(%d) failed.", file->fullpath, file->id); + } + else + { + DEBUG_SVC("query_information %d on %s(%d).", FsInformationClass, file->fullpath, file->id); + } + + irp->Complete(irp); +} + +static void disk_process_irp_query_volume_information(DISK_DEVICE* disk, IRP* irp) +{ + uint32 FsInformationClass; + STREAM* output = irp->output; + + stream_read_uint32(irp->input, FsInformationClass); + + switch (FsInformationClass) + { + case FileFsVolumeInformation: + /* http://msdn.microsoft.com/en-us/library/cc232108.aspx */ + stream_write_uint32(output, 34); /* Length */ + stream_check_size(output, 34); + stream_write_uint64(output, 0); /* VolumeCreationTime */ + stream_write_uint32(output, 0); /* VolumeSerialNumber */ + stream_write_uint32(output, 16); /* VolumeLabelLength */ + stream_write_uint8(output, 0); /* SupportsObjects */ + stream_write_uint8(output, 0); /* Reserved */ + stream_write(output, "F\0R\0E\0E\0R\0D\0P\0\0\0", 16); /* VolumeLabel (Unicode) */ + break; + + case FileFsSizeInformation: + /* http://msdn.microsoft.com/en-us/library/cc232107.aspx */ + stream_write_uint32(output, 24); /* Length */ + stream_check_size(output, 24); + stream_write_uint64(output, 0x1000000); /* TotalAllocationUnits */ + stream_write_uint64(output, 0x800000); /* AvailableAllocationUnits */ + stream_write_uint32(output, 1); /* SectorsPerAllocationUnit */ + stream_write_uint32(output, 0x400); /* BytesPerSector */ + break; + + case FileFsAttributeInformation: + /* http://msdn.microsoft.com/en-us/library/cc232101.aspx */ + stream_write_uint32(output, 22); /* Length */ + stream_check_size(output, 22); + stream_write_uint32(output, + FILE_CASE_SENSITIVE_SEARCH | + FILE_CASE_PRESERVED_NAMES | + FILE_UNICODE_ON_DISK); /* FileSystemAttributes */ + stream_write_uint32(output, 510); /* MaximumComponentNameLength */ + stream_write_uint32(output, 10); /* FileSystemNameLength */ + stream_write(output, "F\0A\0T\03\02\0", 10); /* FileSystemName */ + break; + + case FileFsFullSizeInformation: + /* http://msdn.microsoft.com/en-us/library/cc232104.aspx */ + stream_write_uint32(output, 32); /* Length */ + stream_check_size(output, 32); + stream_write_uint64(output, 0x1000000); /* TotalAllocationUnits */ + stream_write_uint64(output, 0x800000); /* CallerAvailableAllocationUnits */ + stream_write_uint64(output, 0x800000); /* ActualAvailableAllocationUnits */ + stream_write_uint32(output, 1); /* SectorsPerAllocationUnit */ + stream_write_uint32(output, 0x400); /* BytesPerSector */ + break; + + case FileFsDeviceInformation: + /* http://msdn.microsoft.com/en-us/library/cc232109.aspx */ + stream_write_uint32(output, 8); /* Length */ + stream_check_size(output, 8); + stream_write_uint32(output, FILE_DEVICE_DISK); /* DeviceType */ + stream_write_uint32(output, 0); /* Characteristics */ + break; + + default: + irp->IoStatus = STATUS_UNSUCCESSFUL; + stream_write_uint32(output, 0); /* Length */ + DEBUG_WARN("invalid FsInformationClass %d", FsInformationClass); + break; + } + + irp->Complete(irp); } static void disk_process_irp(DISK_DEVICE* disk, IRP* irp) @@ -256,6 +372,14 @@ static void disk_process_irp(DISK_DEVICE* disk, IRP* irp) disk_process_irp_write(disk, irp); break; + case IRP_MJ_QUERY_INFORMATION: + disk_process_irp_query_information(disk, irp); + break; + + case IRP_MJ_QUERY_VOLUME_INFORMATION: + disk_process_irp_query_volume_information(disk, irp); + break; + default: DEBUG_WARN("MajorFunction 0x%X not supported", irp->MajorFunction); irp->IoStatus = STATUS_NOT_SUPPORTED; From f598bca687b54577ddc19cfaf0807b8d18298e5a Mon Sep 17 00:00:00 2001 From: Vic Lee Date: Sun, 7 Aug 2011 19:24:29 +0800 Subject: [PATCH 07/10] rdpdr/disk: add query directory irp processing. --- channels/rdpdr/disk/disk_file.c | 134 ++++++++++++++++++++++++++++++-- channels/rdpdr/disk/disk_file.h | 3 +- channels/rdpdr/disk/disk_main.c | 61 +++++++++++++++ 3 files changed, 189 insertions(+), 9 deletions(-) diff --git a/channels/rdpdr/disk/disk_file.c b/channels/rdpdr/disk/disk_file.c index 3fad16acc..32b03d237 100644 --- a/channels/rdpdr/disk/disk_file.c +++ b/channels/rdpdr/disk/disk_file.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -247,7 +246,6 @@ void disk_file_free(DISK_FILE* file) } xfree(file->fullpath); - xfree(file->pattern); xfree(file); } @@ -309,26 +307,26 @@ boolean disk_file_query_information(DISK_FILE* file, uint32 FsInformationClass, { case FileBasicInformation: /* http://msdn.microsoft.com/en-us/library/cc232094.aspx */ - stream_write_uint32(output, 40); /* Length */ - stream_check_size(output, 40); + stream_write_uint32(output, 36); /* Length */ + stream_check_size(output, 36); stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */ stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_atime)); /* LastAccessTime */ stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* LastWriteTime */ stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_ctime)); /* ChangeTime */ stream_write_uint32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */ - stream_write_uint32(output, 0); /* Reserved */ + /* Reserved(4), MUST NOT be added! */ break; case FileStandardInformation: /* http://msdn.microsoft.com/en-us/library/cc232088.aspx */ - stream_write_uint32(output, 24); /* Length */ - stream_check_size(output, 24); + stream_write_uint32(output, 22); /* Length */ + stream_check_size(output, 22); stream_write_uint64(output, st.st_size); /* AllocationSize */ stream_write_uint64(output, st.st_size); /* EndOfFile */ stream_write_uint32(output, st.st_nlink); /* NumberOfLinks */ stream_write_uint8(output, file->delete_pending ? 1 : 0); /* DeletePending */ stream_write_uint8(output, file->is_dir ? 1 : 0); /* Directory */ - stream_write_uint16(output, 0); /* Reserved */ + /* Reserved(2), MUST NOT be added! */ break; case FileAttributeTagInformation: @@ -346,3 +344,123 @@ boolean disk_file_query_information(DISK_FILE* file, uint32 FsInformationClass, } return True; } + +boolean disk_file_query_directory(DISK_FILE* file, uint32 FsInformationClass, uint8 InitialQuery, + const char* path, STREAM* output) +{ + struct dirent* ent; + char* ent_path; + struct stat st; + UNICONV* uniconv; + size_t len; + boolean ret; + + DEBUG_SVC("path %s FsInformationClass %d", path, FsInformationClass); + + if (InitialQuery != 0) + { + rewinddir(file->dir); + } + + ent = readdir(file->dir); + if (ent == NULL) + { + stream_write_uint32(output, 0); /* Length */ + stream_write_uint8(output, 0); /* Padding */ + return False; + } + + memset(&st, 0, sizeof(struct stat)); + ent_path = xmalloc(strlen(file->fullpath) + strlen(ent->d_name) + 2); + sprintf(ent_path, "%s/%s", file->fullpath, ent->d_name); + if (stat(ent_path, &st) != 0) + { + DEBUG_WARN("stat %s failed.", ent_path); + } + xfree(ent_path); + + uniconv = freerdp_uniconv_new(); + ent_path = freerdp_uniconv_out(uniconv, ent->d_name, &len); + freerdp_uniconv_free(uniconv); + + ret = True; + switch (FsInformationClass) + { + case FileDirectoryInformation: + /* http://msdn.microsoft.com/en-us/library/cc232097.aspx */ + stream_write_uint32(output, 64 + len); /* Length */ + stream_check_size(output, 64 + len); + stream_write_uint32(output, 0); /* NextEntryOffset */ + stream_write_uint32(output, 0); /* FileIndex */ + stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */ + stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_atime)); /* LastAccessTime */ + stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* LastWriteTime */ + stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_ctime)); /* ChangeTime */ + stream_write_uint64(output, st.st_size); /* EndOfFile */ + stream_write_uint64(output, st.st_size); /* AllocationSize */ + stream_write_uint32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */ + stream_write_uint32(output, len); /* FileNameLength */ + stream_write(output, ent_path, len); + break; + + case FileFullDirectoryInformation: + /* http://msdn.microsoft.com/en-us/library/cc232068.aspx */ + stream_write_uint32(output, 68 + len); /* Length */ + stream_check_size(output, 68 + len); + stream_write_uint32(output, 0); /* NextEntryOffset */ + stream_write_uint32(output, 0); /* FileIndex */ + stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */ + stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_atime)); /* LastAccessTime */ + stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* LastWriteTime */ + stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_ctime)); /* ChangeTime */ + stream_write_uint64(output, st.st_size); /* EndOfFile */ + stream_write_uint64(output, st.st_size); /* AllocationSize */ + stream_write_uint32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */ + stream_write_uint32(output, len); /* FileNameLength */ + stream_write_uint32(output, 0); /* EaSize */ + stream_write(output, ent_path, len); + break; + + case FileBothDirectoryInformation: + /* http://msdn.microsoft.com/en-us/library/cc232095.aspx */ + stream_write_uint32(output, 93 + len); /* Length */ + stream_check_size(output, 93 + len); + stream_write_uint32(output, 0); /* NextEntryOffset */ + stream_write_uint32(output, 0); /* FileIndex */ + stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */ + stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_atime)); /* LastAccessTime */ + stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* LastWriteTime */ + stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_ctime)); /* ChangeTime */ + stream_write_uint64(output, st.st_size); /* EndOfFile */ + stream_write_uint64(output, st.st_size); /* AllocationSize */ + stream_write_uint32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */ + stream_write_uint32(output, len); /* FileNameLength */ + stream_write_uint32(output, 0); /* EaSize */ + stream_write_uint8(output, 0); /* ShortNameLength */ + /* Reserved(1), MUST NOT be added! */ + stream_write_zero(output, 24); /* ShortName */ + stream_write(output, ent_path, len); + break; + + case FileNamesInformation: + /* http://msdn.microsoft.com/en-us/library/cc232077.aspx */ + stream_write_uint32(output, 12 + len); /* Length */ + stream_check_size(output, 12 + len); + stream_write_uint32(output, 0); /* NextEntryOffset */ + stream_write_uint32(output, 0); /* FileIndex */ + stream_write_uint32(output, len); /* FileNameLength */ + stream_write(output, ent_path, len); + break; + + default: + stream_write_uint32(output, 0); /* Length */ + stream_write_uint8(output, 0); /* Padding */ + DEBUG_WARN("invalid FsInformationClass %d", FsInformationClass); + ret = False; + break; + } + + xfree(ent_path); + + return ret; +} diff --git a/channels/rdpdr/disk/disk_file.h b/channels/rdpdr/disk/disk_file.h index 8441647e9..1ba1fc1b3 100644 --- a/channels/rdpdr/disk/disk_file.h +++ b/channels/rdpdr/disk/disk_file.h @@ -34,7 +34,6 @@ struct _DISK_FILE DIR* dir; char* fullpath; char* filename; - char* pattern; boolean delete_pending; }; @@ -46,5 +45,7 @@ boolean disk_file_seek(DISK_FILE* file, uint64 Offset); boolean disk_file_read(DISK_FILE* file, uint8* buffer, uint32* Length); boolean disk_file_write(DISK_FILE* file, uint8* buffer, uint32 Length); boolean disk_file_query_information(DISK_FILE* file, uint32 FsInformationClass, STREAM* output); +boolean disk_file_query_directory(DISK_FILE* file, uint32 FsInformationClass, uint8 InitialQuery, + const char* path, STREAM* output); #endif /* __DISK_FILE_H */ diff --git a/channels/rdpdr/disk/disk_main.c b/channels/rdpdr/disk/disk_main.c index 162a97bef..86dacc713 100644 --- a/channels/rdpdr/disk/disk_main.c +++ b/channels/rdpdr/disk/disk_main.c @@ -352,6 +352,63 @@ static void disk_process_irp_query_volume_information(DISK_DEVICE* disk, IRP* ir irp->Complete(irp); } +static void disk_process_irp_query_directory(DISK_DEVICE* disk, IRP* irp) +{ + DISK_FILE* file; + uint32 FsInformationClass; + uint8 InitialQuery; + uint32 PathLength; + UNICONV* uniconv; + char* path; + + stream_read_uint32(irp->input, FsInformationClass); + stream_read_uint8(irp->input, InitialQuery); + stream_read_uint32(irp->input, PathLength); + stream_seek(irp->input, 23); /* Padding */ + + uniconv = freerdp_uniconv_new(); + path = freerdp_uniconv_in(uniconv, stream_get_tail(irp->input), PathLength); + freerdp_uniconv_free(uniconv); + + file = disk_get_file_by_id(disk, irp->FileId); + + if (file == NULL) + { + irp->IoStatus = STATUS_UNSUCCESSFUL; + stream_write_uint32(irp->output, 0); /* Length */ + DEBUG_WARN("FileId %d not valid.", irp->FileId); + } + else if (!disk_file_query_directory(file, FsInformationClass, InitialQuery, path, irp->output)) + { + irp->IoStatus = STATUS_NO_MORE_FILES; + } + + xfree(path); + + irp->Complete(irp); +} + +static void disk_process_irp_directory_control(DISK_DEVICE* disk, IRP* irp) +{ + switch (irp->MinorFunction) + { + case IRP_MN_QUERY_DIRECTORY: + disk_process_irp_query_directory(disk, irp); + break; + + case IRP_MN_NOTIFY_CHANGE_DIRECTORY: /* TODO */ + irp->Discard(irp); + break; + + default: + DEBUG_WARN("MinorFunction 0x%X not supported", irp->MinorFunction); + irp->IoStatus = STATUS_NOT_SUPPORTED; + stream_write_uint32(irp->output, 0); /* Length */ + irp->Complete(irp); + break; + } +} + static void disk_process_irp(DISK_DEVICE* disk, IRP* irp) { switch (irp->MajorFunction) @@ -380,6 +437,10 @@ static void disk_process_irp(DISK_DEVICE* disk, IRP* irp) disk_process_irp_query_volume_information(disk, irp); break; + case IRP_MJ_DIRECTORY_CONTROL: + disk_process_irp_directory_control(disk, irp); + break; + default: DEBUG_WARN("MajorFunction 0x%X not supported", irp->MajorFunction); irp->IoStatus = STATUS_NOT_SUPPORTED; From 0ac3d908f501eecfdc582f7c1ccd3fac46d718c6 Mon Sep 17 00:00:00 2001 From: Vic Lee Date: Sun, 7 Aug 2011 22:21:34 +0800 Subject: [PATCH 08/10] libfreerdp-utils/stream: add stream_seek_uintt64. --- include/freerdp/utils/stream.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/freerdp/utils/stream.h b/include/freerdp/utils/stream.h index 5f3e2f0cc..1e9e38b62 100644 --- a/include/freerdp/utils/stream.h +++ b/include/freerdp/utils/stream.h @@ -133,6 +133,7 @@ void stream_extend(STREAM* stream); #define stream_seek_uint8(_s) stream_seek(_s, 1) #define stream_seek_uint16(_s) stream_seek(_s, 2) #define stream_seek_uint32(_s) stream_seek(_s, 4) +#define stream_seek_uint64(_s) stream_seek(_s, 8) #define stream_read_uint16_be(_s, _v) do { _v = \ (((uint16)(*_s->p)) << 8) + \ From cc3160cb9d4fbeb2457a5b93516e5b24815434e8 Mon Sep 17 00:00:00 2001 From: Vic Lee Date: Mon, 8 Aug 2011 00:23:36 +0800 Subject: [PATCH 09/10] rdpdr/disk: add set_information and device_control irp processing. --- channels/rdpdr/disk/disk_file.c | 121 +++++++++++++++++++++++++++++--- channels/rdpdr/disk/disk_file.h | 1 + channels/rdpdr/disk/disk_main.c | 58 +++++++++++++-- 3 files changed, 165 insertions(+), 15 deletions(-) diff --git a/channels/rdpdr/disk/disk_file.c b/channels/rdpdr/disk/disk_file.c index 32b03d237..da02074e6 100644 --- a/channels/rdpdr/disk/disk_file.c +++ b/channels/rdpdr/disk/disk_file.c @@ -23,7 +23,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -50,7 +51,7 @@ (_f->delete_pending ? FILE_ATTRIBUTE_TEMPORARY : 0) | \ (st.st_mode & S_IWUSR ? 0 : FILE_ATTRIBUTE_READONLY)) -static char* disk_file_get_fullpath(const char* base_path, const char* path) +static char* disk_file_combine_fullpath(const char* base_path, const char* path) { char* fullpath; int len; @@ -131,7 +132,18 @@ static boolean disk_file_remove_dir(const char* path) return ret; } -boolean disk_file_init(DISK_FILE* file, uint32 DesiredAccess, uint32 CreateDisposition, uint32 CreateOptions) +static void disk_file_set_fullpath(DISK_FILE* file, char* fullpath) +{ + xfree(file->fullpath); + file->fullpath = fullpath; + file->filename = strrchr(file->fullpath, '/'); + if (file->filename == NULL) + file->filename = file->fullpath; + else + file->filename += 1; +} + +static boolean disk_file_init(DISK_FILE* file, uint32 DesiredAccess, uint32 CreateDisposition, uint32 CreateOptions) { const static int mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH; struct stat st; @@ -184,7 +196,7 @@ boolean disk_file_init(DISK_FILE* file, uint32 DesiredAccess, uint32 CreateDispo break; } if (!exists && (CreateOptions & FILE_DELETE_ON_CLOSE)) - file->delete_pending = 1; + file->delete_pending = True; if ((DesiredAccess & GENERIC_ALL) || (DesiredAccess & GENERIC_WRITE) @@ -213,12 +225,7 @@ DISK_FILE* disk_file_new(const char* base_path, const char* path, uint32 id, file = xnew(DISK_FILE); file->id = id; - file->fullpath = disk_file_get_fullpath(base_path, path); - file->filename = strrchr(file->fullpath, '/'); - if (file->filename == NULL) - file->filename = file->fullpath; - else - file->filename += 1; + disk_file_set_fullpath(file, disk_file_combine_fullpath(base_path, path)); file->fd = -1; if (!disk_file_init(file, DesiredAccess, CreateDisposition, CreateOptions)) @@ -345,6 +352,100 @@ boolean disk_file_query_information(DISK_FILE* file, uint32 FsInformationClass, return True; } +boolean disk_file_set_information(DISK_FILE* file, uint32 FsInformationClass, uint32 Length, STREAM* input) +{ + struct stat st; + struct timeval tv[2]; + uint64 LastWriteTime; + uint32 FileAttributes; + mode_t m; + uint64 size; + uint32 FileNameLength; + UNICONV* uniconv; + char* s; + char* p; + char* fullpath; + + switch (FsInformationClass) + { + 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_uint32(input, FileAttributes); + + if (fstat(file->fd, &st) != 0) + return False; + tv[0].tv_sec = st.st_atime; + tv[0].tv_usec = 0; + tv[1].tv_sec = (LastWriteTime > 0 ? FILE_TIME_RDP_TO_SYSTEM(LastWriteTime) : st.st_mtime); + tv[1].tv_usec = 0; + futimes(file->fd, tv); + + if (FileAttributes > 0) + { + 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); + } + 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); + ftruncate(file->fd, size); + break; + + case FileDispositionInformation: + /* http://msdn.microsoft.com/en-us/library/cc232098.aspx */ + stream_read_uint8(input, file->delete_pending); + break; + + case FileRenameInformation: + /* http://msdn.microsoft.com/en-us/library/cc232085.aspx */ + stream_seek_uint8(input); /* ReplaceIfExists */ + stream_seek_uint8(input); /* RootDirectory */ + stream_read_uint32(input, FileNameLength); + uniconv = freerdp_uniconv_new(); + s = freerdp_uniconv_in(uniconv, stream_get_tail(input), FileNameLength); + freerdp_uniconv_free(uniconv); + fullpath = xmalloc(strlen(file->fullpath) + strlen(s) + 2); + strcpy(fullpath, file->fullpath); + p = strrchr(fullpath, '/'); + if (p == NULL) + p = fullpath; + else + p++; + strcpy(p, s); + xfree(s); + + if (rename(file->fullpath, fullpath) == 0) + { + disk_file_set_fullpath(file, fullpath); + } + else + { + free(fullpath); + return False; + } + + break; + + default: + DEBUG_WARN("invalid FsInformationClass %d", FsInformationClass); + return False; + } + return True; +} + boolean disk_file_query_directory(DISK_FILE* file, uint32 FsInformationClass, uint8 InitialQuery, const char* path, STREAM* output) { diff --git a/channels/rdpdr/disk/disk_file.h b/channels/rdpdr/disk/disk_file.h index 1ba1fc1b3..950fa3632 100644 --- a/channels/rdpdr/disk/disk_file.h +++ b/channels/rdpdr/disk/disk_file.h @@ -45,6 +45,7 @@ boolean disk_file_seek(DISK_FILE* file, uint64 Offset); boolean disk_file_read(DISK_FILE* file, uint8* buffer, uint32* Length); boolean disk_file_write(DISK_FILE* file, uint8* buffer, uint32 Length); boolean disk_file_query_information(DISK_FILE* file, uint32 FsInformationClass, STREAM* output); +boolean disk_file_set_information(DISK_FILE* file, uint32 FsInformationClass, uint32 Length, STREAM* input); boolean disk_file_query_directory(DISK_FILE* file, uint32 FsInformationClass, uint8 InitialQuery, const char* path, STREAM* output); diff --git a/channels/rdpdr/disk/disk_main.c b/channels/rdpdr/disk/disk_main.c index 86dacc713..1bf546e08 100644 --- a/channels/rdpdr/disk/disk_main.c +++ b/channels/rdpdr/disk/disk_main.c @@ -146,7 +146,7 @@ static void disk_process_irp_close(DISK_DEVICE* disk, IRP* irp) disk_file_free(file); } - stream_write(irp->output, "\0\0\0\0\0", 5); /* Padding(5) */ + stream_write_zero(irp->output, 5); /* Padding(5) */ irp->Complete(irp); } @@ -191,7 +191,7 @@ static void disk_process_irp_read(DISK_DEVICE* disk, IRP* irp) } else { - DEBUG_SVC("read %u-%u from %s(%d).", Offset, Offset + Length, file->fullpath, file->id); + DEBUG_SVC("read %llu-%llu from %s(%d).", Offset, Offset + Length, file->fullpath, file->id); } } @@ -241,7 +241,7 @@ static void disk_process_irp_write(DISK_DEVICE* disk, IRP* irp) } else { - DEBUG_SVC("write %u-%u to %s(%d).", Offset, Offset + Length, file->fullpath, file->id); + DEBUG_SVC("write %llu-%llu to %s(%d).", Offset, Offset + Length, file->fullpath, file->id); } stream_write_uint32(irp->output, Length); @@ -269,16 +269,50 @@ static void disk_process_irp_query_information(DISK_DEVICE* disk, IRP* irp) { irp->IoStatus = STATUS_UNSUCCESSFUL; - DEBUG_WARN("query_information %s(%d) failed.", file->fullpath, file->id); + DEBUG_WARN("FsInformationClass %d on %s(%d) failed.", FsInformationClass, file->fullpath, file->id); } else { - DEBUG_SVC("query_information %d on %s(%d).", FsInformationClass, file->fullpath, file->id); + DEBUG_SVC("FsInformationClass %d on %s(%d).", FsInformationClass, file->fullpath, file->id); } irp->Complete(irp); } +static void disk_process_irp_set_information(DISK_DEVICE* disk, IRP* irp) +{ + DISK_FILE* file; + uint32 FsInformationClass; + uint32 Length; + + stream_read_uint32(irp->input, FsInformationClass); + stream_read_uint32(irp->input, Length); + stream_seek(irp->input, 24); /* Padding */ + + file = disk_get_file_by_id(disk, irp->FileId); + + if (file == NULL) + { + irp->IoStatus = STATUS_UNSUCCESSFUL; + + DEBUG_WARN("FileId %d not valid.", irp->FileId); + } + else if (!disk_file_set_information(file, FsInformationClass, Length, irp->input)) + { + irp->IoStatus = STATUS_UNSUCCESSFUL; + + DEBUG_WARN("FsInformationClass %d on %s(%d) failed.", FsInformationClass, file->fullpath, file->id); + } + else + { + DEBUG_SVC("FsInformationClass %d on %s(%d) ok.", FsInformationClass, file->fullpath, file->id); + } + + stream_write_uint32(irp->output, Length); + + irp->Complete(irp); +} + static void disk_process_irp_query_volume_information(DISK_DEVICE* disk, IRP* irp) { uint32 FsInformationClass; @@ -409,6 +443,12 @@ static void disk_process_irp_directory_control(DISK_DEVICE* disk, IRP* irp) } } +static void disk_process_irp_device_control(DISK_DEVICE* disk, IRP* irp) +{ + stream_write_uint32(irp->output, 0); /* OutputBufferLength */ + irp->Complete(irp); +} + static void disk_process_irp(DISK_DEVICE* disk, IRP* irp) { switch (irp->MajorFunction) @@ -433,6 +473,10 @@ static void disk_process_irp(DISK_DEVICE* disk, IRP* irp) disk_process_irp_query_information(disk, irp); break; + case IRP_MJ_SET_INFORMATION: + disk_process_irp_set_information(disk, irp); + break; + case IRP_MJ_QUERY_VOLUME_INFORMATION: disk_process_irp_query_volume_information(disk, irp); break; @@ -441,6 +485,10 @@ static void disk_process_irp(DISK_DEVICE* disk, IRP* irp) disk_process_irp_directory_control(disk, irp); break; + case IRP_MJ_DEVICE_CONTROL: + disk_process_irp_device_control(disk, irp); + break; + default: DEBUG_WARN("MajorFunction 0x%X not supported", irp->MajorFunction); irp->IoStatus = STATUS_NOT_SUPPORTED; From e4585c801e7942112f9dd5b2df9a6b7eb3a7376e Mon Sep 17 00:00:00 2001 From: Vic Lee Date: Mon, 8 Aug 2011 00:37:31 +0800 Subject: [PATCH 10/10] rdpdr/disk: fix rename. --- channels/rdpdr/disk/disk_file.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/channels/rdpdr/disk/disk_file.c b/channels/rdpdr/disk/disk_file.c index da02074e6..486cf57ef 100644 --- a/channels/rdpdr/disk/disk_file.c +++ b/channels/rdpdr/disk/disk_file.c @@ -51,23 +51,29 @@ (_f->delete_pending ? FILE_ATTRIBUTE_TEMPORARY : 0) | \ (st.st_mode & S_IWUSR ? 0 : FILE_ATTRIBUTE_READONLY)) +static void disk_file_fix_path(char* path) +{ + int len; + int i; + + len = strlen(path); + for (i = 0; i < len; i++) + { + if (path[i] == '\\') + path[i] = '/'; + } + if (len > 0 && path[len - 1] == '/') + path[len - 1] = '\0'; +} + static char* disk_file_combine_fullpath(const char* base_path, const char* path) { char* fullpath; - int len; - int i; fullpath = xmalloc(strlen(base_path) + strlen(path) + 1); strcpy(fullpath, base_path); strcat(fullpath, path); - len = strlen(fullpath); - for (i = 0; i < len; i++) - { - if (fullpath[i] == '\\') - fullpath[i] = '/'; - } - if (len > 0 && fullpath[len - 1] == '/') - fullpath[len - 1] = '\0'; + disk_file_fix_path(fullpath); return fullpath; } @@ -424,15 +430,18 @@ boolean disk_file_set_information(DISK_FILE* file, uint32 FsInformationClass, ui p = fullpath; else p++; - strcpy(p, s); + strcpy(p, s[0] == '\\' || s[0] == '/' ? s + 1 : s); xfree(s); + disk_file_fix_path(fullpath); if (rename(file->fullpath, fullpath) == 0) { + DEBUG_SVC("renamed %s to %s", file->fullpath, fullpath); disk_file_set_fullpath(file, fullpath); } else { + DEBUG_WARN("rename %s to %s failed", file->fullpath, fullpath); free(fullpath); return False; }