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