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 case ERROR_DIR_NOT_EMPTY:
126 rc = STATUS_DIRECTORY_NOT_EMPTY;
127 break;
128
129 default:
130 rc = STATUS_UNSUCCESSFUL;
131 WLog_ERR(TAG, "Error code not found: %" PRIu32 "", fs_errno);
132 break;
133 }
134
135 return rc;
136}
137
138static DRIVE_FILE* drive_get_file_by_id(DRIVE_DEVICE* drive, UINT32 id)
139{
140 DRIVE_FILE* file = NULL;
141 void* key = (void*)(size_t)id;
142
143 if (!drive)
144 return NULL;
145
146 file = (DRIVE_FILE*)ListDictionary_GetItemValue(drive->files, key);
147 return file;
148}
149
155static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp)
156{
157 UINT32 FileId = 0;
158 DRIVE_FILE* file = NULL;
159 BYTE Information = 0;
160 const WCHAR* path = NULL;
161
162 if (!drive || !irp || !irp->devman)
163 return ERROR_INVALID_PARAMETER;
164
165 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 6 * 4 + 8))
166 return ERROR_INVALID_DATA;
167
168 const uint32_t DesiredAccess = Stream_Get_UINT32(irp->input);
169 const uint64_t allocationSize = Stream_Get_UINT64(irp->input);
170 const uint32_t FileAttributes = Stream_Get_UINT32(irp->input);
171 const uint32_t SharedAccess = Stream_Get_UINT32(irp->input);
172 const uint32_t CreateDisposition = Stream_Get_UINT32(irp->input);
173 const uint32_t CreateOptions = Stream_Get_UINT32(irp->input);
174 const uint32_t PathLength = Stream_Get_UINT32(irp->input);
175
176 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, PathLength))
177 return ERROR_INVALID_DATA;
178
179 path = Stream_ConstPointer(irp->input);
180 FileId = irp->devman->id_sequence++;
181 file = drive_file_new(drive->path, path, PathLength / sizeof(WCHAR), FileId, DesiredAccess,
182 CreateDisposition, CreateOptions, FileAttributes, SharedAccess);
183
184 if (!file)
185 {
186 irp->IoStatus = drive_map_windows_err(GetLastError());
187 FileId = 0;
188 Information = 0;
189 }
190 else
191 {
192 void* key = (void*)(size_t)file->id;
193
194 if (!ListDictionary_Add(drive->files, key, file))
195 {
196 WLog_ERR(TAG, "ListDictionary_Add failed!");
197 return ERROR_INTERNAL_ERROR;
198 }
199
200 switch (CreateDisposition)
201 {
202 case FILE_SUPERSEDE:
203 case FILE_OPEN:
204 case FILE_CREATE:
205 case FILE_OVERWRITE:
206 Information = FILE_SUPERSEDED;
207 break;
208
209 case FILE_OPEN_IF:
210 Information = FILE_OPENED;
211 break;
212
213 case FILE_OVERWRITE_IF:
214 Information = FILE_OVERWRITTEN;
215 break;
216
217 default:
218 Information = 0;
219 break;
220 }
221
222 if (allocationSize > 0)
223 {
224 const BYTE buffer[] = { '\0' };
225 if (!drive_file_seek(file, allocationSize - sizeof(buffer)))
226 return ERROR_INTERNAL_ERROR;
227 if (!drive_file_write(file, buffer, sizeof(buffer)))
228 return ERROR_INTERNAL_ERROR;
229 }
230 }
231
232 Stream_Write_UINT32(irp->output, FileId);
233 Stream_Write_UINT8(irp->output, Information);
234
235 WINPR_ASSERT(irp->Complete);
236 return irp->Complete(irp);
237}
238
244static UINT drive_process_irp_close(DRIVE_DEVICE* drive, IRP* irp)
245{
246 void* key = NULL;
247 DRIVE_FILE* file = NULL;
248
249 if (!drive || !irp || !irp->output)
250 return ERROR_INVALID_PARAMETER;
251
252 file = drive_get_file_by_id(drive, irp->FileId);
253 key = (void*)(size_t)irp->FileId;
254
255 if (!file)
256 irp->IoStatus = STATUS_UNSUCCESSFUL;
257 else
258 {
259 ListDictionary_Take(drive->files, key);
260
261 if (drive_file_free(file))
262 irp->IoStatus = STATUS_SUCCESS;
263 else
264 irp->IoStatus = drive_map_windows_err(GetLastError());
265 }
266
267 Stream_Zero(irp->output, 5); /* Padding(5) */
268
269 WINPR_ASSERT(irp->Complete);
270 return irp->Complete(irp);
271}
272
278static UINT drive_process_irp_read(DRIVE_DEVICE* drive, IRP* irp)
279{
280 DRIVE_FILE* file = NULL;
281 UINT32 Length = 0;
282 UINT64 Offset = 0;
283
284 if (!drive || !irp || !irp->output)
285 return ERROR_INVALID_PARAMETER;
286
287 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12))
288 return ERROR_INVALID_DATA;
289
290 Stream_Read_UINT32(irp->input, Length);
291 Stream_Read_UINT64(irp->input, Offset);
292 file = drive_get_file_by_id(drive, irp->FileId);
293
294 if (!file)
295 {
296 irp->IoStatus = STATUS_UNSUCCESSFUL;
297 Length = 0;
298 }
299 else if (!drive_file_seek(file, Offset))
300 {
301 irp->IoStatus = drive_map_windows_err(GetLastError());
302 Length = 0;
303 }
304
305 if (!Stream_EnsureRemainingCapacity(irp->output, Length + 4))
306 {
307 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
308 return ERROR_INTERNAL_ERROR;
309 }
310 else if (Length == 0)
311 Stream_Write_UINT32(irp->output, 0);
312 else
313 {
314 BYTE* buffer = Stream_PointerAs(irp->output, BYTE) + sizeof(UINT32);
315
316 if (!drive_file_read(file, buffer, &Length))
317 {
318 irp->IoStatus = drive_map_windows_err(GetLastError());
319 Stream_Write_UINT32(irp->output, 0);
320 }
321 else
322 {
323 Stream_Write_UINT32(irp->output, Length);
324 Stream_Seek(irp->output, Length);
325 }
326 }
327
328 WINPR_ASSERT(irp->Complete);
329 return irp->Complete(irp);
330}
331
337static UINT drive_process_irp_write(DRIVE_DEVICE* drive, IRP* irp)
338{
339 DRIVE_FILE* file = NULL;
340 UINT32 Length = 0;
341 UINT64 Offset = 0;
342
343 if (!drive || !irp || !irp->input || !irp->output)
344 return ERROR_INVALID_PARAMETER;
345
346 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
347 return ERROR_INVALID_DATA;
348
349 Stream_Read_UINT32(irp->input, Length);
350 Stream_Read_UINT64(irp->input, Offset);
351 Stream_Seek(irp->input, 20); /* Padding */
352 const void* ptr = Stream_ConstPointer(irp->input);
353 if (!Stream_SafeSeek(irp->input, Length))
354 return ERROR_INVALID_DATA;
355 file = drive_get_file_by_id(drive, irp->FileId);
356
357 if (!file)
358 {
359 irp->IoStatus = STATUS_UNSUCCESSFUL;
360 Length = 0;
361 }
362 else if (!drive_file_seek(file, Offset))
363 {
364 irp->IoStatus = drive_map_windows_err(GetLastError());
365 Length = 0;
366 }
367 else if (!drive_file_write(file, ptr, Length))
368 {
369 irp->IoStatus = drive_map_windows_err(GetLastError());
370 Length = 0;
371 }
372
373 Stream_Write_UINT32(irp->output, Length);
374 Stream_Write_UINT8(irp->output, 0); /* Padding */
375
376 WINPR_ASSERT(irp->Complete);
377 return irp->Complete(irp);
378}
379
385static UINT drive_process_irp_query_information(DRIVE_DEVICE* drive, IRP* irp)
386{
387 DRIVE_FILE* file = NULL;
388 UINT32 FsInformationClass = 0;
389
390 if (!drive || !irp)
391 return ERROR_INVALID_PARAMETER;
392
393 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
394 return ERROR_INVALID_DATA;
395
396 Stream_Read_UINT32(irp->input, FsInformationClass);
397 file = drive_get_file_by_id(drive, irp->FileId);
398
399 if (!file)
400 {
401 irp->IoStatus = STATUS_UNSUCCESSFUL;
402 }
403 else if (!drive_file_query_information(file, FsInformationClass, irp->output))
404 {
405 irp->IoStatus = drive_map_windows_err(GetLastError());
406 }
407
408 WINPR_ASSERT(irp->Complete);
409 return irp->Complete(irp);
410}
411
417static UINT drive_process_irp_set_information(DRIVE_DEVICE* drive, IRP* irp)
418{
419 DRIVE_FILE* file = NULL;
420 UINT32 FsInformationClass = 0;
421 UINT32 Length = 0;
422
423 if (!drive || !irp || !irp->input || !irp->output)
424 return ERROR_INVALID_PARAMETER;
425
426 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
427 return ERROR_INVALID_DATA;
428
429 Stream_Read_UINT32(irp->input, FsInformationClass);
430 Stream_Read_UINT32(irp->input, Length);
431 Stream_Seek(irp->input, 24); /* Padding */
432 file = drive_get_file_by_id(drive, irp->FileId);
433
434 if (!file)
435 {
436 irp->IoStatus = STATUS_UNSUCCESSFUL;
437 }
438 else if (!drive_file_set_information(file, FsInformationClass, Length, irp->input))
439 {
440 irp->IoStatus = drive_map_windows_err(GetLastError());
441 }
442
443 Stream_Write_UINT32(irp->output, Length);
444
445 WINPR_ASSERT(irp->Complete);
446 return irp->Complete(irp);
447}
448
454static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive, IRP* irp)
455{
456 UINT32 FsInformationClass = 0;
457 wStream* output = NULL;
458 DWORD lpSectorsPerCluster = 0;
459 DWORD lpBytesPerSector = 0;
460 DWORD lpNumberOfFreeClusters = 0;
461 DWORD lpTotalNumberOfClusters = 0;
462 WIN32_FILE_ATTRIBUTE_DATA wfad = { 0 };
463 WCHAR LabelBuffer[32] = { 0 };
464
465 if (!drive || !irp)
466 return ERROR_INVALID_PARAMETER;
467
468 output = irp->output;
469
470 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
471 return ERROR_INVALID_DATA;
472
473 Stream_Read_UINT32(irp->input, FsInformationClass);
474 GetDiskFreeSpaceW(drive->path, &lpSectorsPerCluster, &lpBytesPerSector, &lpNumberOfFreeClusters,
475 &lpTotalNumberOfClusters);
476
477 switch (FsInformationClass)
478 {
479 case FileFsVolumeInformation:
480 {
481 /* http://msdn.microsoft.com/en-us/library/cc232108.aspx */
482 const WCHAR* volumeLabel =
483 InitializeConstWCharFromUtf8("FREERDP", LabelBuffer, ARRAYSIZE(LabelBuffer));
484 const size_t volumeLabelLen = (_wcslen(volumeLabel) + 1) * sizeof(WCHAR);
485 const size_t length = 17ul + volumeLabelLen;
486
487 if ((length > UINT32_MAX) || (volumeLabelLen > UINT32_MAX))
488 return CHANNEL_RC_NO_BUFFER;
489
490 Stream_Write_UINT32(output, (UINT32)length); /* Length */
491
492 if (!Stream_EnsureRemainingCapacity(output, length))
493 {
494 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
495 return CHANNEL_RC_NO_MEMORY;
496 }
497
498 GetFileAttributesExW(drive->path, GetFileExInfoStandard, &wfad);
499 Stream_Write_UINT32(output, wfad.ftCreationTime.dwLowDateTime); /* VolumeCreationTime */
500 Stream_Write_UINT32(output,
501 wfad.ftCreationTime.dwHighDateTime); /* VolumeCreationTime */
502 Stream_Write_UINT32(output, lpNumberOfFreeClusters & 0xffff); /* VolumeSerialNumber */
503 Stream_Write_UINT32(output, (UINT32)volumeLabelLen); /* VolumeLabelLength */
504 Stream_Write_UINT8(output, 0); /* SupportsObjects */
505 /* Reserved(1), MUST NOT be added! */
506 Stream_Write(output, volumeLabel, volumeLabelLen); /* VolumeLabel (Unicode) */
507 }
508 break;
509
510 case FileFsSizeInformation:
511 /* http://msdn.microsoft.com/en-us/library/cc232107.aspx */
512 Stream_Write_UINT32(output, 24); /* Length */
513
514 if (!Stream_EnsureRemainingCapacity(output, 24))
515 {
516 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
517 return CHANNEL_RC_NO_MEMORY;
518 }
519
520 Stream_Write_UINT64(output, lpTotalNumberOfClusters); /* TotalAllocationUnits */
521 Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */
522 Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */
523 Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */
524 break;
525
526 case FileFsAttributeInformation:
527 {
528 /* http://msdn.microsoft.com/en-us/library/cc232101.aspx */
529 const WCHAR* diskType =
530 InitializeConstWCharFromUtf8("FAT32", LabelBuffer, ARRAYSIZE(LabelBuffer));
531 const size_t diskTypeLen = (_wcslen(diskType) + 1) * sizeof(WCHAR);
532 const size_t length = 12ul + diskTypeLen;
533
534 if ((length > UINT32_MAX) || (diskTypeLen > UINT32_MAX))
535 return CHANNEL_RC_NO_BUFFER;
536
537 Stream_Write_UINT32(output, (UINT32)length); /* Length */
538
539 if (!Stream_EnsureRemainingCapacity(output, length))
540 {
541 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
542 return CHANNEL_RC_NO_MEMORY;
543 }
544
545 Stream_Write_UINT32(output, FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES |
546 FILE_UNICODE_ON_DISK); /* FileSystemAttributes */
547 Stream_Write_UINT32(output, MAX_PATH); /* MaximumComponentNameLength */
548 Stream_Write_UINT32(output, (UINT32)diskTypeLen); /* FileSystemNameLength */
549 Stream_Write(output, diskType, diskTypeLen); /* FileSystemName (Unicode) */
550 }
551 break;
552
553 case FileFsFullSizeInformation:
554 /* http://msdn.microsoft.com/en-us/library/cc232104.aspx */
555 Stream_Write_UINT32(output, 32); /* Length */
556
557 if (!Stream_EnsureRemainingCapacity(output, 32))
558 {
559 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
560 return CHANNEL_RC_NO_MEMORY;
561 }
562
563 Stream_Write_UINT64(output, lpTotalNumberOfClusters); /* TotalAllocationUnits */
564 Stream_Write_UINT64(output,
565 lpNumberOfFreeClusters); /* CallerAvailableAllocationUnits */
566 Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */
567 Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */
568 Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */
569 break;
570
571 case FileFsDeviceInformation:
572 /* http://msdn.microsoft.com/en-us/library/cc232109.aspx */
573 Stream_Write_UINT32(output, 8); /* Length */
574
575 if (!Stream_EnsureRemainingCapacity(output, 8))
576 {
577 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
578 return CHANNEL_RC_NO_MEMORY;
579 }
580
581 Stream_Write_UINT32(output, FILE_DEVICE_DISK); /* DeviceType */
582 Stream_Write_UINT32(output, 0); /* Characteristics */
583 break;
584
585 default:
586 WLog_WARN(TAG, "Unhandled FSInformationClass %s [0x%08" PRIx32 "]",
587 FSInformationClass2Tag(FsInformationClass), FsInformationClass);
588 irp->IoStatus = STATUS_UNSUCCESSFUL;
589 Stream_Write_UINT32(output, 0); /* Length */
590 break;
591 }
592
593 WINPR_ASSERT(irp->Complete);
594 return irp->Complete(irp);
595}
596
597/* http://msdn.microsoft.com/en-us/library/cc241518.aspx */
598
604static UINT drive_process_irp_silent_ignore(DRIVE_DEVICE* drive, IRP* irp)
605{
606 if (!drive || !irp || !irp->output)
607 return ERROR_INVALID_PARAMETER;
608
609 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
610 return ERROR_INVALID_DATA;
611
612 const uint32_t FsInformationClass = Stream_Get_UINT32(irp->input);
613 WLog_VRB(TAG, "Silently ignore FSInformationClass %s [0x%08" PRIx32 "]",
614 FSInformationClass2Tag(FsInformationClass), FsInformationClass);
615 Stream_Write_UINT32(irp->output, 0); /* Length */
616 WINPR_ASSERT(irp->Complete);
617 return irp->Complete(irp);
618}
619
625static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp)
626{
627 const WCHAR* path = NULL;
628 DRIVE_FILE* file = NULL;
629 BYTE InitialQuery = 0;
630 UINT32 PathLength = 0;
631 UINT32 FsInformationClass = 0;
632
633 if (!drive || !irp)
634 return ERROR_INVALID_PARAMETER;
635
636 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
637 return ERROR_INVALID_DATA;
638
639 Stream_Read_UINT32(irp->input, FsInformationClass);
640 Stream_Read_UINT8(irp->input, InitialQuery);
641 Stream_Read_UINT32(irp->input, PathLength);
642 Stream_Seek(irp->input, 23); /* Padding */
643 path = Stream_ConstPointer(irp->input);
644 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, PathLength))
645 return ERROR_INVALID_DATA;
646
647 file = drive_get_file_by_id(drive, irp->FileId);
648
649 if (file == NULL)
650 {
651 irp->IoStatus = STATUS_UNSUCCESSFUL;
652 Stream_Write_UINT32(irp->output, 0); /* Length */
653 }
654 else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path,
655 PathLength / sizeof(WCHAR), irp->output))
656 {
657 irp->IoStatus = drive_map_windows_err(GetLastError());
658 }
659
660 WINPR_ASSERT(irp->Complete);
661 return irp->Complete(irp);
662}
663
669static UINT drive_process_irp_directory_control(DRIVE_DEVICE* drive, IRP* irp)
670{
671 if (!drive || !irp)
672 return ERROR_INVALID_PARAMETER;
673
674 switch (irp->MinorFunction)
675 {
676 case IRP_MN_QUERY_DIRECTORY:
677 return drive_process_irp_query_directory(drive, irp);
678
679 case IRP_MN_NOTIFY_CHANGE_DIRECTORY: /* TODO */
680 return irp->Discard(irp);
681
682 default:
683 irp->IoStatus = STATUS_NOT_SUPPORTED;
684 Stream_Write_UINT32(irp->output, 0); /* Length */
685 WINPR_ASSERT(irp->Complete);
686 return irp->Complete(irp);
687 }
688
689 return CHANNEL_RC_OK;
690}
691
697static UINT drive_process_irp_device_control(DRIVE_DEVICE* drive, IRP* irp)
698{
699 if (!drive || !irp)
700 return ERROR_INVALID_PARAMETER;
701
702 Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
703 WINPR_ASSERT(irp->Complete);
704 return irp->Complete(irp);
705}
706
712static UINT drive_process_irp(DRIVE_DEVICE* drive, IRP* irp)
713{
714 UINT error = 0;
715
716 if (!drive || !irp)
717 return ERROR_INVALID_PARAMETER;
718
719 irp->IoStatus = STATUS_SUCCESS;
720
721 switch (irp->MajorFunction)
722 {
723 case IRP_MJ_CREATE:
724 error = drive_process_irp_create(drive, irp);
725 break;
726
727 case IRP_MJ_CLOSE:
728 error = drive_process_irp_close(drive, irp);
729 break;
730
731 case IRP_MJ_READ:
732 error = drive_process_irp_read(drive, irp);
733 break;
734
735 case IRP_MJ_WRITE:
736 error = drive_process_irp_write(drive, irp);
737 break;
738
739 case IRP_MJ_QUERY_INFORMATION:
740 error = drive_process_irp_query_information(drive, irp);
741 break;
742
743 case IRP_MJ_SET_INFORMATION:
744 error = drive_process_irp_set_information(drive, irp);
745 break;
746
747 case IRP_MJ_QUERY_VOLUME_INFORMATION:
748 error = drive_process_irp_query_volume_information(drive, irp);
749 break;
750
751 case IRP_MJ_LOCK_CONTROL:
752 error = drive_process_irp_silent_ignore(drive, irp);
753 break;
754
755 case IRP_MJ_DIRECTORY_CONTROL:
756 error = drive_process_irp_directory_control(drive, irp);
757 break;
758
759 case IRP_MJ_DEVICE_CONTROL:
760 error = drive_process_irp_device_control(drive, irp);
761 break;
762
763 default:
764 irp->IoStatus = STATUS_NOT_SUPPORTED;
765 WINPR_ASSERT(irp->Complete);
766 error = irp->Complete(irp);
767 break;
768 }
769
770 return error;
771}
772
773static BOOL drive_poll_run(DRIVE_DEVICE* drive, IRP* irp)
774{
775 WINPR_ASSERT(drive);
776
777 if (irp)
778 {
779 const UINT error = drive_process_irp(drive, irp);
780 if (error)
781 {
782 WLog_ERR(TAG, "drive_process_irp failed with error %" PRIu32 "!", error);
783 return FALSE;
784 }
785 }
786
787 return TRUE;
788}
789
790static DWORD WINAPI drive_thread_func(LPVOID arg)
791{
792 DRIVE_DEVICE* drive = (DRIVE_DEVICE*)arg;
793 UINT error = CHANNEL_RC_OK;
794
795 if (!drive)
796 {
797 error = ERROR_INVALID_PARAMETER;
798 goto fail;
799 }
800
801 while (1)
802 {
803 if (!MessageQueue_Wait(drive->IrpQueue))
804 {
805 WLog_ERR(TAG, "MessageQueue_Wait failed!");
806 error = ERROR_INTERNAL_ERROR;
807 break;
808 }
809
810 if (MessageQueue_Size(drive->IrpQueue) < 1)
811 continue;
812
813 wMessage message = { 0 };
814 if (!MessageQueue_Peek(drive->IrpQueue, &message, TRUE))
815 {
816 WLog_ERR(TAG, "MessageQueue_Peek failed!");
817 continue;
818 }
819
820 if (message.id == WMQ_QUIT)
821 break;
822
823 IRP* irp = (IRP*)message.wParam;
824 if (!drive_poll_run(drive, irp))
825 break;
826 }
827
828fail:
829
830 if (error && drive && drive->rdpcontext)
831 setChannelError(drive->rdpcontext, error, "drive_thread_func reported an error");
832
833 ExitThread(error);
834 return error;
835}
836
842static UINT drive_irp_request(DEVICE* device, IRP* irp)
843{
844 DRIVE_DEVICE* drive = (DRIVE_DEVICE*)device;
845
846 if (!drive)
847 return ERROR_INVALID_PARAMETER;
848
849 if (drive->async)
850 {
851 if (!MessageQueue_Post(drive->IrpQueue, NULL, 0, (void*)irp, NULL))
852 {
853 WLog_ERR(TAG, "MessageQueue_Post failed!");
854 return ERROR_INTERNAL_ERROR;
855 }
856 }
857 else
858 {
859 if (!drive_poll_run(drive, irp))
860 return ERROR_INTERNAL_ERROR;
861 }
862
863 return CHANNEL_RC_OK;
864}
865
866static UINT drive_free_int(DRIVE_DEVICE* drive)
867{
868 UINT error = CHANNEL_RC_OK;
869
870 if (!drive)
871 return ERROR_INVALID_PARAMETER;
872
873 (void)CloseHandle(drive->thread);
874 ListDictionary_Free(drive->files);
875 MessageQueue_Free(drive->IrpQueue);
876 Stream_Free(drive->device.data, TRUE);
877 free(drive->path);
878 free(drive);
879 return error;
880}
881
887static UINT drive_free(DEVICE* device)
888{
889 DRIVE_DEVICE* drive = (DRIVE_DEVICE*)device;
890 UINT error = CHANNEL_RC_OK;
891
892 if (!drive)
893 return ERROR_INVALID_PARAMETER;
894
895 if (MessageQueue_PostQuit(drive->IrpQueue, 0) &&
896 (WaitForSingleObject(drive->thread, INFINITE) == WAIT_FAILED))
897 {
898 error = GetLastError();
899 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
900 return error;
901 }
902
903 return drive_free_int(drive);
904}
905
909static void drive_file_objfree(void* obj)
910{
911 drive_file_free((DRIVE_FILE*)obj);
912}
913
914static void drive_message_free(void* obj)
915{
916 wMessage* msg = obj;
917 if (!msg)
918 return;
919 if (msg->id != 0)
920 return;
921
922 IRP* irp = (IRP*)msg->wParam;
923 if (!irp)
924 return;
925 WINPR_ASSERT(irp->Discard);
926 irp->Discard(irp);
927}
928
934static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, const char* name,
935 const char* path, BOOL automount, uint32_t* pid)
936{
937 WINPR_ASSERT(pid);
938
939 size_t length = 0;
940 DRIVE_DEVICE* drive = NULL;
941 UINT error = ERROR_INTERNAL_ERROR;
942
943 if (!pEntryPoints || !name || !path)
944 {
945 WLog_ERR(TAG, "[%s] Invalid parameters: pEntryPoints=%p, name=%p, path=%p", pEntryPoints,
946 name, path);
947 return ERROR_INVALID_PARAMETER;
948 }
949
950 if (name[0] && path[0])
951 {
952 size_t pathLength = strnlen(path, MAX_PATH);
953 drive = (DRIVE_DEVICE*)calloc(1, sizeof(DRIVE_DEVICE));
954
955 if (!drive)
956 {
957 WLog_ERR(TAG, "calloc failed!");
958 return CHANNEL_RC_NO_MEMORY;
959 }
960
961 drive->device.type = RDPDR_DTYP_FILESYSTEM;
962 drive->device.IRPRequest = drive_irp_request;
963 drive->device.Free = drive_free;
964 drive->rdpcontext = pEntryPoints->rdpcontext;
965 drive->automount = automount;
966 length = strlen(name);
967 drive->device.data = Stream_New(NULL, length + 1);
968
969 if (!drive->device.data)
970 {
971 WLog_ERR(TAG, "Stream_New failed!");
972 error = CHANNEL_RC_NO_MEMORY;
973 goto out_error;
974 }
975
976 for (size_t i = 0; i < length; i++)
977 {
978 /* Filter 2.2.1.3 Device Announce Header (DEVICE_ANNOUNCE) forbidden symbols */
979 switch (name[i])
980 {
981 case ':':
982 case '<':
983 case '>':
984 case '\"':
985 case '/':
986 case '\\':
987 case '|':
988 case ' ':
989 Stream_Write_UINT8(drive->device.data, '_');
990 break;
991 default:
992 Stream_Write_UINT8(drive->device.data, (BYTE)name[i]);
993 break;
994 }
995 }
996 Stream_Write_UINT8(drive->device.data, '\0');
997
998 drive->device.name = Stream_BufferAs(drive->device.data, char);
999 if (!drive->device.name)
1000 goto out_error;
1001
1002 if ((pathLength > 1) && (path[pathLength - 1] == '/'))
1003 pathLength--;
1004
1005 drive->path = ConvertUtf8NToWCharAlloc(path, pathLength, NULL);
1006 if (!drive->path)
1007 {
1008 error = CHANNEL_RC_NO_MEMORY;
1009 goto out_error;
1010 }
1011
1012 drive->files = ListDictionary_New(TRUE);
1013
1014 if (!drive->files)
1015 {
1016 WLog_ERR(TAG, "ListDictionary_New failed!");
1017 error = CHANNEL_RC_NO_MEMORY;
1018 goto out_error;
1019 }
1020
1021 ListDictionary_ValueObject(drive->files)->fnObjectFree = drive_file_objfree;
1022 drive->IrpQueue = MessageQueue_New(NULL);
1023
1024 if (!drive->IrpQueue)
1025 {
1026 WLog_ERR(TAG, "ListDictionary_New failed!");
1027 error = CHANNEL_RC_NO_MEMORY;
1028 goto out_error;
1029 }
1030
1031 wObject* obj = MessageQueue_Object(drive->IrpQueue);
1032 WINPR_ASSERT(obj);
1033 obj->fnObjectFree = drive_message_free;
1034
1035 if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &drive->device)))
1036 {
1037 WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error);
1038 goto out_error;
1039 }
1040 *pid = drive->device.id;
1041
1042 drive->async = !freerdp_settings_get_bool(drive->rdpcontext->settings,
1043 FreeRDP_SynchronousStaticChannels);
1044 if (drive->async)
1045 {
1046 if (!(drive->thread =
1047 CreateThread(NULL, 0, drive_thread_func, drive, CREATE_SUSPENDED, NULL)))
1048 {
1049 WLog_ERR(TAG, "CreateThread failed!");
1050 goto out_error;
1051 }
1052
1053 ResumeThread(drive->thread);
1054 }
1055 }
1056
1057 return CHANNEL_RC_OK;
1058out_error:
1059 drive_free_int(drive);
1060 return error;
1061}
1062
1063static BOOL drive_filtered(const WCHAR* drive)
1064{
1065 const WCHAR a[] = { 'A', '\0' };
1066 const WCHAR b[] = { 'B', '\0' };
1067 const WCHAR la[] = { 'a', '\0' };
1068 const WCHAR lb[] = { 'b', '\0' };
1069 const WCHAR* list[] = { a, b, la, lb };
1070
1071 for (size_t x = 0; x < ARRAYSIZE(list); x++)
1072 {
1073 const WCHAR* cur = list[x];
1074 if (_wcsncmp(drive, cur, 2) == 0)
1075 return TRUE;
1076 }
1077 return FALSE;
1078}
1079
1080static UINT handle_all_drives(RDPDR_DRIVE* drive, PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
1081{
1082 UINT error = ERROR_INTERNAL_ERROR;
1083
1084 WINPR_ASSERT(drive);
1085 WINPR_ASSERT(pEntryPoints);
1086
1087 /* Enumerate all devices: */
1088 const DWORD dlen = GetLogicalDriveStringsW(0, NULL);
1089
1090 WCHAR* devlist = calloc(dlen, sizeof(WCHAR));
1091 if (!devlist)
1092 return ERROR_OUTOFMEMORY;
1093
1094 const DWORD rc = GetLogicalDriveStringsW(dlen, devlist);
1095 if (rc >= dlen)
1096 goto fail;
1097
1098 size_t len = 0;
1099 for (size_t offset = 0; offset < rc; offset += len + 1)
1100 {
1101 len = _wcsnlen(&devlist[offset], rc - offset);
1102
1103 const WCHAR* dev = &devlist[offset];
1104 if (!drive_filtered(dev))
1105 {
1106 char* bufdup = NULL;
1107 char* devdup = ConvertWCharNToUtf8Alloc(dev, len, NULL);
1108 if (!devdup)
1109 {
1110 error = ERROR_OUTOFMEMORY;
1111 goto fail;
1112 }
1113 size_t size = 0;
1114 winpr_asprintf(&bufdup, &size, "%s_%s", drive->device.Name, devdup);
1115
1116 error =
1117 drive_register_drive_path(pEntryPoints, bufdup, devdup, TRUE, &drive->device.Id);
1118 free(devdup);
1119 free(bufdup);
1120 if (error != CHANNEL_RC_OK)
1121 goto fail;
1122 }
1123 }
1124
1125 error = CHANNEL_RC_OK;
1126
1127fail:
1128 free(devlist);
1129
1130 return error;
1131}
1132
1138FREERDP_ENTRY_POINT(
1139 UINT VCAPITYPE drive_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
1140{
1141 UINT error = 0;
1142
1143 WINPR_ASSERT(pEntryPoints);
1144
1145 RDPDR_DRIVE* drive = (RDPDR_DRIVE*)pEntryPoints->device;
1146 WINPR_ASSERT(drive);
1147
1148 const char all[] = "*";
1149 const char home[] = "%";
1150 if (strncmp(drive->Path, all, sizeof(all)) == 0)
1151 {
1152 error = handle_all_drives(drive, pEntryPoints);
1153 }
1154 else if (strncmp(drive->Path, home, sizeof(home)) == 0)
1155 {
1156 free(drive->Path);
1157 drive->Path = GetKnownPath(KNOWN_PATH_HOME);
1158
1159 if (!drive->Path)
1160 {
1161 WLog_ERR(TAG, "_strdup failed!");
1162 return CHANNEL_RC_NO_MEMORY;
1163 }
1164 error = drive_register_drive_path(pEntryPoints, drive->device.Name, drive->Path,
1165 drive->automount, &drive->device.Id);
1166 }
1167 else
1168 {
1169 error = drive_register_drive_path(pEntryPoints, drive->device.Name, drive->Path,
1170 drive->automount, &drive->device.Id);
1171 }
1172 return error;
1173}
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