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