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
140WINPR_ATTR_NODISCARD
141static DRIVE_FILE* drive_get_file_by_id(DRIVE_DEVICE* drive, UINT32 id)
142{
143 DRIVE_FILE* file = nullptr;
144 void* key = (void*)(size_t)id;
145
146 if (!drive)
147 return nullptr;
148
149 file = (DRIVE_FILE*)ListDictionary_GetItemValue(drive->files, key);
150 return file;
151}
152
158static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp)
159{
160 BYTE Information = 0;
161
162 WINPR_ASSERT(drive);
163 WINPR_ASSERT(irp);
164 if (!irp->devman)
165 return ERROR_INVALID_PARAMETER;
166
167 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 6 * 4 + 8))
168 return ERROR_INVALID_DATA;
169
170 const uint32_t DesiredAccess = Stream_Get_UINT32(irp->input);
171 const uint64_t allocationSize = Stream_Get_UINT64(irp->input);
172 const uint32_t FileAttributes = Stream_Get_UINT32(irp->input);
173 const uint32_t SharedAccess = Stream_Get_UINT32(irp->input);
174 const uint32_t CreateDisposition = Stream_Get_UINT32(irp->input);
175 const uint32_t CreateOptions = Stream_Get_UINT32(irp->input);
176 const uint32_t PathLength = Stream_Get_UINT32(irp->input);
177
178 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, PathLength))
179 return ERROR_INVALID_DATA;
180
181 const WCHAR* path = Stream_ConstPointer(irp->input);
182 UINT32 FileId = irp->devman->id_sequence++;
183 DRIVE_FILE* file =
184 drive_file_new(drive->path, path, PathLength / sizeof(WCHAR), FileId, DesiredAccess,
185 CreateDisposition, CreateOptions, FileAttributes, SharedAccess);
186
187 if (!file)
188 {
189 irp->IoStatus = drive_map_windows_err(GetLastError());
190 FileId = 0;
191 Information = 0;
192 }
193 else
194 {
195 void* key = (void*)(size_t)file->id;
196
197 switch (CreateDisposition)
198 {
199 case FILE_SUPERSEDE:
200 case FILE_OPEN:
201 case FILE_CREATE:
202 case FILE_OVERWRITE:
203 Information = FILE_SUPERSEDED;
204 break;
205
206 case FILE_OPEN_IF:
207 Information = FILE_OPENED;
208 break;
209
210 case FILE_OVERWRITE_IF:
211 Information = FILE_OVERWRITTEN;
212 break;
213
214 default:
215 Information = 0;
216 break;
217 }
218
219 if (allocationSize > 0)
220 {
221 const BYTE buffer[] = { '\0' };
222 if (!drive_file_seek(file, allocationSize - sizeof(buffer)) ||
223 !drive_file_write(file, buffer, sizeof(buffer)))
224 {
225 drive_file_free(file);
226 return ERROR_INTERNAL_ERROR;
227 }
228 }
229
230 if (!ListDictionary_Add(drive->files, key, file))
231 {
232 WLog_ERR(TAG, "ListDictionary_Add failed!");
233 drive_file_free(file);
234 return ERROR_INTERNAL_ERROR;
235 }
236 }
237
238 Stream_Write_UINT32(irp->output, FileId);
239 Stream_Write_UINT8(irp->output, Information);
240
241 return CHANNEL_RC_OK;
242}
243
249static UINT drive_process_irp_close(DRIVE_DEVICE* drive, IRP* irp)
250{
251 WINPR_ASSERT(drive);
252 WINPR_ASSERT(irp);
253 if (!irp->output)
254 return ERROR_INVALID_PARAMETER;
255
256 DRIVE_FILE* file = drive_get_file_by_id(drive, irp->FileId);
257 void* key = (void*)(size_t)irp->FileId;
258
259 if (!file)
260 irp->IoStatus = STATUS_UNSUCCESSFUL;
261 else
262 {
263 ListDictionary_Remove(drive->files, key);
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 = nullptr;
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
301 if (!Stream_EnsureRemainingCapacity(irp->output, 4ull))
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 const size_t pos = Stream_GetPosition(irp->output);
311 Stream_Seek_UINT32(irp->output);
312 const BOOL rc = drive_file_read(file, irp->output, Offset, &Length);
313 if (!Stream_SetPosition(irp->output, pos))
314 return ERROR_INTERNAL_ERROR;
315 if (!rc)
316 {
317 irp->IoStatus = drive_map_windows_err(GetLastError());
318 Stream_Write_UINT32(irp->output, 0);
319 }
320 else
321 {
322 Stream_Write_UINT32(irp->output, Length);
323 Stream_Seek(irp->output, Length);
324 }
325 }
326
327 return CHANNEL_RC_OK;
328}
329
335static UINT drive_process_irp_write(DRIVE_DEVICE* drive, IRP* irp)
336{
337 DRIVE_FILE* file = nullptr;
338 UINT32 Length = 0;
339 UINT64 Offset = 0;
340
341 WINPR_ASSERT(drive);
342 WINPR_ASSERT(irp);
343 if (!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 return CHANNEL_RC_OK;
377}
378
384static UINT drive_process_irp_query_information(DRIVE_DEVICE* drive, IRP* irp)
385{
386 DRIVE_FILE* file = nullptr;
387 UINT32 FsInformationClass = 0;
388
389 WINPR_ASSERT(drive);
390 WINPR_ASSERT(irp);
391
392 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
393 return ERROR_INVALID_DATA;
394
395 Stream_Read_UINT32(irp->input, FsInformationClass);
396 file = drive_get_file_by_id(drive, irp->FileId);
397
398 if (!file)
399 {
400 irp->IoStatus = STATUS_UNSUCCESSFUL;
401 }
402 else if (!drive_file_query_information(file, FsInformationClass, irp->output))
403 {
404 irp->IoStatus = drive_map_windows_err(GetLastError());
405 }
406
407 return CHANNEL_RC_OK;
408}
409
415static UINT drive_process_irp_set_information(DRIVE_DEVICE* drive, IRP* irp)
416{
417 DRIVE_FILE* file = nullptr;
418 UINT32 FsInformationClass = 0;
419 UINT32 Length = 0;
420
421 WINPR_ASSERT(drive);
422 WINPR_ASSERT(irp);
423 if (!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 return CHANNEL_RC_OK;
446}
447
453static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive, IRP* irp)
454{
455 UINT32 FsInformationClass = 0;
456 DWORD lpSectorsPerCluster = 0;
457 DWORD lpBytesPerSector = 0;
458 DWORD lpNumberOfFreeClusters = 0;
459 DWORD lpTotalNumberOfClusters = 0;
460 WIN32_FILE_ATTRIBUTE_DATA wfad = WINPR_C_ARRAY_INIT;
461
462 WINPR_ASSERT(drive);
463 WINPR_ASSERT(irp);
464
465 wStream* output = irp->output;
466
467 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
468 return ERROR_INVALID_DATA;
469
470 Stream_Read_UINT32(irp->input, FsInformationClass);
471 if (!GetDiskFreeSpaceW(drive->path, &lpSectorsPerCluster, &lpBytesPerSector,
472 &lpNumberOfFreeClusters, &lpTotalNumberOfClusters))
473 {
474 const UINT32 err = GetLastError();
475 const HRESULT herr = HRESULT_FROM_WIN32(err);
476 WLog_WARN(TAG, "GetDiskFreeSpaceW failed: %s [%" PRId32 "], win32=%s [%" PRIu32 "]",
477 NtStatus2Tag(herr), herr, Win32ErrorCode2Tag(err & 0xFFFF), err);
478 lpSectorsPerCluster = 0;
479 lpBytesPerSector = 0;
480 lpNumberOfFreeClusters = 0;
481 lpTotalNumberOfClusters = 0;
482 }
483
484 switch (FsInformationClass)
485 {
486 case FileFsVolumeInformation:
487 {
488 /* http://msdn.microsoft.com/en-us/library/cc232108.aspx */
489 const WCHAR* volumeLabel = freerdp_getApplicationDetailsStringW();
490 const size_t volumeLabelLen = (_wcslen(volumeLabel) + 1) * sizeof(WCHAR);
491 const size_t length = 17ul + volumeLabelLen;
492
493 if ((length > UINT32_MAX) || (volumeLabelLen > UINT32_MAX))
494 return CHANNEL_RC_NO_BUFFER;
495
496 Stream_Write_UINT32(output, (UINT32)length); /* Length */
497
498 if (!Stream_EnsureRemainingCapacity(output, length))
499 {
500 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
501 return CHANNEL_RC_NO_MEMORY;
502 }
503
504 if (!GetFileAttributesExW(drive->path, GetFileExInfoStandard, &wfad))
505 {
506 const UINT32 err = GetLastError();
507 const HRESULT herr = HRESULT_FROM_WIN32(err);
508 WLog_WARN(TAG,
509 "GetFileAttributesExW failed: %s [%" PRId32 "], win32=%s [%" PRIu32 "]",
510 NtStatus2Tag(herr), herr, Win32ErrorCode2Tag(err & 0xFFFF), err);
511
512 const WIN32_FILE_ATTRIBUTE_DATA empty = WINPR_C_ARRAY_INIT;
513 wfad = empty;
514 }
515
516 Stream_Write_UINT32(output, wfad.ftCreationTime.dwLowDateTime); /* VolumeCreationTime */
517 Stream_Write_UINT32(output,
518 wfad.ftCreationTime.dwHighDateTime); /* VolumeCreationTime */
519 Stream_Write_UINT32(output, lpNumberOfFreeClusters & 0xffff); /* VolumeSerialNumber */
520 Stream_Write_UINT32(output, (UINT32)volumeLabelLen); /* VolumeLabelLength */
521 Stream_Write_UINT8(output, 0); /* SupportsObjects */
522 /* Reserved(1), MUST NOT be added! */
523 Stream_Write(output, volumeLabel, volumeLabelLen); /* VolumeLabel (Unicode) */
524 }
525 break;
526
527 case FileFsSizeInformation:
528 /* http://msdn.microsoft.com/en-us/library/cc232107.aspx */
529 Stream_Write_UINT32(output, 24); /* Length */
530
531 if (!Stream_EnsureRemainingCapacity(output, 24))
532 {
533 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
534 return CHANNEL_RC_NO_MEMORY;
535 }
536
537 Stream_Write_UINT64(output, lpTotalNumberOfClusters); /* TotalAllocationUnits */
538 Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */
539 Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */
540 Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */
541 break;
542
543 case FileFsAttributeInformation:
544 {
545 WCHAR LabelBuffer[32] = WINPR_C_ARRAY_INIT;
546 /* http://msdn.microsoft.com/en-us/library/cc232101.aspx */
547 const WCHAR* diskType =
548 InitializeConstWCharFromUtf8("FAT32", LabelBuffer, ARRAYSIZE(LabelBuffer));
549 const size_t diskTypeLen = (_wcslen(diskType) + 1) * sizeof(WCHAR);
550 const size_t length = 12ul + diskTypeLen;
551
552 if ((length > UINT32_MAX) || (diskTypeLen > UINT32_MAX))
553 return CHANNEL_RC_NO_BUFFER;
554
555 Stream_Write_UINT32(output, (UINT32)length); /* Length */
556
557 if (!Stream_EnsureRemainingCapacity(output, length))
558 {
559 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
560 return CHANNEL_RC_NO_MEMORY;
561 }
562
563 Stream_Write_UINT32(output, FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES |
564 FILE_UNICODE_ON_DISK); /* FileSystemAttributes */
565 Stream_Write_UINT32(output, MAX_PATH); /* MaximumComponentNameLength */
566 Stream_Write_UINT32(output, (UINT32)diskTypeLen); /* FileSystemNameLength */
567 Stream_Write(output, diskType, diskTypeLen); /* FileSystemName (Unicode) */
568 }
569 break;
570
571 case FileFsFullSizeInformation:
572 /* http://msdn.microsoft.com/en-us/library/cc232104.aspx */
573 Stream_Write_UINT32(output, 32); /* Length */
574
575 if (!Stream_EnsureRemainingCapacity(output, 32))
576 {
577 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
578 return CHANNEL_RC_NO_MEMORY;
579 }
580
581 Stream_Write_UINT64(output, lpTotalNumberOfClusters); /* TotalAllocationUnits */
582 Stream_Write_UINT64(output,
583 lpNumberOfFreeClusters); /* CallerAvailableAllocationUnits */
584 Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */
585 Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */
586 Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */
587 break;
588
589 case FileFsDeviceInformation:
590 /* http://msdn.microsoft.com/en-us/library/cc232109.aspx */
591 Stream_Write_UINT32(output, 8); /* Length */
592
593 if (!Stream_EnsureRemainingCapacity(output, 8))
594 {
595 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
596 return CHANNEL_RC_NO_MEMORY;
597 }
598
599 Stream_Write_UINT32(output, FILE_DEVICE_DISK); /* DeviceType */
600 Stream_Write_UINT32(output, 0); /* Characteristics */
601 break;
602
603 default:
604 WLog_WARN(TAG, "Unhandled FSInformationClass %s [0x%08" PRIx32 "]",
605 FSInformationClass2Tag(FsInformationClass), FsInformationClass);
606 irp->IoStatus = STATUS_UNSUCCESSFUL;
607 Stream_Write_UINT32(output, 0); /* Length */
608 break;
609 }
610
611 return CHANNEL_RC_OK;
612}
613
614/* http://msdn.microsoft.com/en-us/library/cc241518.aspx */
615
621static UINT drive_process_irp_silent_ignore(WINPR_ATTR_UNUSED DRIVE_DEVICE* drive, IRP* irp)
622{
623 WINPR_ASSERT(drive);
624 WINPR_ASSERT(irp);
625 if (!irp->output)
626 return ERROR_INVALID_PARAMETER;
627
628 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
629 return ERROR_INVALID_DATA;
630
631 const uint32_t FsInformationClass = Stream_Get_UINT32(irp->input);
632 WLog_VRB(TAG, "Silently ignore FSInformationClass %s [0x%08" PRIx32 "]",
633 FSInformationClass2Tag(FsInformationClass), FsInformationClass);
634 Stream_Write_UINT32(irp->output, 0); /* Length */
635 return CHANNEL_RC_OK;
636}
637
643static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp)
644{
645 const WCHAR* path = nullptr;
646 DRIVE_FILE* file = nullptr;
647 BYTE InitialQuery = 0;
648 UINT32 PathLength = 0;
649 UINT32 FsInformationClass = 0;
650
651 WINPR_ASSERT(drive);
652 WINPR_ASSERT(irp);
653
654 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
655 return ERROR_INVALID_DATA;
656
657 Stream_Read_UINT32(irp->input, FsInformationClass);
658 Stream_Read_UINT8(irp->input, InitialQuery);
659 Stream_Read_UINT32(irp->input, PathLength);
660 Stream_Seek(irp->input, 23); /* Padding */
661 path = Stream_ConstPointer(irp->input);
662 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, PathLength))
663 return ERROR_INVALID_DATA;
664
665 file = drive_get_file_by_id(drive, irp->FileId);
666
667 if (file == nullptr)
668 {
669 irp->IoStatus = STATUS_UNSUCCESSFUL;
670 Stream_Write_UINT32(irp->output, 0); /* Length */
671 }
672 else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path,
673 PathLength / sizeof(WCHAR), irp->output))
674 {
675 irp->IoStatus = drive_map_windows_err(GetLastError());
676 }
677
678 return CHANNEL_RC_OK;
679}
680
686static UINT drive_process_irp_directory_control(DRIVE_DEVICE* drive, IRP* irp)
687{
688 WINPR_ASSERT(drive);
689 WINPR_ASSERT(irp);
690
691 switch (irp->MinorFunction)
692 {
693 case IRP_MN_QUERY_DIRECTORY:
694 return drive_process_irp_query_directory(drive, irp);
695
696 case IRP_MN_NOTIFY_CHANGE_DIRECTORY: /* TODO */
697 irp->IoStatus = STATUS_NOT_SUPPORTED;
698 Stream_Write_UINT32(irp->output, 0); /* Length */
699 break;
700
701 default:
702 irp->IoStatus = STATUS_NOT_SUPPORTED;
703 Stream_Write_UINT32(irp->output, 0); /* Length */
704 break;
705 }
706
707 return CHANNEL_RC_OK;
708}
709
715static UINT drive_process_irp_device_control(WINPR_ATTR_UNUSED DRIVE_DEVICE* drive, IRP* irp)
716{
717 WINPR_ASSERT(drive);
718 WINPR_ASSERT(irp);
719
720 Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
721 return CHANNEL_RC_OK;
722}
723
724static UINT drive_evaluate(UINT error, IRP* irp)
725{
726 WINPR_ASSERT(irp);
727 if (error == CHANNEL_RC_OK)
728 {
729 WINPR_ASSERT(irp->Complete);
730 return irp->Complete(irp);
731 }
732
733 WLog_ERR(TAG, "IRP %s failed with %" PRIu32, rdpdr_irp_string(irp->MajorFunction), error);
734 WINPR_ASSERT(irp->Discard);
735 irp->Discard(irp);
736 return error;
737}
738
744static UINT drive_process_irp(DRIVE_DEVICE* drive, IRP* irp)
745{
746 UINT error = CHANNEL_RC_OK;
747 WINPR_ASSERT(drive);
748 WINPR_ASSERT(irp);
749
750 irp->IoStatus = STATUS_SUCCESS;
751
752 switch (irp->MajorFunction)
753 {
754 case IRP_MJ_CREATE:
755 error = drive_process_irp_create(drive, irp);
756 break;
757
758 case IRP_MJ_CLOSE:
759 error = drive_process_irp_close(drive, irp);
760 break;
761
762 case IRP_MJ_READ:
763 error = drive_process_irp_read(drive, irp);
764 break;
765
766 case IRP_MJ_WRITE:
767 error = drive_process_irp_write(drive, irp);
768 break;
769
770 case IRP_MJ_QUERY_INFORMATION:
771 error = drive_process_irp_query_information(drive, irp);
772 break;
773
774 case IRP_MJ_SET_INFORMATION:
775 error = drive_process_irp_set_information(drive, irp);
776 break;
777
778 case IRP_MJ_QUERY_VOLUME_INFORMATION:
779 error = drive_process_irp_query_volume_information(drive, irp);
780 break;
781
782 case IRP_MJ_LOCK_CONTROL:
783 error = drive_process_irp_silent_ignore(drive, irp);
784 break;
785
786 case IRP_MJ_DIRECTORY_CONTROL:
787 error = drive_process_irp_directory_control(drive, irp);
788 break;
789
790 case IRP_MJ_DEVICE_CONTROL:
791 error = drive_process_irp_device_control(drive, irp);
792 break;
793
794 default:
795 irp->IoStatus = STATUS_NOT_SUPPORTED;
796 break;
797 }
798
799 return drive_evaluate(error, irp);
800}
801
802static BOOL drive_poll_run(DRIVE_DEVICE* drive, IRP* irp)
803{
804 WINPR_ASSERT(drive);
805
806 if (irp)
807 {
808 const UINT error = drive_process_irp(drive, irp);
809 if (error)
810 {
811 WLog_ERR(TAG, "drive_process_irp failed with error %" PRIu32 "!", error);
812 return FALSE;
813 }
814 }
815
816 return TRUE;
817}
818
819static DWORD WINAPI drive_thread_func(LPVOID arg)
820{
821 DRIVE_DEVICE* drive = (DRIVE_DEVICE*)arg;
822 UINT error = CHANNEL_RC_OK;
823
824 if (!drive)
825 {
826 error = ERROR_INVALID_PARAMETER;
827 goto fail;
828 }
829
830 while (1)
831 {
832 if (!MessageQueue_Wait(drive->IrpQueue))
833 {
834 WLog_ERR(TAG, "MessageQueue_Wait failed!");
835 error = ERROR_INTERNAL_ERROR;
836 break;
837 }
838
839 if (MessageQueue_Size(drive->IrpQueue) < 1)
840 continue;
841
842 wMessage message = WINPR_C_ARRAY_INIT;
843 if (!MessageQueue_Peek(drive->IrpQueue, &message, TRUE))
844 {
845 WLog_ERR(TAG, "MessageQueue_Peek failed!");
846 continue;
847 }
848
849 if (message.id == WMQ_QUIT)
850 break;
851
852 IRP* irp = (IRP*)message.wParam;
853 if (!drive_poll_run(drive, irp))
854 break;
855 }
856
857fail:
858
859 if (error && drive && drive->rdpcontext)
860 setChannelError(drive->rdpcontext, error, "drive_thread_func reported an error");
861
862 ExitThread(error);
863 return error;
864}
865
871static UINT drive_irp_request(DEVICE* device, IRP* irp)
872{
873 DRIVE_DEVICE* drive = (DRIVE_DEVICE*)device;
874
875 if (!drive)
876 return ERROR_INVALID_PARAMETER;
877
878 if (drive->async)
879 {
880 if (!MessageQueue_Post(drive->IrpQueue, nullptr, 0, (void*)irp, nullptr))
881 {
882 WLog_ERR(TAG, "MessageQueue_Post failed!");
883 return ERROR_INTERNAL_ERROR;
884 }
885 }
886 else
887 {
888 if (!drive_poll_run(drive, irp))
889 return ERROR_INTERNAL_ERROR;
890 }
891
892 return CHANNEL_RC_OK;
893}
894
895static UINT drive_free_int(DRIVE_DEVICE* drive)
896{
897 UINT error = CHANNEL_RC_OK;
898
899 if (!drive)
900 return ERROR_INVALID_PARAMETER;
901
902 (void)CloseHandle(drive->thread);
903 ListDictionary_Free(drive->files);
904 MessageQueue_Free(drive->IrpQueue);
905 Stream_Free(drive->device.data, TRUE);
906 free(drive->path);
907 free(drive);
908 return error;
909}
910
916static UINT drive_free(DEVICE* device)
917{
918 DRIVE_DEVICE* drive = (DRIVE_DEVICE*)device;
919 UINT error = CHANNEL_RC_OK;
920
921 if (!drive)
922 return ERROR_INVALID_PARAMETER;
923
924 if (MessageQueue_PostQuit(drive->IrpQueue, 0) &&
925 (WaitForSingleObject(drive->thread, INFINITE) == WAIT_FAILED))
926 {
927 error = GetLastError();
928 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
929 return error;
930 }
931
932 return drive_free_int(drive);
933}
934
938static void drive_file_objfree(void* obj)
939{
940 drive_file_free((DRIVE_FILE*)obj);
941}
942
943static void drive_message_free(void* obj)
944{
945 wMessage* msg = obj;
946 if (!msg)
947 return;
948 if (msg->id != 0)
949 return;
950
951 IRP* irp = (IRP*)msg->wParam;
952 if (!irp)
953 return;
954 WINPR_ASSERT(irp->Discard);
955 irp->Discard(irp);
956}
957
963static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, const char* name,
964 const char* path, BOOL automount, uint32_t* pid)
965{
966 WINPR_ASSERT(pid);
967
968 size_t length = 0;
969 DRIVE_DEVICE* drive = nullptr;
970 UINT error = ERROR_INTERNAL_ERROR;
971
972 if (!pEntryPoints || !name || !path)
973 {
974 WLog_ERR(TAG, "Invalid parameters: pEntryPoints=%p, name=%p, path=%p",
975 WINPR_CXX_COMPAT_CAST(const void*, pEntryPoints),
976 WINPR_CXX_COMPAT_CAST(const void*, name),
977 WINPR_CXX_COMPAT_CAST(const void*, path));
978 return ERROR_INVALID_PARAMETER;
979 }
980
981 if (name[0] && path[0])
982 {
983 size_t pathLength = strnlen(path, MAX_PATH);
984 drive = (DRIVE_DEVICE*)calloc(1, sizeof(DRIVE_DEVICE));
985
986 if (!drive)
987 {
988 WLog_ERR(TAG, "calloc failed!");
989 return CHANNEL_RC_NO_MEMORY;
990 }
991
992 drive->device.type = RDPDR_DTYP_FILESYSTEM;
993 drive->device.IRPRequest = drive_irp_request;
994 drive->device.Free = drive_free;
995 drive->rdpcontext = pEntryPoints->rdpcontext;
996 drive->automount = automount;
997 length = strlen(name);
998 drive->device.data = Stream_New(nullptr, length + 1);
999
1000 if (!drive->device.data)
1001 {
1002 WLog_ERR(TAG, "Stream_New failed!");
1003 error = CHANNEL_RC_NO_MEMORY;
1004 goto out_error;
1005 }
1006
1007 for (size_t i = 0; i < length; i++)
1008 {
1009 /* Filter 2.2.1.3 Device Announce Header (DEVICE_ANNOUNCE) forbidden symbols */
1010 switch (name[i])
1011 {
1012 case ':':
1013 case '<':
1014 case '>':
1015 case '\"':
1016 case '/':
1017 case '\\':
1018 case '|':
1019 case ' ':
1020 Stream_Write_UINT8(drive->device.data, '_');
1021 break;
1022 default:
1023 Stream_Write_UINT8(drive->device.data, (BYTE)name[i]);
1024 break;
1025 }
1026 }
1027 Stream_Write_UINT8(drive->device.data, '\0');
1028
1029 drive->device.name = Stream_BufferAs(drive->device.data, char);
1030 if (!drive->device.name)
1031 goto out_error;
1032
1033 if ((pathLength > 1) && (path[pathLength - 1] == '/'))
1034 pathLength--;
1035
1036 drive->path = ConvertUtf8NToWCharAlloc(path, pathLength, nullptr);
1037 if (!drive->path)
1038 {
1039 error = CHANNEL_RC_NO_MEMORY;
1040 goto out_error;
1041 }
1042
1043 drive->files = ListDictionary_New(TRUE);
1044
1045 if (!drive->files)
1046 {
1047 WLog_ERR(TAG, "ListDictionary_New failed!");
1048 error = CHANNEL_RC_NO_MEMORY;
1049 goto out_error;
1050 }
1051
1052 ListDictionary_ValueObject(drive->files)->fnObjectFree = drive_file_objfree;
1053 drive->IrpQueue = MessageQueue_New(nullptr);
1054
1055 if (!drive->IrpQueue)
1056 {
1057 WLog_ERR(TAG, "ListDictionary_New failed!");
1058 error = CHANNEL_RC_NO_MEMORY;
1059 goto out_error;
1060 }
1061
1062 wObject* obj = MessageQueue_Object(drive->IrpQueue);
1063 WINPR_ASSERT(obj);
1064 obj->fnObjectFree = drive_message_free;
1065
1066 if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &drive->device)))
1067 {
1068 WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error);
1069 goto out_error;
1070 }
1071 *pid = drive->device.id;
1072
1073 drive->async = !freerdp_settings_get_bool(drive->rdpcontext->settings,
1074 FreeRDP_SynchronousStaticChannels);
1075 if (drive->async)
1076 {
1077 if (!(drive->thread = CreateThread(nullptr, 0, drive_thread_func, drive,
1078 CREATE_SUSPENDED, nullptr)))
1079 {
1080 WLog_ERR(TAG, "CreateThread failed!");
1081 goto out_error;
1082 }
1083
1084 ResumeThread(drive->thread);
1085 }
1086 }
1087
1088 return CHANNEL_RC_OK;
1089out_error:
1090 drive_free_int(drive);
1091 return error;
1092}
1093
1094static BOOL drive_filtered(const WCHAR* drive)
1095{
1096 const WCHAR a[] = { 'A', '\0' };
1097 const WCHAR b[] = { 'B', '\0' };
1098 const WCHAR la[] = { 'a', '\0' };
1099 const WCHAR lb[] = { 'b', '\0' };
1100 const WCHAR* list[] = { a, b, la, lb };
1101
1102 for (size_t x = 0; x < ARRAYSIZE(list); x++)
1103 {
1104 const WCHAR* cur = list[x];
1105 if (_wcsncmp(drive, cur, 2) == 0)
1106 return TRUE;
1107 }
1108 return FALSE;
1109}
1110
1111static UINT handle_all_drives(RDPDR_DRIVE* drive, PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
1112{
1113 UINT error = ERROR_INTERNAL_ERROR;
1114
1115 WINPR_ASSERT(drive);
1116 WINPR_ASSERT(pEntryPoints);
1117
1118 /* Enumerate all devices: */
1119 const DWORD dlen = GetLogicalDriveStringsW(0, nullptr);
1120
1121 WCHAR* devlist = calloc(dlen, sizeof(WCHAR));
1122 if (!devlist)
1123 return ERROR_OUTOFMEMORY;
1124
1125 const DWORD rc = GetLogicalDriveStringsW(dlen, devlist);
1126 if (rc >= dlen)
1127 goto fail;
1128
1129 for (size_t offset = 0, len = 0; offset < rc; offset += len + 1)
1130 {
1131 len = _wcsnlen(&devlist[offset], rc - offset);
1132
1133 const WCHAR* dev = &devlist[offset];
1134 if (!drive_filtered(dev))
1135 {
1136 char* bufdup = nullptr;
1137 char* devdup = ConvertWCharNToUtf8Alloc(dev, len, nullptr);
1138 if (!devdup)
1139 {
1140 error = ERROR_OUTOFMEMORY;
1141 goto fail;
1142 }
1143 size_t size = 0;
1144 winpr_asprintf(&bufdup, &size, "%s_%s", drive->device.Name, devdup);
1145
1146 error =
1147 drive_register_drive_path(pEntryPoints, bufdup, devdup, TRUE, &drive->device.Id);
1148 free(devdup);
1149 free(bufdup);
1150 if (error != CHANNEL_RC_OK)
1151 goto fail;
1152 }
1153 }
1154
1155 error = CHANNEL_RC_OK;
1156
1157fail:
1158 free(devlist);
1159
1160 return error;
1161}
1162
1168FREERDP_ENTRY_POINT(
1169 UINT VCAPITYPE drive_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
1170{
1171 UINT error = 0;
1172
1173 WINPR_ASSERT(pEntryPoints);
1174
1175 RDPDR_DRIVE* drive = (RDPDR_DRIVE*)pEntryPoints->device;
1176 WINPR_ASSERT(drive);
1177
1178 const char all[] = "*";
1179 const char home[] = "%";
1180 if (strncmp(drive->Path, all, sizeof(all)) == 0)
1181 {
1182 error = handle_all_drives(drive, pEntryPoints);
1183 }
1184 else if (strncmp(drive->Path, home, sizeof(home)) == 0)
1185 {
1186 free(drive->Path);
1187 drive->Path = GetKnownPath(KNOWN_PATH_HOME);
1188
1189 if (!drive->Path)
1190 {
1191 WLog_ERR(TAG, "_strdup failed!");
1192 return CHANNEL_RC_NO_MEMORY;
1193 }
1194 error = drive_register_drive_path(pEntryPoints, drive->device.Name, drive->Path,
1195 drive->automount, &drive->device.Id);
1196 }
1197 else
1198 {
1199 error = drive_register_drive_path(pEntryPoints, drive->device.Name, drive->Path,
1200 drive->automount, &drive->device.Id);
1201 }
1202 return error;
1203}
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