FreeRDP
Loading...
Searching...
No Matches
drive_main.c
1
24#include <freerdp/config.h>
25#include <freerdp/utils/helpers.h>
26#include <freerdp/utils/rdpdr_utils.h>
27
28#include <errno.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32
33#include <winpr/crt.h>
34#include <winpr/assert.h>
35#include <winpr/path.h>
36#include <winpr/file.h>
37#include <winpr/string.h>
38#include <winpr/synch.h>
39#include <winpr/thread.h>
40#include <winpr/stream.h>
41#include <winpr/environment.h>
42#include <winpr/interlocked.h>
43#include <winpr/collections.h>
44#include <winpr/shell.h>
45
46#include <freerdp/freerdp.h>
47#include <freerdp/channels/rdpdr.h>
48
49#include "drive_file.h"
50
51typedef struct
52{
53 DEVICE device;
54
55 WCHAR* path;
56 BOOL automount;
57 UINT32 PathLength;
58 wListDictionary* files;
59
60 HANDLE thread;
61 BOOL async;
62 wMessageQueue* IrpQueue;
63
64 DEVMAN* devman;
65
66 rdpContext* rdpcontext;
67} DRIVE_DEVICE;
68
69static NTSTATUS drive_map_windows_err(DWORD fs_errno)
70{
71 NTSTATUS rc = 0;
72
73 /* try to return NTSTATUS version of error code */
74
75 switch (fs_errno)
76 {
77 case STATUS_SUCCESS:
78 rc = STATUS_SUCCESS;
79 break;
80
81 case ERROR_ACCESS_DENIED:
82 case ERROR_SHARING_VIOLATION:
83 rc = STATUS_ACCESS_DENIED;
84 break;
85
86 case ERROR_FILE_NOT_FOUND:
87 rc = STATUS_NO_SUCH_FILE;
88 break;
89
90 case ERROR_BUSY_DRIVE:
91 rc = STATUS_DEVICE_BUSY;
92 break;
93
94 case ERROR_INVALID_DRIVE:
95 rc = STATUS_NO_SUCH_DEVICE;
96 break;
97
98 case ERROR_NOT_READY:
99 rc = STATUS_NO_SUCH_DEVICE;
100 break;
101
102 case ERROR_FILE_EXISTS:
103 case ERROR_ALREADY_EXISTS:
104 rc = STATUS_OBJECT_NAME_COLLISION;
105 break;
106
107 case ERROR_INVALID_NAME:
108 rc = STATUS_NO_SUCH_FILE;
109 break;
110
111 case ERROR_INVALID_HANDLE:
112 rc = STATUS_INVALID_HANDLE;
113 break;
114
115 case ERROR_NO_MORE_FILES:
116 rc = STATUS_NO_MORE_FILES;
117 break;
118
119 case ERROR_DIRECTORY:
120 rc = STATUS_NOT_A_DIRECTORY;
121 break;
122
123 case ERROR_PATH_NOT_FOUND:
124 rc = STATUS_OBJECT_PATH_NOT_FOUND;
125 break;
126
127 case ERROR_DIR_NOT_EMPTY:
128 rc = STATUS_DIRECTORY_NOT_EMPTY;
129 break;
130
131 default:
132 rc = STATUS_UNSUCCESSFUL;
133 WLog_ERR(TAG, "Error code not found: %" PRIu32 "", fs_errno);
134 break;
135 }
136
137 return rc;
138}
139
140static DRIVE_FILE* drive_get_file_by_id(DRIVE_DEVICE* drive, UINT32 id)
141{
142 DRIVE_FILE* file = NULL;
143 void* key = (void*)(size_t)id;
144
145 if (!drive)
146 return NULL;
147
148 file = (DRIVE_FILE*)ListDictionary_GetItemValue(drive->files, key);
149 return file;
150}
151
157static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp)
158{
159 BYTE Information = 0;
160
161 WINPR_ASSERT(drive);
162 WINPR_ASSERT(irp);
163 if (!irp->devman)
164 return ERROR_INVALID_PARAMETER;
165
166 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 6 * 4 + 8))
167 return ERROR_INVALID_DATA;
168
169 const uint32_t DesiredAccess = Stream_Get_UINT32(irp->input);
170 const uint64_t allocationSize = Stream_Get_UINT64(irp->input);
171 const uint32_t FileAttributes = Stream_Get_UINT32(irp->input);
172 const uint32_t SharedAccess = Stream_Get_UINT32(irp->input);
173 const uint32_t CreateDisposition = Stream_Get_UINT32(irp->input);
174 const uint32_t CreateOptions = Stream_Get_UINT32(irp->input);
175 const uint32_t PathLength = Stream_Get_UINT32(irp->input);
176
177 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, PathLength))
178 return ERROR_INVALID_DATA;
179
180 const WCHAR* path = Stream_ConstPointer(irp->input);
181 UINT32 FileId = irp->devman->id_sequence++;
182 DRIVE_FILE* file =
183 drive_file_new(drive->path, path, PathLength / sizeof(WCHAR), FileId, DesiredAccess,
184 CreateDisposition, CreateOptions, FileAttributes, SharedAccess);
185
186 if (!file)
187 {
188 irp->IoStatus = drive_map_windows_err(GetLastError());
189 FileId = 0;
190 Information = 0;
191 }
192 else
193 {
194 void* key = (void*)(size_t)file->id;
195
196 if (!ListDictionary_Add(drive->files, key, file))
197 {
198 WLog_ERR(TAG, "ListDictionary_Add failed!");
199 return ERROR_INTERNAL_ERROR;
200 }
201
202 switch (CreateDisposition)
203 {
204 case FILE_SUPERSEDE:
205 case FILE_OPEN:
206 case FILE_CREATE:
207 case FILE_OVERWRITE:
208 Information = FILE_SUPERSEDED;
209 break;
210
211 case FILE_OPEN_IF:
212 Information = FILE_OPENED;
213 break;
214
215 case FILE_OVERWRITE_IF:
216 Information = FILE_OVERWRITTEN;
217 break;
218
219 default:
220 Information = 0;
221 break;
222 }
223
224 if (allocationSize > 0)
225 {
226 const BYTE buffer[] = { '\0' };
227 if (!drive_file_seek(file, allocationSize - sizeof(buffer)))
228 return ERROR_INTERNAL_ERROR;
229 if (!drive_file_write(file, buffer, sizeof(buffer)))
230 return ERROR_INTERNAL_ERROR;
231 }
232 }
233
234 Stream_Write_UINT32(irp->output, FileId);
235 Stream_Write_UINT8(irp->output, Information);
236
237 return CHANNEL_RC_OK;
238}
239
245static UINT drive_process_irp_close(DRIVE_DEVICE* drive, IRP* irp)
246{
247 WINPR_ASSERT(drive);
248 WINPR_ASSERT(irp);
249 if (!irp->output)
250 return ERROR_INVALID_PARAMETER;
251
252 DRIVE_FILE* file = drive_get_file_by_id(drive, irp->FileId);
253 void* 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 return CHANNEL_RC_OK;
270}
271
277static UINT drive_process_irp_read(DRIVE_DEVICE* drive, IRP* irp)
278{
279 DRIVE_FILE* file = NULL;
280 UINT32 Length = 0;
281 UINT64 Offset = 0;
282
283 WINPR_ASSERT(drive);
284 WINPR_ASSERT(irp);
285 if (!irp->output)
286 return ERROR_INVALID_PARAMETER;
287
288 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12))
289 return ERROR_INVALID_DATA;
290
291 Stream_Read_UINT32(irp->input, Length);
292 Stream_Read_UINT64(irp->input, Offset);
293 file = drive_get_file_by_id(drive, irp->FileId);
294
295 if (!file)
296 {
297 irp->IoStatus = STATUS_UNSUCCESSFUL;
298 Length = 0;
299 }
300 else if (!drive_file_seek(file, Offset))
301 {
302 irp->IoStatus = drive_map_windows_err(GetLastError());
303 Length = 0;
304 }
305
306 if (!Stream_EnsureRemainingCapacity(irp->output, 4ull + Length))
307 {
308 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
309 return ERROR_INTERNAL_ERROR;
310 }
311 else if (Length == 0)
312 Stream_Write_UINT32(irp->output, 0);
313 else
314 {
315 BYTE* buffer = Stream_PointerAs(irp->output, BYTE) + sizeof(UINT32);
316
317 if (!drive_file_read(file, buffer, &Length))
318 {
319 irp->IoStatus = drive_map_windows_err(GetLastError());
320 Stream_Write_UINT32(irp->output, 0);
321 }
322 else
323 {
324 Stream_Write_UINT32(irp->output, Length);
325 Stream_Seek(irp->output, Length);
326 }
327 }
328
329 return CHANNEL_RC_OK;
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 WINPR_ASSERT(drive);
344 WINPR_ASSERT(irp);
345 if (!irp->input || !irp->output)
346 return ERROR_INVALID_PARAMETER;
347
348 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
349 return ERROR_INVALID_DATA;
350
351 Stream_Read_UINT32(irp->input, Length);
352 Stream_Read_UINT64(irp->input, Offset);
353 Stream_Seek(irp->input, 20); /* Padding */
354 const void* ptr = Stream_ConstPointer(irp->input);
355 if (!Stream_SafeSeek(irp->input, Length))
356 return ERROR_INVALID_DATA;
357 file = drive_get_file_by_id(drive, irp->FileId);
358
359 if (!file)
360 {
361 irp->IoStatus = STATUS_UNSUCCESSFUL;
362 Length = 0;
363 }
364 else if (!drive_file_seek(file, Offset))
365 {
366 irp->IoStatus = drive_map_windows_err(GetLastError());
367 Length = 0;
368 }
369 else if (!drive_file_write(file, ptr, Length))
370 {
371 irp->IoStatus = drive_map_windows_err(GetLastError());
372 Length = 0;
373 }
374
375 Stream_Write_UINT32(irp->output, Length);
376 Stream_Write_UINT8(irp->output, 0); /* Padding */
377
378 return CHANNEL_RC_OK;
379}
380
386static UINT drive_process_irp_query_information(DRIVE_DEVICE* drive, IRP* irp)
387{
388 DRIVE_FILE* file = NULL;
389 UINT32 FsInformationClass = 0;
390
391 WINPR_ASSERT(drive);
392 WINPR_ASSERT(irp);
393
394 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
395 return ERROR_INVALID_DATA;
396
397 Stream_Read_UINT32(irp->input, FsInformationClass);
398 file = drive_get_file_by_id(drive, irp->FileId);
399
400 if (!file)
401 {
402 irp->IoStatus = STATUS_UNSUCCESSFUL;
403 }
404 else if (!drive_file_query_information(file, FsInformationClass, irp->output))
405 {
406 irp->IoStatus = drive_map_windows_err(GetLastError());
407 }
408
409 return CHANNEL_RC_OK;
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 WINPR_ASSERT(drive);
424 WINPR_ASSERT(irp);
425 if (!irp->input || !irp->output)
426 return ERROR_INVALID_PARAMETER;
427
428 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
429 return ERROR_INVALID_DATA;
430
431 Stream_Read_UINT32(irp->input, FsInformationClass);
432 Stream_Read_UINT32(irp->input, Length);
433 Stream_Seek(irp->input, 24); /* Padding */
434 file = drive_get_file_by_id(drive, irp->FileId);
435
436 if (!file)
437 {
438 irp->IoStatus = STATUS_UNSUCCESSFUL;
439 }
440 else if (!drive_file_set_information(file, FsInformationClass, Length, irp->input))
441 {
442 irp->IoStatus = drive_map_windows_err(GetLastError());
443 }
444
445 Stream_Write_UINT32(irp->output, Length);
446
447 return CHANNEL_RC_OK;
448}
449
455static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive, IRP* irp)
456{
457 UINT32 FsInformationClass = 0;
458 DWORD lpSectorsPerCluster = 0;
459 DWORD lpBytesPerSector = 0;
460 DWORD lpNumberOfFreeClusters = 0;
461 DWORD lpTotalNumberOfClusters = 0;
462 WIN32_FILE_ATTRIBUTE_DATA wfad = { 0 };
463
464 WINPR_ASSERT(drive);
465 WINPR_ASSERT(irp);
466
467 wStream* 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 = freerdp_getApplicationDetailsStringW();
482 const size_t volumeLabelLen = (_wcslen(volumeLabel) + 1) * sizeof(WCHAR);
483 const size_t length = 17ul + volumeLabelLen;
484
485 if ((length > UINT32_MAX) || (volumeLabelLen > UINT32_MAX))
486 return CHANNEL_RC_NO_BUFFER;
487
488 Stream_Write_UINT32(output, (UINT32)length); /* Length */
489
490 if (!Stream_EnsureRemainingCapacity(output, length))
491 {
492 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
493 return CHANNEL_RC_NO_MEMORY;
494 }
495
496 GetFileAttributesExW(drive->path, GetFileExInfoStandard, &wfad);
497 Stream_Write_UINT32(output, wfad.ftCreationTime.dwLowDateTime); /* VolumeCreationTime */
498 Stream_Write_UINT32(output,
499 wfad.ftCreationTime.dwHighDateTime); /* VolumeCreationTime */
500 Stream_Write_UINT32(output, lpNumberOfFreeClusters & 0xffff); /* VolumeSerialNumber */
501 Stream_Write_UINT32(output, (UINT32)volumeLabelLen); /* VolumeLabelLength */
502 Stream_Write_UINT8(output, 0); /* SupportsObjects */
503 /* Reserved(1), MUST NOT be added! */
504 Stream_Write(output, volumeLabel, volumeLabelLen); /* VolumeLabel (Unicode) */
505 }
506 break;
507
508 case FileFsSizeInformation:
509 /* http://msdn.microsoft.com/en-us/library/cc232107.aspx */
510 Stream_Write_UINT32(output, 24); /* Length */
511
512 if (!Stream_EnsureRemainingCapacity(output, 24))
513 {
514 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
515 return CHANNEL_RC_NO_MEMORY;
516 }
517
518 Stream_Write_UINT64(output, lpTotalNumberOfClusters); /* TotalAllocationUnits */
519 Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */
520 Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */
521 Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */
522 break;
523
524 case FileFsAttributeInformation:
525 {
526 WCHAR LabelBuffer[32] = { 0 };
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 return CHANNEL_RC_OK;
593}
594
595/* http://msdn.microsoft.com/en-us/library/cc241518.aspx */
596
602static UINT drive_process_irp_silent_ignore(WINPR_ATTR_UNUSED DRIVE_DEVICE* drive, IRP* irp)
603{
604 WINPR_ASSERT(drive);
605 WINPR_ASSERT(irp);
606 if (!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 return CHANNEL_RC_OK;
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 WINPR_ASSERT(drive);
633 WINPR_ASSERT(irp);
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 return CHANNEL_RC_OK;
660}
661
667static UINT drive_process_irp_directory_control(DRIVE_DEVICE* drive, IRP* irp)
668{
669 WINPR_ASSERT(drive);
670 WINPR_ASSERT(irp);
671
672 switch (irp->MinorFunction)
673 {
674 case IRP_MN_QUERY_DIRECTORY:
675 return drive_process_irp_query_directory(drive, irp);
676
677 case IRP_MN_NOTIFY_CHANGE_DIRECTORY: /* TODO */
678 irp->IoStatus = STATUS_NOT_SUPPORTED;
679 Stream_Write_UINT32(irp->output, 0); /* Length */
680 break;
681
682 default:
683 irp->IoStatus = STATUS_NOT_SUPPORTED;
684 Stream_Write_UINT32(irp->output, 0); /* Length */
685 break;
686 }
687
688 return CHANNEL_RC_OK;
689}
690
696static UINT drive_process_irp_device_control(WINPR_ATTR_UNUSED DRIVE_DEVICE* drive, IRP* irp)
697{
698 WINPR_ASSERT(drive);
699 WINPR_ASSERT(irp);
700
701 Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
702 return CHANNEL_RC_OK;
703}
704
705static UINT drive_evaluate(UINT error, IRP* irp)
706{
707 WINPR_ASSERT(irp);
708 if (error == CHANNEL_RC_OK)
709 {
710 WINPR_ASSERT(irp->Complete);
711 return irp->Complete(irp);
712 }
713
714 WLog_ERR(TAG, "IRP %s failed with %" PRIu32, rdpdr_irp_string(irp->MajorFunction), error);
715 WINPR_ASSERT(irp->Discard);
716 irp->Discard(irp);
717 return error;
718}
719
725static UINT drive_process_irp(DRIVE_DEVICE* drive, IRP* irp)
726{
727 UINT error = CHANNEL_RC_OK;
728 WINPR_ASSERT(drive);
729 WINPR_ASSERT(irp);
730
731 irp->IoStatus = STATUS_SUCCESS;
732
733 switch (irp->MajorFunction)
734 {
735 case IRP_MJ_CREATE:
736 error = drive_process_irp_create(drive, irp);
737 break;
738
739 case IRP_MJ_CLOSE:
740 error = drive_process_irp_close(drive, irp);
741 break;
742
743 case IRP_MJ_READ:
744 error = drive_process_irp_read(drive, irp);
745 break;
746
747 case IRP_MJ_WRITE:
748 error = drive_process_irp_write(drive, irp);
749 break;
750
751 case IRP_MJ_QUERY_INFORMATION:
752 error = drive_process_irp_query_information(drive, irp);
753 break;
754
755 case IRP_MJ_SET_INFORMATION:
756 error = drive_process_irp_set_information(drive, irp);
757 break;
758
759 case IRP_MJ_QUERY_VOLUME_INFORMATION:
760 error = drive_process_irp_query_volume_information(drive, irp);
761 break;
762
763 case IRP_MJ_LOCK_CONTROL:
764 error = drive_process_irp_silent_ignore(drive, irp);
765 break;
766
767 case IRP_MJ_DIRECTORY_CONTROL:
768 error = drive_process_irp_directory_control(drive, irp);
769 break;
770
771 case IRP_MJ_DEVICE_CONTROL:
772 error = drive_process_irp_device_control(drive, irp);
773 break;
774
775 default:
776 irp->IoStatus = STATUS_NOT_SUPPORTED;
777 break;
778 }
779
780 return drive_evaluate(error, irp);
781}
782
783static BOOL drive_poll_run(DRIVE_DEVICE* drive, IRP* irp)
784{
785 WINPR_ASSERT(drive);
786
787 if (irp)
788 {
789 const UINT error = drive_process_irp(drive, irp);
790 if (error)
791 {
792 WLog_ERR(TAG, "drive_process_irp failed with error %" PRIu32 "!", error);
793 return FALSE;
794 }
795 }
796
797 return TRUE;
798}
799
800static DWORD WINAPI drive_thread_func(LPVOID arg)
801{
802 DRIVE_DEVICE* drive = (DRIVE_DEVICE*)arg;
803 UINT error = CHANNEL_RC_OK;
804
805 if (!drive)
806 {
807 error = ERROR_INVALID_PARAMETER;
808 goto fail;
809 }
810
811 while (1)
812 {
813 if (!MessageQueue_Wait(drive->IrpQueue))
814 {
815 WLog_ERR(TAG, "MessageQueue_Wait failed!");
816 error = ERROR_INTERNAL_ERROR;
817 break;
818 }
819
820 if (MessageQueue_Size(drive->IrpQueue) < 1)
821 continue;
822
823 wMessage message = { 0 };
824 if (!MessageQueue_Peek(drive->IrpQueue, &message, TRUE))
825 {
826 WLog_ERR(TAG, "MessageQueue_Peek failed!");
827 continue;
828 }
829
830 if (message.id == WMQ_QUIT)
831 break;
832
833 IRP* irp = (IRP*)message.wParam;
834 if (!drive_poll_run(drive, irp))
835 break;
836 }
837
838fail:
839
840 if (error && drive && drive->rdpcontext)
841 setChannelError(drive->rdpcontext, error, "drive_thread_func reported an error");
842
843 ExitThread(error);
844 return error;
845}
846
852static UINT drive_irp_request(DEVICE* device, IRP* irp)
853{
854 DRIVE_DEVICE* drive = (DRIVE_DEVICE*)device;
855
856 if (!drive)
857 return ERROR_INVALID_PARAMETER;
858
859 if (drive->async)
860 {
861 if (!MessageQueue_Post(drive->IrpQueue, NULL, 0, (void*)irp, NULL))
862 {
863 WLog_ERR(TAG, "MessageQueue_Post failed!");
864 return ERROR_INTERNAL_ERROR;
865 }
866 }
867 else
868 {
869 if (!drive_poll_run(drive, irp))
870 return ERROR_INTERNAL_ERROR;
871 }
872
873 return CHANNEL_RC_OK;
874}
875
876static UINT drive_free_int(DRIVE_DEVICE* drive)
877{
878 UINT error = CHANNEL_RC_OK;
879
880 if (!drive)
881 return ERROR_INVALID_PARAMETER;
882
883 (void)CloseHandle(drive->thread);
884 ListDictionary_Free(drive->files);
885 MessageQueue_Free(drive->IrpQueue);
886 Stream_Free(drive->device.data, TRUE);
887 free(drive->path);
888 free(drive);
889 return error;
890}
891
897static UINT drive_free(DEVICE* device)
898{
899 DRIVE_DEVICE* drive = (DRIVE_DEVICE*)device;
900 UINT error = CHANNEL_RC_OK;
901
902 if (!drive)
903 return ERROR_INVALID_PARAMETER;
904
905 if (MessageQueue_PostQuit(drive->IrpQueue, 0) &&
906 (WaitForSingleObject(drive->thread, INFINITE) == WAIT_FAILED))
907 {
908 error = GetLastError();
909 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
910 return error;
911 }
912
913 return drive_free_int(drive);
914}
915
919static void drive_file_objfree(void* obj)
920{
921 drive_file_free((DRIVE_FILE*)obj);
922}
923
924static void drive_message_free(void* obj)
925{
926 wMessage* msg = obj;
927 if (!msg)
928 return;
929 if (msg->id != 0)
930 return;
931
932 IRP* irp = (IRP*)msg->wParam;
933 if (!irp)
934 return;
935 WINPR_ASSERT(irp->Discard);
936 irp->Discard(irp);
937}
938
944static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, const char* name,
945 const char* path, BOOL automount, uint32_t* pid)
946{
947 WINPR_ASSERT(pid);
948
949 size_t length = 0;
950 DRIVE_DEVICE* drive = NULL;
951 UINT error = ERROR_INTERNAL_ERROR;
952
953 if (!pEntryPoints || !name || !path)
954 {
955 WLog_ERR(TAG, "Invalid parameters: pEntryPoints=%p, name=%p, path=%p",
956 WINPR_CXX_COMPAT_CAST(const void*, pEntryPoints),
957 WINPR_CXX_COMPAT_CAST(const void*, name),
958 WINPR_CXX_COMPAT_CAST(const void*, path));
959 return ERROR_INVALID_PARAMETER;
960 }
961
962 if (name[0] && path[0])
963 {
964 size_t pathLength = strnlen(path, MAX_PATH);
965 drive = (DRIVE_DEVICE*)calloc(1, sizeof(DRIVE_DEVICE));
966
967 if (!drive)
968 {
969 WLog_ERR(TAG, "calloc failed!");
970 return CHANNEL_RC_NO_MEMORY;
971 }
972
973 drive->device.type = RDPDR_DTYP_FILESYSTEM;
974 drive->device.IRPRequest = drive_irp_request;
975 drive->device.Free = drive_free;
976 drive->rdpcontext = pEntryPoints->rdpcontext;
977 drive->automount = automount;
978 length = strlen(name);
979 drive->device.data = Stream_New(NULL, length + 1);
980
981 if (!drive->device.data)
982 {
983 WLog_ERR(TAG, "Stream_New failed!");
984 error = CHANNEL_RC_NO_MEMORY;
985 goto out_error;
986 }
987
988 for (size_t i = 0; i < length; i++)
989 {
990 /* Filter 2.2.1.3 Device Announce Header (DEVICE_ANNOUNCE) forbidden symbols */
991 switch (name[i])
992 {
993 case ':':
994 case '<':
995 case '>':
996 case '\"':
997 case '/':
998 case '\\':
999 case '|':
1000 case ' ':
1001 Stream_Write_UINT8(drive->device.data, '_');
1002 break;
1003 default:
1004 Stream_Write_UINT8(drive->device.data, (BYTE)name[i]);
1005 break;
1006 }
1007 }
1008 Stream_Write_UINT8(drive->device.data, '\0');
1009
1010 drive->device.name = Stream_BufferAs(drive->device.data, char);
1011 if (!drive->device.name)
1012 goto out_error;
1013
1014 if ((pathLength > 1) && (path[pathLength - 1] == '/'))
1015 pathLength--;
1016
1017 drive->path = ConvertUtf8NToWCharAlloc(path, pathLength, NULL);
1018 if (!drive->path)
1019 {
1020 error = CHANNEL_RC_NO_MEMORY;
1021 goto out_error;
1022 }
1023
1024 drive->files = ListDictionary_New(TRUE);
1025
1026 if (!drive->files)
1027 {
1028 WLog_ERR(TAG, "ListDictionary_New failed!");
1029 error = CHANNEL_RC_NO_MEMORY;
1030 goto out_error;
1031 }
1032
1033 ListDictionary_ValueObject(drive->files)->fnObjectFree = drive_file_objfree;
1034 drive->IrpQueue = MessageQueue_New(NULL);
1035
1036 if (!drive->IrpQueue)
1037 {
1038 WLog_ERR(TAG, "ListDictionary_New failed!");
1039 error = CHANNEL_RC_NO_MEMORY;
1040 goto out_error;
1041 }
1042
1043 wObject* obj = MessageQueue_Object(drive->IrpQueue);
1044 WINPR_ASSERT(obj);
1045 obj->fnObjectFree = drive_message_free;
1046
1047 if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &drive->device)))
1048 {
1049 WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error);
1050 goto out_error;
1051 }
1052 *pid = drive->device.id;
1053
1054 drive->async = !freerdp_settings_get_bool(drive->rdpcontext->settings,
1055 FreeRDP_SynchronousStaticChannels);
1056 if (drive->async)
1057 {
1058 if (!(drive->thread =
1059 CreateThread(NULL, 0, drive_thread_func, drive, CREATE_SUSPENDED, NULL)))
1060 {
1061 WLog_ERR(TAG, "CreateThread failed!");
1062 goto out_error;
1063 }
1064
1065 ResumeThread(drive->thread);
1066 }
1067 }
1068
1069 return CHANNEL_RC_OK;
1070out_error:
1071 drive_free_int(drive);
1072 return error;
1073}
1074
1075static BOOL drive_filtered(const WCHAR* drive)
1076{
1077 const WCHAR a[] = { 'A', '\0' };
1078 const WCHAR b[] = { 'B', '\0' };
1079 const WCHAR la[] = { 'a', '\0' };
1080 const WCHAR lb[] = { 'b', '\0' };
1081 const WCHAR* list[] = { a, b, la, lb };
1082
1083 for (size_t x = 0; x < ARRAYSIZE(list); x++)
1084 {
1085 const WCHAR* cur = list[x];
1086 if (_wcsncmp(drive, cur, 2) == 0)
1087 return TRUE;
1088 }
1089 return FALSE;
1090}
1091
1092static UINT handle_all_drives(RDPDR_DRIVE* drive, PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
1093{
1094 UINT error = ERROR_INTERNAL_ERROR;
1095
1096 WINPR_ASSERT(drive);
1097 WINPR_ASSERT(pEntryPoints);
1098
1099 /* Enumerate all devices: */
1100 const DWORD dlen = GetLogicalDriveStringsW(0, NULL);
1101
1102 WCHAR* devlist = calloc(dlen, sizeof(WCHAR));
1103 if (!devlist)
1104 return ERROR_OUTOFMEMORY;
1105
1106 const DWORD rc = GetLogicalDriveStringsW(dlen, devlist);
1107 if (rc >= dlen)
1108 goto fail;
1109
1110 for (size_t offset = 0, len = 0; offset < rc; offset += len + 1)
1111 {
1112 len = _wcsnlen(&devlist[offset], rc - offset);
1113
1114 const WCHAR* dev = &devlist[offset];
1115 if (!drive_filtered(dev))
1116 {
1117 char* bufdup = NULL;
1118 char* devdup = ConvertWCharNToUtf8Alloc(dev, len, NULL);
1119 if (!devdup)
1120 {
1121 error = ERROR_OUTOFMEMORY;
1122 goto fail;
1123 }
1124 size_t size = 0;
1125 winpr_asprintf(&bufdup, &size, "%s_%s", drive->device.Name, devdup);
1126
1127 error =
1128 drive_register_drive_path(pEntryPoints, bufdup, devdup, TRUE, &drive->device.Id);
1129 free(devdup);
1130 free(bufdup);
1131 if (error != CHANNEL_RC_OK)
1132 goto fail;
1133 }
1134 }
1135
1136 error = CHANNEL_RC_OK;
1137
1138fail:
1139 free(devlist);
1140
1141 return error;
1142}
1143
1149FREERDP_ENTRY_POINT(
1150 UINT VCAPITYPE drive_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
1151{
1152 UINT error = 0;
1153
1154 WINPR_ASSERT(pEntryPoints);
1155
1156 RDPDR_DRIVE* drive = (RDPDR_DRIVE*)pEntryPoints->device;
1157 WINPR_ASSERT(drive);
1158
1159 const char all[] = "*";
1160 const char home[] = "%";
1161 if (strncmp(drive->Path, all, sizeof(all)) == 0)
1162 {
1163 error = handle_all_drives(drive, pEntryPoints);
1164 }
1165 else if (strncmp(drive->Path, home, sizeof(home)) == 0)
1166 {
1167 free(drive->Path);
1168 drive->Path = GetKnownPath(KNOWN_PATH_HOME);
1169
1170 if (!drive->Path)
1171 {
1172 WLog_ERR(TAG, "_strdup failed!");
1173 return CHANNEL_RC_NO_MEMORY;
1174 }
1175 error = drive_register_drive_path(pEntryPoints, drive->device.Name, drive->Path,
1176 drive->automount, &drive->device.Id);
1177 }
1178 else
1179 {
1180 error = drive_register_drive_path(pEntryPoints, drive->device.Name, drive->Path,
1181 drive->automount, &drive->device.Id);
1182 }
1183 return error;
1184}
WINPR_ATTR_NODISCARD 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:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:58