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