FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
drive_main.c
1
24#include <freerdp/config.h>
25
26#include <errno.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#include <winpr/crt.h>
32#include <winpr/assert.h>
33#include <winpr/path.h>
34#include <winpr/file.h>
35#include <winpr/string.h>
36#include <winpr/synch.h>
37#include <winpr/thread.h>
38#include <winpr/stream.h>
39#include <winpr/environment.h>
40#include <winpr/interlocked.h>
41#include <winpr/collections.h>
42#include <winpr/shell.h>
43
44#include <freerdp/freerdp.h>
45#include <freerdp/channels/rdpdr.h>
46
47#include "drive_file.h"
48
49typedef struct
50{
51 DEVICE device;
52
53 WCHAR* path;
54 BOOL automount;
55 UINT32 PathLength;
56 wListDictionary* files;
57
58 HANDLE thread;
59 BOOL async;
60 wMessageQueue* IrpQueue;
61
62 DEVMAN* devman;
63
64 rdpContext* rdpcontext;
65} DRIVE_DEVICE;
66
67static NTSTATUS drive_map_windows_err(DWORD fs_errno)
68{
69 NTSTATUS rc = 0;
70
71 /* try to return NTSTATUS version of error code */
72
73 switch (fs_errno)
74 {
75 case STATUS_SUCCESS:
76 rc = STATUS_SUCCESS;
77 break;
78
79 case ERROR_ACCESS_DENIED:
80 case ERROR_SHARING_VIOLATION:
81 rc = STATUS_ACCESS_DENIED;
82 break;
83
84 case ERROR_FILE_NOT_FOUND:
85 rc = STATUS_NO_SUCH_FILE;
86 break;
87
88 case ERROR_BUSY_DRIVE:
89 rc = STATUS_DEVICE_BUSY;
90 break;
91
92 case ERROR_INVALID_DRIVE:
93 rc = STATUS_NO_SUCH_DEVICE;
94 break;
95
96 case ERROR_NOT_READY:
97 rc = STATUS_NO_SUCH_DEVICE;
98 break;
99
100 case ERROR_FILE_EXISTS:
101 case ERROR_ALREADY_EXISTS:
102 rc = STATUS_OBJECT_NAME_COLLISION;
103 break;
104
105 case ERROR_INVALID_NAME:
106 rc = STATUS_NO_SUCH_FILE;
107 break;
108
109 case ERROR_INVALID_HANDLE:
110 rc = STATUS_INVALID_HANDLE;
111 break;
112
113 case ERROR_NO_MORE_FILES:
114 rc = STATUS_NO_MORE_FILES;
115 break;
116
117 case ERROR_DIRECTORY:
118 rc = STATUS_NOT_A_DIRECTORY;
119 break;
120
121 case ERROR_PATH_NOT_FOUND:
122 rc = STATUS_OBJECT_PATH_NOT_FOUND;
123 break;
124
125 default:
126 rc = STATUS_UNSUCCESSFUL;
127 WLog_ERR(TAG, "Error code not found: %" PRIu32 "", fs_errno);
128 break;
129 }
130
131 return rc;
132}
133
134static DRIVE_FILE* drive_get_file_by_id(DRIVE_DEVICE* drive, UINT32 id)
135{
136 DRIVE_FILE* file = NULL;
137 void* key = (void*)(size_t)id;
138
139 if (!drive)
140 return NULL;
141
142 file = (DRIVE_FILE*)ListDictionary_GetItemValue(drive->files, key);
143 return file;
144}
145
151static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp)
152{
153 UINT32 FileId = 0;
154 DRIVE_FILE* file = NULL;
155 BYTE Information = 0;
156 const WCHAR* path = NULL;
157
158 if (!drive || !irp || !irp->devman || !irp->Complete)
159 return ERROR_INVALID_PARAMETER;
160
161 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 6 * 4 + 8))
162 return ERROR_INVALID_DATA;
163
164 const uint32_t DesiredAccess = Stream_Get_UINT32(irp->input);
165 const uint64_t allocationSize = Stream_Get_UINT64(irp->input);
166 const uint32_t FileAttributes = Stream_Get_UINT32(irp->input);
167 const uint32_t SharedAccess = Stream_Get_UINT32(irp->input);
168 const uint32_t CreateDisposition = Stream_Get_UINT32(irp->input);
169 const uint32_t CreateOptions = Stream_Get_UINT32(irp->input);
170 const uint32_t PathLength = Stream_Get_UINT32(irp->input);
171
172 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, PathLength))
173 return ERROR_INVALID_DATA;
174
175 path = Stream_ConstPointer(irp->input);
176 FileId = irp->devman->id_sequence++;
177 file = drive_file_new(drive->path, path, PathLength / sizeof(WCHAR), FileId, DesiredAccess,
178 CreateDisposition, CreateOptions, FileAttributes, SharedAccess);
179
180 if (!file)
181 {
182 irp->IoStatus = drive_map_windows_err(GetLastError());
183 FileId = 0;
184 Information = 0;
185 }
186 else
187 {
188 void* key = (void*)(size_t)file->id;
189
190 if (!ListDictionary_Add(drive->files, key, file))
191 {
192 WLog_ERR(TAG, "ListDictionary_Add failed!");
193 return ERROR_INTERNAL_ERROR;
194 }
195
196 switch (CreateDisposition)
197 {
198 case FILE_SUPERSEDE:
199 case FILE_OPEN:
200 case FILE_CREATE:
201 case FILE_OVERWRITE:
202 Information = FILE_SUPERSEDED;
203 break;
204
205 case FILE_OPEN_IF:
206 Information = FILE_OPENED;
207 break;
208
209 case FILE_OVERWRITE_IF:
210 Information = FILE_OVERWRITTEN;
211 break;
212
213 default:
214 Information = 0;
215 break;
216 }
217
218 if (allocationSize > 0)
219 {
220 const BYTE buffer[] = { '\0' };
221 if (!drive_file_seek(file, allocationSize - sizeof(buffer)))
222 return ERROR_INTERNAL_ERROR;
223 if (!drive_file_write(file, buffer, sizeof(buffer)))
224 return ERROR_INTERNAL_ERROR;
225 }
226 }
227
228 Stream_Write_UINT32(irp->output, FileId);
229 Stream_Write_UINT8(irp->output, Information);
230 return irp->Complete(irp);
231}
232
238static UINT drive_process_irp_close(DRIVE_DEVICE* drive, IRP* irp)
239{
240 void* key = NULL;
241 DRIVE_FILE* file = NULL;
242
243 if (!drive || !irp || !irp->Complete || !irp->output)
244 return ERROR_INVALID_PARAMETER;
245
246 file = drive_get_file_by_id(drive, irp->FileId);
247 key = (void*)(size_t)irp->FileId;
248
249 if (!file)
250 irp->IoStatus = STATUS_UNSUCCESSFUL;
251 else
252 {
253 ListDictionary_Take(drive->files, key);
254
255 if (drive_file_free(file))
256 irp->IoStatus = STATUS_SUCCESS;
257 else
258 irp->IoStatus = drive_map_windows_err(GetLastError());
259 }
260
261 Stream_Zero(irp->output, 5); /* Padding(5) */
262 return irp->Complete(irp);
263}
264
270static UINT drive_process_irp_read(DRIVE_DEVICE* drive, IRP* irp)
271{
272 DRIVE_FILE* file = NULL;
273 UINT32 Length = 0;
274 UINT64 Offset = 0;
275
276 if (!drive || !irp || !irp->output || !irp->Complete)
277 return ERROR_INVALID_PARAMETER;
278
279 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12))
280 return ERROR_INVALID_DATA;
281
282 Stream_Read_UINT32(irp->input, Length);
283 Stream_Read_UINT64(irp->input, Offset);
284 file = drive_get_file_by_id(drive, irp->FileId);
285
286 if (!file)
287 {
288 irp->IoStatus = STATUS_UNSUCCESSFUL;
289 Length = 0;
290 }
291 else if (!drive_file_seek(file, Offset))
292 {
293 irp->IoStatus = drive_map_windows_err(GetLastError());
294 Length = 0;
295 }
296
297 if (!Stream_EnsureRemainingCapacity(irp->output, Length + 4))
298 {
299 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
300 return ERROR_INTERNAL_ERROR;
301 }
302 else if (Length == 0)
303 Stream_Write_UINT32(irp->output, 0);
304 else
305 {
306 BYTE* buffer = Stream_PointerAs(irp->output, BYTE) + sizeof(UINT32);
307
308 if (!drive_file_read(file, buffer, &Length))
309 {
310 irp->IoStatus = drive_map_windows_err(GetLastError());
311 Stream_Write_UINT32(irp->output, 0);
312 }
313 else
314 {
315 Stream_Write_UINT32(irp->output, Length);
316 Stream_Seek(irp->output, Length);
317 }
318 }
319
320 return irp->Complete(irp);
321}
322
328static UINT drive_process_irp_write(DRIVE_DEVICE* drive, IRP* irp)
329{
330 DRIVE_FILE* file = NULL;
331 UINT32 Length = 0;
332 UINT64 Offset = 0;
333
334 if (!drive || !irp || !irp->input || !irp->output || !irp->Complete)
335 return ERROR_INVALID_PARAMETER;
336
337 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
338 return ERROR_INVALID_DATA;
339
340 Stream_Read_UINT32(irp->input, Length);
341 Stream_Read_UINT64(irp->input, Offset);
342 Stream_Seek(irp->input, 20); /* Padding */
343 const void* ptr = Stream_ConstPointer(irp->input);
344 if (!Stream_SafeSeek(irp->input, Length))
345 return ERROR_INVALID_DATA;
346 file = drive_get_file_by_id(drive, irp->FileId);
347
348 if (!file)
349 {
350 irp->IoStatus = STATUS_UNSUCCESSFUL;
351 Length = 0;
352 }
353 else if (!drive_file_seek(file, Offset))
354 {
355 irp->IoStatus = drive_map_windows_err(GetLastError());
356 Length = 0;
357 }
358 else if (!drive_file_write(file, ptr, Length))
359 {
360 irp->IoStatus = drive_map_windows_err(GetLastError());
361 Length = 0;
362 }
363
364 Stream_Write_UINT32(irp->output, Length);
365 Stream_Write_UINT8(irp->output, 0); /* Padding */
366 return irp->Complete(irp);
367}
368
374static UINT drive_process_irp_query_information(DRIVE_DEVICE* drive, IRP* irp)
375{
376 DRIVE_FILE* file = NULL;
377 UINT32 FsInformationClass = 0;
378
379 if (!drive || !irp || !irp->Complete)
380 return ERROR_INVALID_PARAMETER;
381
382 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
383 return ERROR_INVALID_DATA;
384
385 Stream_Read_UINT32(irp->input, FsInformationClass);
386 file = drive_get_file_by_id(drive, irp->FileId);
387
388 if (!file)
389 {
390 irp->IoStatus = STATUS_UNSUCCESSFUL;
391 }
392 else if (!drive_file_query_information(file, FsInformationClass, irp->output))
393 {
394 irp->IoStatus = drive_map_windows_err(GetLastError());
395 }
396
397 return irp->Complete(irp);
398}
399
405static UINT drive_process_irp_set_information(DRIVE_DEVICE* drive, IRP* irp)
406{
407 DRIVE_FILE* file = NULL;
408 UINT32 FsInformationClass = 0;
409 UINT32 Length = 0;
410
411 if (!drive || !irp || !irp->Complete || !irp->input || !irp->output)
412 return ERROR_INVALID_PARAMETER;
413
414 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
415 return ERROR_INVALID_DATA;
416
417 Stream_Read_UINT32(irp->input, FsInformationClass);
418 Stream_Read_UINT32(irp->input, Length);
419 Stream_Seek(irp->input, 24); /* Padding */
420 file = drive_get_file_by_id(drive, irp->FileId);
421
422 if (!file)
423 {
424 irp->IoStatus = STATUS_UNSUCCESSFUL;
425 }
426 else if (!drive_file_set_information(file, FsInformationClass, Length, irp->input))
427 {
428 irp->IoStatus = drive_map_windows_err(GetLastError());
429 }
430
431 if (file && file->is_dir && !PathIsDirectoryEmptyW(file->fullpath))
432 irp->IoStatus = STATUS_DIRECTORY_NOT_EMPTY;
433
434 Stream_Write_UINT32(irp->output, Length);
435 return irp->Complete(irp);
436}
437
443static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive, IRP* irp)
444{
445 UINT32 FsInformationClass = 0;
446 wStream* output = NULL;
447 DWORD lpSectorsPerCluster = 0;
448 DWORD lpBytesPerSector = 0;
449 DWORD lpNumberOfFreeClusters = 0;
450 DWORD lpTotalNumberOfClusters = 0;
451 WIN32_FILE_ATTRIBUTE_DATA wfad = { 0 };
452 WCHAR LabelBuffer[32] = { 0 };
453
454 if (!drive || !irp)
455 return ERROR_INVALID_PARAMETER;
456
457 output = irp->output;
458
459 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
460 return ERROR_INVALID_DATA;
461
462 Stream_Read_UINT32(irp->input, FsInformationClass);
463 GetDiskFreeSpaceW(drive->path, &lpSectorsPerCluster, &lpBytesPerSector, &lpNumberOfFreeClusters,
464 &lpTotalNumberOfClusters);
465
466 switch (FsInformationClass)
467 {
468 case FileFsVolumeInformation:
469 {
470 /* http://msdn.microsoft.com/en-us/library/cc232108.aspx */
471 const WCHAR* volumeLabel =
472 InitializeConstWCharFromUtf8("FREERDP", LabelBuffer, ARRAYSIZE(LabelBuffer));
473 const size_t volumeLabelLen = (_wcslen(volumeLabel) + 1) * sizeof(WCHAR);
474 const size_t length = 17ul + volumeLabelLen;
475
476 if ((length > UINT32_MAX) || (volumeLabelLen > UINT32_MAX))
477 return CHANNEL_RC_NO_BUFFER;
478
479 Stream_Write_UINT32(output, (UINT32)length); /* Length */
480
481 if (!Stream_EnsureRemainingCapacity(output, length))
482 {
483 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
484 return CHANNEL_RC_NO_MEMORY;
485 }
486
487 GetFileAttributesExW(drive->path, GetFileExInfoStandard, &wfad);
488 Stream_Write_UINT32(output, wfad.ftCreationTime.dwLowDateTime); /* VolumeCreationTime */
489 Stream_Write_UINT32(output,
490 wfad.ftCreationTime.dwHighDateTime); /* VolumeCreationTime */
491 Stream_Write_UINT32(output, lpNumberOfFreeClusters & 0xffff); /* VolumeSerialNumber */
492 Stream_Write_UINT32(output, (UINT32)volumeLabelLen); /* VolumeLabelLength */
493 Stream_Write_UINT8(output, 0); /* SupportsObjects */
494 /* Reserved(1), MUST NOT be added! */
495 Stream_Write(output, volumeLabel, volumeLabelLen); /* VolumeLabel (Unicode) */
496 }
497 break;
498
499 case FileFsSizeInformation:
500 /* http://msdn.microsoft.com/en-us/library/cc232107.aspx */
501 Stream_Write_UINT32(output, 24); /* Length */
502
503 if (!Stream_EnsureRemainingCapacity(output, 24))
504 {
505 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
506 return CHANNEL_RC_NO_MEMORY;
507 }
508
509 Stream_Write_UINT64(output, lpTotalNumberOfClusters); /* TotalAllocationUnits */
510 Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */
511 Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */
512 Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */
513 break;
514
515 case FileFsAttributeInformation:
516 {
517 /* http://msdn.microsoft.com/en-us/library/cc232101.aspx */
518 const WCHAR* diskType =
519 InitializeConstWCharFromUtf8("FAT32", LabelBuffer, ARRAYSIZE(LabelBuffer));
520 const size_t diskTypeLen = (_wcslen(diskType) + 1) * sizeof(WCHAR);
521 const size_t length = 12ul + diskTypeLen;
522
523 if ((length > UINT32_MAX) || (diskTypeLen > UINT32_MAX))
524 return CHANNEL_RC_NO_BUFFER;
525
526 Stream_Write_UINT32(output, (UINT32)length); /* Length */
527
528 if (!Stream_EnsureRemainingCapacity(output, length))
529 {
530 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
531 return CHANNEL_RC_NO_MEMORY;
532 }
533
534 Stream_Write_UINT32(output, FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES |
535 FILE_UNICODE_ON_DISK); /* FileSystemAttributes */
536 Stream_Write_UINT32(output, MAX_PATH); /* MaximumComponentNameLength */
537 Stream_Write_UINT32(output, (UINT32)diskTypeLen); /* FileSystemNameLength */
538 Stream_Write(output, diskType, diskTypeLen); /* FileSystemName (Unicode) */
539 }
540 break;
541
542 case FileFsFullSizeInformation:
543 /* http://msdn.microsoft.com/en-us/library/cc232104.aspx */
544 Stream_Write_UINT32(output, 32); /* Length */
545
546 if (!Stream_EnsureRemainingCapacity(output, 32))
547 {
548 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
549 return CHANNEL_RC_NO_MEMORY;
550 }
551
552 Stream_Write_UINT64(output, lpTotalNumberOfClusters); /* TotalAllocationUnits */
553 Stream_Write_UINT64(output,
554 lpNumberOfFreeClusters); /* CallerAvailableAllocationUnits */
555 Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */
556 Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */
557 Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */
558 break;
559
560 case FileFsDeviceInformation:
561 /* http://msdn.microsoft.com/en-us/library/cc232109.aspx */
562 Stream_Write_UINT32(output, 8); /* Length */
563
564 if (!Stream_EnsureRemainingCapacity(output, 8))
565 {
566 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
567 return CHANNEL_RC_NO_MEMORY;
568 }
569
570 Stream_Write_UINT32(output, FILE_DEVICE_DISK); /* DeviceType */
571 Stream_Write_UINT32(output, 0); /* Characteristics */
572 break;
573
574 default:
575 WLog_WARN(TAG, "Unhandled FSInformationClass %s [0x%08" PRIx32 "]",
576 FSInformationClass2Tag(FsInformationClass), FsInformationClass);
577 irp->IoStatus = STATUS_UNSUCCESSFUL;
578 Stream_Write_UINT32(output, 0); /* Length */
579 break;
580 }
581
582 return irp->Complete(irp);
583}
584
585/* http://msdn.microsoft.com/en-us/library/cc241518.aspx */
586
592static UINT drive_process_irp_silent_ignore(DRIVE_DEVICE* drive, IRP* irp)
593{
594 if (!drive || !irp || !irp->output || !irp->Complete)
595 return ERROR_INVALID_PARAMETER;
596
597 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
598 return ERROR_INVALID_DATA;
599
600 const uint32_t FsInformationClass = Stream_Get_UINT32(irp->input);
601 WLog_VRB(TAG, "Silently ignore FSInformationClass %s [0x%08" PRIx32 "]",
602 FSInformationClass2Tag(FsInformationClass), FsInformationClass);
603 Stream_Write_UINT32(irp->output, 0); /* Length */
604 return irp->Complete(irp);
605}
606
612static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp)
613{
614 const WCHAR* path = NULL;
615 DRIVE_FILE* file = NULL;
616 BYTE InitialQuery = 0;
617 UINT32 PathLength = 0;
618 UINT32 FsInformationClass = 0;
619
620 if (!drive || !irp || !irp->Complete)
621 return ERROR_INVALID_PARAMETER;
622
623 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
624 return ERROR_INVALID_DATA;
625
626 Stream_Read_UINT32(irp->input, FsInformationClass);
627 Stream_Read_UINT8(irp->input, InitialQuery);
628 Stream_Read_UINT32(irp->input, PathLength);
629 Stream_Seek(irp->input, 23); /* Padding */
630 path = Stream_ConstPointer(irp->input);
631 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, PathLength))
632 return ERROR_INVALID_DATA;
633
634 file = drive_get_file_by_id(drive, irp->FileId);
635
636 if (file == NULL)
637 {
638 irp->IoStatus = STATUS_UNSUCCESSFUL;
639 Stream_Write_UINT32(irp->output, 0); /* Length */
640 }
641 else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path,
642 PathLength / sizeof(WCHAR), irp->output))
643 {
644 irp->IoStatus = drive_map_windows_err(GetLastError());
645 }
646
647 return irp->Complete(irp);
648}
649
655static UINT drive_process_irp_directory_control(DRIVE_DEVICE* drive, IRP* irp)
656{
657 if (!drive || !irp)
658 return ERROR_INVALID_PARAMETER;
659
660 switch (irp->MinorFunction)
661 {
662 case IRP_MN_QUERY_DIRECTORY:
663 return drive_process_irp_query_directory(drive, irp);
664
665 case IRP_MN_NOTIFY_CHANGE_DIRECTORY: /* TODO */
666 return irp->Discard(irp);
667
668 default:
669 irp->IoStatus = STATUS_NOT_SUPPORTED;
670 Stream_Write_UINT32(irp->output, 0); /* Length */
671 return irp->Complete(irp);
672 }
673
674 return CHANNEL_RC_OK;
675}
676
682static UINT drive_process_irp_device_control(DRIVE_DEVICE* drive, IRP* irp)
683{
684 if (!drive || !irp)
685 return ERROR_INVALID_PARAMETER;
686
687 Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
688 return irp->Complete(irp);
689}
690
696static UINT drive_process_irp(DRIVE_DEVICE* drive, IRP* irp)
697{
698 UINT error = 0;
699
700 if (!drive || !irp)
701 return ERROR_INVALID_PARAMETER;
702
703 irp->IoStatus = STATUS_SUCCESS;
704
705 switch (irp->MajorFunction)
706 {
707 case IRP_MJ_CREATE:
708 error = drive_process_irp_create(drive, irp);
709 break;
710
711 case IRP_MJ_CLOSE:
712 error = drive_process_irp_close(drive, irp);
713 break;
714
715 case IRP_MJ_READ:
716 error = drive_process_irp_read(drive, irp);
717 break;
718
719 case IRP_MJ_WRITE:
720 error = drive_process_irp_write(drive, irp);
721 break;
722
723 case IRP_MJ_QUERY_INFORMATION:
724 error = drive_process_irp_query_information(drive, irp);
725 break;
726
727 case IRP_MJ_SET_INFORMATION:
728 error = drive_process_irp_set_information(drive, irp);
729 break;
730
731 case IRP_MJ_QUERY_VOLUME_INFORMATION:
732 error = drive_process_irp_query_volume_information(drive, irp);
733 break;
734
735 case IRP_MJ_LOCK_CONTROL:
736 error = drive_process_irp_silent_ignore(drive, irp);
737 break;
738
739 case IRP_MJ_DIRECTORY_CONTROL:
740 error = drive_process_irp_directory_control(drive, irp);
741 break;
742
743 case IRP_MJ_DEVICE_CONTROL:
744 error = drive_process_irp_device_control(drive, irp);
745 break;
746
747 default:
748 irp->IoStatus = STATUS_NOT_SUPPORTED;
749 error = irp->Complete(irp);
750 break;
751 }
752
753 return error;
754}
755
756static BOOL drive_poll_run(DRIVE_DEVICE* drive, IRP* irp)
757{
758 WINPR_ASSERT(drive);
759
760 if (irp)
761 {
762 const UINT error = drive_process_irp(drive, irp);
763 if (error)
764 {
765 WLog_ERR(TAG, "drive_process_irp failed with error %" PRIu32 "!", error);
766 return FALSE;
767 }
768 }
769
770 return TRUE;
771}
772
773static DWORD WINAPI drive_thread_func(LPVOID arg)
774{
775 DRIVE_DEVICE* drive = (DRIVE_DEVICE*)arg;
776 UINT error = CHANNEL_RC_OK;
777
778 if (!drive)
779 {
780 error = ERROR_INVALID_PARAMETER;
781 goto fail;
782 }
783
784 while (1)
785 {
786 if (!MessageQueue_Wait(drive->IrpQueue))
787 {
788 WLog_ERR(TAG, "MessageQueue_Wait failed!");
789 error = ERROR_INTERNAL_ERROR;
790 break;
791 }
792
793 if (MessageQueue_Size(drive->IrpQueue) < 1)
794 continue;
795
796 wMessage message = { 0 };
797 if (!MessageQueue_Peek(drive->IrpQueue, &message, TRUE))
798 {
799 WLog_ERR(TAG, "MessageQueue_Peek failed!");
800 continue;
801 }
802
803 if (message.id == WMQ_QUIT)
804 break;
805
806 IRP* irp = (IRP*)message.wParam;
807 if (!drive_poll_run(drive, irp))
808 break;
809 }
810
811fail:
812
813 if (error && drive && drive->rdpcontext)
814 setChannelError(drive->rdpcontext, error, "drive_thread_func reported an error");
815
816 ExitThread(error);
817 return error;
818}
819
825static UINT drive_irp_request(DEVICE* device, IRP* irp)
826{
827 DRIVE_DEVICE* drive = (DRIVE_DEVICE*)device;
828
829 if (!drive)
830 return ERROR_INVALID_PARAMETER;
831
832 if (drive->async)
833 {
834 if (!MessageQueue_Post(drive->IrpQueue, NULL, 0, (void*)irp, NULL))
835 {
836 WLog_ERR(TAG, "MessageQueue_Post failed!");
837 return ERROR_INTERNAL_ERROR;
838 }
839 }
840 else
841 {
842 if (!drive_poll_run(drive, irp))
843 return ERROR_INTERNAL_ERROR;
844 }
845
846 return CHANNEL_RC_OK;
847}
848
849static UINT drive_free_int(DRIVE_DEVICE* drive)
850{
851 UINT error = CHANNEL_RC_OK;
852
853 if (!drive)
854 return ERROR_INVALID_PARAMETER;
855
856 (void)CloseHandle(drive->thread);
857 ListDictionary_Free(drive->files);
858 MessageQueue_Free(drive->IrpQueue);
859 Stream_Free(drive->device.data, TRUE);
860 free(drive->path);
861 free(drive);
862 return error;
863}
864
870static UINT drive_free(DEVICE* device)
871{
872 DRIVE_DEVICE* drive = (DRIVE_DEVICE*)device;
873 UINT error = CHANNEL_RC_OK;
874
875 if (!drive)
876 return ERROR_INVALID_PARAMETER;
877
878 if (MessageQueue_PostQuit(drive->IrpQueue, 0) &&
879 (WaitForSingleObject(drive->thread, INFINITE) == WAIT_FAILED))
880 {
881 error = GetLastError();
882 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
883 return error;
884 }
885
886 return drive_free_int(drive);
887}
888
892static void drive_file_objfree(void* obj)
893{
894 drive_file_free((DRIVE_FILE*)obj);
895}
896
897static void drive_message_free(void* obj)
898{
899 wMessage* msg = obj;
900 if (!msg)
901 return;
902 if (msg->id != 0)
903 return;
904
905 IRP* irp = (IRP*)msg->wParam;
906 if (!irp)
907 return;
908 WINPR_ASSERT(irp->Discard);
909 irp->Discard(irp);
910}
911
917static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, const char* name,
918 const char* path, BOOL automount)
919{
920 size_t length = 0;
921 DRIVE_DEVICE* drive = NULL;
922 UINT error = ERROR_INTERNAL_ERROR;
923
924 if (!pEntryPoints || !name || !path)
925 {
926 WLog_ERR(TAG, "[%s] Invalid parameters: pEntryPoints=%p, name=%p, path=%p", pEntryPoints,
927 name, path);
928 return ERROR_INVALID_PARAMETER;
929 }
930
931 if (name[0] && path[0])
932 {
933 size_t pathLength = strnlen(path, MAX_PATH);
934 drive = (DRIVE_DEVICE*)calloc(1, sizeof(DRIVE_DEVICE));
935
936 if (!drive)
937 {
938 WLog_ERR(TAG, "calloc failed!");
939 return CHANNEL_RC_NO_MEMORY;
940 }
941
942 drive->device.type = RDPDR_DTYP_FILESYSTEM;
943 drive->device.IRPRequest = drive_irp_request;
944 drive->device.Free = drive_free;
945 drive->rdpcontext = pEntryPoints->rdpcontext;
946 drive->automount = automount;
947 length = strlen(name);
948 drive->device.data = Stream_New(NULL, length + 1);
949
950 if (!drive->device.data)
951 {
952 WLog_ERR(TAG, "Stream_New failed!");
953 error = CHANNEL_RC_NO_MEMORY;
954 goto out_error;
955 }
956
957 for (size_t i = 0; i < length; i++)
958 {
959 /* Filter 2.2.1.3 Device Announce Header (DEVICE_ANNOUNCE) forbidden symbols */
960 switch (name[i])
961 {
962 case ':':
963 case '<':
964 case '>':
965 case '\"':
966 case '/':
967 case '\\':
968 case '|':
969 case ' ':
970 Stream_Write_UINT8(drive->device.data, '_');
971 break;
972 default:
973 Stream_Write_UINT8(drive->device.data, (BYTE)name[i]);
974 break;
975 }
976 }
977 Stream_Write_UINT8(drive->device.data, '\0');
978
979 drive->device.name = Stream_BufferAs(drive->device.data, char);
980 if (!drive->device.name)
981 goto out_error;
982
983 if ((pathLength > 1) && (path[pathLength - 1] == '/'))
984 pathLength--;
985
986 drive->path = ConvertUtf8NToWCharAlloc(path, pathLength, NULL);
987 if (!drive->path)
988 {
989 error = CHANNEL_RC_NO_MEMORY;
990 goto out_error;
991 }
992
993 drive->files = ListDictionary_New(TRUE);
994
995 if (!drive->files)
996 {
997 WLog_ERR(TAG, "ListDictionary_New failed!");
998 error = CHANNEL_RC_NO_MEMORY;
999 goto out_error;
1000 }
1001
1002 ListDictionary_ValueObject(drive->files)->fnObjectFree = drive_file_objfree;
1003 drive->IrpQueue = MessageQueue_New(NULL);
1004
1005 if (!drive->IrpQueue)
1006 {
1007 WLog_ERR(TAG, "ListDictionary_New failed!");
1008 error = CHANNEL_RC_NO_MEMORY;
1009 goto out_error;
1010 }
1011
1012 wObject* obj = MessageQueue_Object(drive->IrpQueue);
1013 WINPR_ASSERT(obj);
1014 obj->fnObjectFree = drive_message_free;
1015
1016 if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)drive)))
1017 {
1018 WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error);
1019 goto out_error;
1020 }
1021
1022 drive->async = !freerdp_settings_get_bool(drive->rdpcontext->settings,
1023 FreeRDP_SynchronousStaticChannels);
1024 if (drive->async)
1025 {
1026 if (!(drive->thread =
1027 CreateThread(NULL, 0, drive_thread_func, drive, CREATE_SUSPENDED, NULL)))
1028 {
1029 WLog_ERR(TAG, "CreateThread failed!");
1030 goto out_error;
1031 }
1032
1033 ResumeThread(drive->thread);
1034 }
1035 }
1036
1037 return CHANNEL_RC_OK;
1038out_error:
1039 drive_free_int(drive);
1040 return error;
1041}
1042
1048FREERDP_ENTRY_POINT(
1049 UINT VCAPITYPE drive_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
1050{
1051 RDPDR_DRIVE* drive = NULL;
1052 UINT error = 0;
1053#ifdef WIN32
1054 int len;
1055 char devlist[512], buf[512];
1056 char* bufdup;
1057 char* devdup;
1058#endif
1059
1060 WINPR_ASSERT(pEntryPoints);
1061
1062 drive = (RDPDR_DRIVE*)pEntryPoints->device;
1063 WINPR_ASSERT(drive);
1064
1065#ifndef WIN32
1066 if (strcmp(drive->Path, "*") == 0)
1067 {
1068 /* all drives */
1069 free(drive->Path);
1070 drive->Path = _strdup("/");
1071
1072 if (!drive->Path)
1073 {
1074 WLog_ERR(TAG, "_strdup failed!");
1075 return CHANNEL_RC_NO_MEMORY;
1076 }
1077 }
1078 else if (strcmp(drive->Path, "%") == 0)
1079 {
1080 free(drive->Path);
1081 drive->Path = GetKnownPath(KNOWN_PATH_HOME);
1082
1083 if (!drive->Path)
1084 {
1085 WLog_ERR(TAG, "_strdup failed!");
1086 return CHANNEL_RC_NO_MEMORY;
1087 }
1088 }
1089
1090 error =
1091 drive_register_drive_path(pEntryPoints, drive->device.Name, drive->Path, drive->automount);
1092#else
1093 /* Special case: path[0] == '*' -> export all drives */
1094 /* Special case: path[0] == '%' -> user home dir */
1095 if (strcmp(drive->Path, "%") == 0)
1096 {
1097 GetEnvironmentVariableA("USERPROFILE", buf, sizeof(buf));
1098 PathCchAddBackslashA(buf, sizeof(buf));
1099 free(drive->Path);
1100 drive->Path = _strdup(buf);
1101
1102 if (!drive->Path)
1103 {
1104 WLog_ERR(TAG, "_strdup failed!");
1105 return CHANNEL_RC_NO_MEMORY;
1106 }
1107
1108 error = drive_register_drive_path(pEntryPoints, drive->device.Name, drive->Path,
1109 drive->automount);
1110 }
1111 else if (strcmp(drive->Path, "*") == 0)
1112 {
1113 /* Enumerate all devices: */
1114 GetLogicalDriveStringsA(sizeof(devlist) - 1, devlist);
1115
1116 for (size_t i = 0;; i++)
1117 {
1118 char* dev = &devlist[i * 4];
1119 if (!*dev)
1120 break;
1121 if (*dev > 'B')
1122 {
1123 /* Suppress disk drives A and B to avoid pesty messages */
1124 len = sprintf_s(buf, sizeof(buf) - 4, "%s", drive->device.Name);
1125 buf[len] = '_';
1126 buf[len + 1] = dev[0];
1127 buf[len + 2] = 0;
1128 buf[len + 3] = 0;
1129
1130 if (!(bufdup = _strdup(buf)))
1131 {
1132 WLog_ERR(TAG, "_strdup failed!");
1133 return CHANNEL_RC_NO_MEMORY;
1134 }
1135
1136 if (!(devdup = _strdup(dev)))
1137 {
1138 WLog_ERR(TAG, "_strdup failed!");
1139 return CHANNEL_RC_NO_MEMORY;
1140 }
1141
1142 if ((error = drive_register_drive_path(pEntryPoints, bufdup, devdup, TRUE)))
1143 {
1144 break;
1145 }
1146 }
1147 }
1148 }
1149 else
1150 {
1151 error = drive_register_drive_path(pEntryPoints, drive->device.Name, drive->Path,
1152 drive->automount);
1153 }
1154
1155#endif
1156 return error;
1157}
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
This struct contains function pointer to initialize/free objects.
Definition collections.h:57