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