Coverage Report

Created: 2026-02-26 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/channels/drive/client/drive_main.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * File System Virtual Channel
4
 *
5
 * Copyright 2010-2011 Vic Lee
6
 * Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
7
 * Copyright 2015 Thincast Technologies GmbH
8
 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
9
 * Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
10
 *
11
 * Licensed under the Apache License, Version 2.0 (the "License");
12
 * you may not use this file except in compliance with the License.
13
 * You may obtain a copy of the License at
14
 *
15
 *     http://www.apache.org/licenses/LICENSE-2.0
16
 *
17
 * Unless required by applicable law or agreed to in writing, software
18
 * distributed under the License is distributed on an "AS IS" BASIS,
19
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
 * See the License for the specific language governing permissions and
21
 * limitations under the License.
22
 */
23
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
51
typedef 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
69
static NTSTATUS drive_map_windows_err(DWORD fs_errno)
70
0
{
71
0
  NTSTATUS rc = 0;
72
73
  /* try to return NTSTATUS version of error code */
74
75
0
  switch (fs_errno)
76
0
  {
77
0
    case STATUS_SUCCESS:
78
0
      rc = STATUS_SUCCESS;
79
0
      break;
80
81
0
    case ERROR_ACCESS_DENIED:
82
0
    case ERROR_SHARING_VIOLATION:
83
0
      rc = STATUS_ACCESS_DENIED;
84
0
      break;
85
86
0
    case ERROR_FILE_NOT_FOUND:
87
0
      rc = STATUS_NO_SUCH_FILE;
88
0
      break;
89
90
0
    case ERROR_BUSY_DRIVE:
91
0
      rc = STATUS_DEVICE_BUSY;
92
0
      break;
93
94
0
    case ERROR_INVALID_DRIVE:
95
0
      rc = STATUS_NO_SUCH_DEVICE;
96
0
      break;
97
98
0
    case ERROR_NOT_READY:
99
0
      rc = STATUS_NO_SUCH_DEVICE;
100
0
      break;
101
102
0
    case ERROR_FILE_EXISTS:
103
0
    case ERROR_ALREADY_EXISTS:
104
0
      rc = STATUS_OBJECT_NAME_COLLISION;
105
0
      break;
106
107
0
    case ERROR_INVALID_NAME:
108
0
      rc = STATUS_NO_SUCH_FILE;
109
0
      break;
110
111
0
    case ERROR_INVALID_HANDLE:
112
0
      rc = STATUS_INVALID_HANDLE;
113
0
      break;
114
115
0
    case ERROR_NO_MORE_FILES:
116
0
      rc = STATUS_NO_MORE_FILES;
117
0
      break;
118
119
0
    case ERROR_DIRECTORY:
120
0
      rc = STATUS_NOT_A_DIRECTORY;
121
0
      break;
122
123
0
    case ERROR_PATH_NOT_FOUND:
124
0
      rc = STATUS_OBJECT_PATH_NOT_FOUND;
125
0
      break;
126
127
0
    case ERROR_DIR_NOT_EMPTY:
128
0
      rc = STATUS_DIRECTORY_NOT_EMPTY;
129
0
      break;
130
131
0
    default:
132
0
      rc = STATUS_UNSUCCESSFUL;
133
0
      WLog_ERR(TAG, "Error code not found: %" PRIu32 "", fs_errno);
134
0
      break;
135
0
  }
136
137
0
  return rc;
138
0
}
139
140
static DRIVE_FILE* drive_get_file_by_id(DRIVE_DEVICE* drive, UINT32 id)
141
0
{
142
0
  DRIVE_FILE* file = NULL;
143
0
  void* key = (void*)(size_t)id;
144
145
0
  if (!drive)
146
0
    return NULL;
147
148
0
  file = (DRIVE_FILE*)ListDictionary_GetItemValue(drive->files, key);
149
0
  return file;
150
0
}
151
152
/**
153
 * Function description
154
 *
155
 * @return 0 on success, otherwise a Win32 error code
156
 */
157
static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp)
158
0
{
159
0
  BYTE Information = 0;
160
161
0
  WINPR_ASSERT(drive);
162
0
  WINPR_ASSERT(irp);
163
0
  if (!irp->devman)
164
0
    return ERROR_INVALID_PARAMETER;
165
166
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 6 * 4 + 8))
167
0
    return ERROR_INVALID_DATA;
168
169
0
  const uint32_t DesiredAccess = Stream_Get_UINT32(irp->input);
170
0
  const uint64_t allocationSize = Stream_Get_UINT64(irp->input);
171
0
  const uint32_t FileAttributes = Stream_Get_UINT32(irp->input);
172
0
  const uint32_t SharedAccess = Stream_Get_UINT32(irp->input);
173
0
  const uint32_t CreateDisposition = Stream_Get_UINT32(irp->input);
174
0
  const uint32_t CreateOptions = Stream_Get_UINT32(irp->input);
175
0
  const uint32_t PathLength = Stream_Get_UINT32(irp->input);
176
177
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, PathLength))
178
0
    return ERROR_INVALID_DATA;
179
180
0
  const WCHAR* path = Stream_ConstPointer(irp->input);
181
0
  UINT32 FileId = irp->devman->id_sequence++;
182
0
  DRIVE_FILE* file =
183
0
      drive_file_new(drive->path, path, PathLength / sizeof(WCHAR), FileId, DesiredAccess,
184
0
                     CreateDisposition, CreateOptions, FileAttributes, SharedAccess);
185
186
0
  if (!file)
187
0
  {
188
0
    irp->IoStatus = drive_map_windows_err(GetLastError());
189
0
    FileId = 0;
190
0
    Information = 0;
191
0
  }
192
0
  else
193
0
  {
194
0
    void* key = (void*)(size_t)file->id;
195
196
0
    if (!ListDictionary_Add(drive->files, key, file))
197
0
    {
198
0
      WLog_ERR(TAG, "ListDictionary_Add failed!");
199
0
      return ERROR_INTERNAL_ERROR;
200
0
    }
201
202
0
    switch (CreateDisposition)
203
0
    {
204
0
      case FILE_SUPERSEDE:
205
0
      case FILE_OPEN:
206
0
      case FILE_CREATE:
207
0
      case FILE_OVERWRITE:
208
0
        Information = FILE_SUPERSEDED;
209
0
        break;
210
211
0
      case FILE_OPEN_IF:
212
0
        Information = FILE_OPENED;
213
0
        break;
214
215
0
      case FILE_OVERWRITE_IF:
216
0
        Information = FILE_OVERWRITTEN;
217
0
        break;
218
219
0
      default:
220
0
        Information = 0;
221
0
        break;
222
0
    }
223
224
0
    if (allocationSize > 0)
225
0
    {
226
0
      const BYTE buffer[] = { '\0' };
227
0
      if (!drive_file_seek(file, allocationSize - sizeof(buffer)))
228
0
        return ERROR_INTERNAL_ERROR;
229
0
      if (!drive_file_write(file, buffer, sizeof(buffer)))
230
0
        return ERROR_INTERNAL_ERROR;
231
0
    }
232
0
  }
233
234
0
  Stream_Write_UINT32(irp->output, FileId);
235
0
  Stream_Write_UINT8(irp->output, Information);
236
237
0
  return CHANNEL_RC_OK;
238
0
}
239
240
/**
241
 * Function description
242
 *
243
 * @return 0 on success, otherwise a Win32 error code
244
 */
245
static UINT drive_process_irp_close(DRIVE_DEVICE* drive, IRP* irp)
246
0
{
247
0
  WINPR_ASSERT(drive);
248
0
  WINPR_ASSERT(irp);
249
0
  if (!irp->output)
250
0
    return ERROR_INVALID_PARAMETER;
251
252
0
  DRIVE_FILE* file = drive_get_file_by_id(drive, irp->FileId);
253
0
  void* key = (void*)(size_t)irp->FileId;
254
255
0
  if (!file)
256
0
    irp->IoStatus = STATUS_UNSUCCESSFUL;
257
0
  else
258
0
  {
259
0
    ListDictionary_Take(drive->files, key);
260
261
0
    if (drive_file_free(file))
262
0
      irp->IoStatus = STATUS_SUCCESS;
263
0
    else
264
0
      irp->IoStatus = drive_map_windows_err(GetLastError());
265
0
  }
266
267
0
  Stream_Zero(irp->output, 5); /* Padding(5) */
268
269
0
  return CHANNEL_RC_OK;
270
0
}
271
272
/**
273
 * Function description
274
 *
275
 * @return 0 on success, otherwise a Win32 error code
276
 */
277
static UINT drive_process_irp_read(DRIVE_DEVICE* drive, IRP* irp)
278
0
{
279
0
  DRIVE_FILE* file = NULL;
280
0
  UINT32 Length = 0;
281
0
  UINT64 Offset = 0;
282
283
0
  WINPR_ASSERT(drive);
284
0
  WINPR_ASSERT(irp);
285
0
  if (!irp->output)
286
0
    return ERROR_INVALID_PARAMETER;
287
288
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12))
289
0
    return ERROR_INVALID_DATA;
290
291
0
  Stream_Read_UINT32(irp->input, Length);
292
0
  Stream_Read_UINT64(irp->input, Offset);
293
0
  file = drive_get_file_by_id(drive, irp->FileId);
294
295
0
  if (!file)
296
0
  {
297
0
    irp->IoStatus = STATUS_UNSUCCESSFUL;
298
0
    Length = 0;
299
0
  }
300
0
  else if (!drive_file_seek(file, Offset))
301
0
  {
302
0
    irp->IoStatus = drive_map_windows_err(GetLastError());
303
0
    Length = 0;
304
0
  }
305
306
0
  if (!Stream_EnsureRemainingCapacity(irp->output, 4ull + Length))
307
0
  {
308
0
    WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
309
0
    return ERROR_INTERNAL_ERROR;
310
0
  }
311
0
  else if (Length == 0)
312
0
    Stream_Write_UINT32(irp->output, 0);
313
0
  else
314
0
  {
315
0
    BYTE* buffer = Stream_PointerAs(irp->output, BYTE) + sizeof(UINT32);
316
317
0
    if (!drive_file_read(file, buffer, &Length))
318
0
    {
319
0
      irp->IoStatus = drive_map_windows_err(GetLastError());
320
0
      Stream_Write_UINT32(irp->output, 0);
321
0
    }
322
0
    else
323
0
    {
324
0
      Stream_Write_UINT32(irp->output, Length);
325
0
      Stream_Seek(irp->output, Length);
326
0
    }
327
0
  }
328
329
0
  return CHANNEL_RC_OK;
330
0
}
331
332
/**
333
 * Function description
334
 *
335
 * @return 0 on success, otherwise a Win32 error code
336
 */
337
static UINT drive_process_irp_write(DRIVE_DEVICE* drive, IRP* irp)
338
0
{
339
0
  DRIVE_FILE* file = NULL;
340
0
  UINT32 Length = 0;
341
0
  UINT64 Offset = 0;
342
343
0
  WINPR_ASSERT(drive);
344
0
  WINPR_ASSERT(irp);
345
0
  if (!irp->input || !irp->output)
346
0
    return ERROR_INVALID_PARAMETER;
347
348
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
349
0
    return ERROR_INVALID_DATA;
350
351
0
  Stream_Read_UINT32(irp->input, Length);
352
0
  Stream_Read_UINT64(irp->input, Offset);
353
0
  Stream_Seek(irp->input, 20); /* Padding */
354
0
  const void* ptr = Stream_ConstPointer(irp->input);
355
0
  if (!Stream_SafeSeek(irp->input, Length))
356
0
    return ERROR_INVALID_DATA;
357
0
  file = drive_get_file_by_id(drive, irp->FileId);
358
359
0
  if (!file)
360
0
  {
361
0
    irp->IoStatus = STATUS_UNSUCCESSFUL;
362
0
    Length = 0;
363
0
  }
364
0
  else if (!drive_file_seek(file, Offset))
365
0
  {
366
0
    irp->IoStatus = drive_map_windows_err(GetLastError());
367
0
    Length = 0;
368
0
  }
369
0
  else if (!drive_file_write(file, ptr, Length))
370
0
  {
371
0
    irp->IoStatus = drive_map_windows_err(GetLastError());
372
0
    Length = 0;
373
0
  }
374
375
0
  Stream_Write_UINT32(irp->output, Length);
376
0
  Stream_Write_UINT8(irp->output, 0); /* Padding */
377
378
0
  return CHANNEL_RC_OK;
379
0
}
380
381
/**
382
 * Function description
383
 *
384
 * @return 0 on success, otherwise a Win32 error code
385
 */
386
static UINT drive_process_irp_query_information(DRIVE_DEVICE* drive, IRP* irp)
387
0
{
388
0
  DRIVE_FILE* file = NULL;
389
0
  UINT32 FsInformationClass = 0;
390
391
0
  WINPR_ASSERT(drive);
392
0
  WINPR_ASSERT(irp);
393
394
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
395
0
    return ERROR_INVALID_DATA;
396
397
0
  Stream_Read_UINT32(irp->input, FsInformationClass);
398
0
  file = drive_get_file_by_id(drive, irp->FileId);
399
400
0
  if (!file)
401
0
  {
402
0
    irp->IoStatus = STATUS_UNSUCCESSFUL;
403
0
  }
404
0
  else if (!drive_file_query_information(file, FsInformationClass, irp->output))
405
0
  {
406
0
    irp->IoStatus = drive_map_windows_err(GetLastError());
407
0
  }
408
409
0
  return CHANNEL_RC_OK;
410
0
}
411
412
/**
413
 * Function description
414
 *
415
 * @return 0 on success, otherwise a Win32 error code
416
 */
417
static UINT drive_process_irp_set_information(DRIVE_DEVICE* drive, IRP* irp)
418
0
{
419
0
  DRIVE_FILE* file = NULL;
420
0
  UINT32 FsInformationClass = 0;
421
0
  UINT32 Length = 0;
422
423
0
  WINPR_ASSERT(drive);
424
0
  WINPR_ASSERT(irp);
425
0
  if (!irp->input || !irp->output)
426
0
    return ERROR_INVALID_PARAMETER;
427
428
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
429
0
    return ERROR_INVALID_DATA;
430
431
0
  Stream_Read_UINT32(irp->input, FsInformationClass);
432
0
  Stream_Read_UINT32(irp->input, Length);
433
0
  Stream_Seek(irp->input, 24); /* Padding */
434
0
  file = drive_get_file_by_id(drive, irp->FileId);
435
436
0
  if (!file)
437
0
  {
438
0
    irp->IoStatus = STATUS_UNSUCCESSFUL;
439
0
  }
440
0
  else if (!drive_file_set_information(file, FsInformationClass, Length, irp->input))
441
0
  {
442
0
    irp->IoStatus = drive_map_windows_err(GetLastError());
443
0
  }
444
445
0
  Stream_Write_UINT32(irp->output, Length);
446
447
0
  return CHANNEL_RC_OK;
448
0
}
449
450
/**
451
 * Function description
452
 *
453
 * @return 0 on success, otherwise a Win32 error code
454
 */
455
static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive, IRP* irp)
456
0
{
457
0
  UINT32 FsInformationClass = 0;
458
0
  DWORD lpSectorsPerCluster = 0;
459
0
  DWORD lpBytesPerSector = 0;
460
0
  DWORD lpNumberOfFreeClusters = 0;
461
0
  DWORD lpTotalNumberOfClusters = 0;
462
0
  WIN32_FILE_ATTRIBUTE_DATA wfad = WINPR_C_ARRAY_INIT;
463
464
0
  WINPR_ASSERT(drive);
465
0
  WINPR_ASSERT(irp);
466
467
0
  wStream* output = irp->output;
468
469
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
470
0
    return ERROR_INVALID_DATA;
471
472
0
  Stream_Read_UINT32(irp->input, FsInformationClass);
473
0
  GetDiskFreeSpaceW(drive->path, &lpSectorsPerCluster, &lpBytesPerSector, &lpNumberOfFreeClusters,
474
0
                    &lpTotalNumberOfClusters);
475
476
0
  switch (FsInformationClass)
477
0
  {
478
0
    case FileFsVolumeInformation:
479
0
    {
480
      /* http://msdn.microsoft.com/en-us/library/cc232108.aspx */
481
0
      const WCHAR* volumeLabel = freerdp_getApplicationDetailsStringW();
482
0
      const size_t volumeLabelLen = (_wcslen(volumeLabel) + 1) * sizeof(WCHAR);
483
0
      const size_t length = 17ul + volumeLabelLen;
484
485
0
      if ((length > UINT32_MAX) || (volumeLabelLen > UINT32_MAX))
486
0
        return CHANNEL_RC_NO_BUFFER;
487
488
0
      Stream_Write_UINT32(output, (UINT32)length); /* Length */
489
490
0
      if (!Stream_EnsureRemainingCapacity(output, length))
491
0
      {
492
0
        WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
493
0
        return CHANNEL_RC_NO_MEMORY;
494
0
      }
495
496
0
      GetFileAttributesExW(drive->path, GetFileExInfoStandard, &wfad);
497
0
      Stream_Write_UINT32(output, wfad.ftCreationTime.dwLowDateTime); /* VolumeCreationTime */
498
0
      Stream_Write_UINT32(output,
499
0
                          wfad.ftCreationTime.dwHighDateTime);      /* VolumeCreationTime */
500
0
      Stream_Write_UINT32(output, lpNumberOfFreeClusters & 0xffff); /* VolumeSerialNumber */
501
0
      Stream_Write_UINT32(output, (UINT32)volumeLabelLen);          /* VolumeLabelLength */
502
0
      Stream_Write_UINT8(output, 0);                                /* SupportsObjects */
503
      /* Reserved(1), MUST NOT be added! */
504
0
      Stream_Write(output, volumeLabel, volumeLabelLen); /* VolumeLabel (Unicode) */
505
0
    }
506
0
    break;
507
508
0
    case FileFsSizeInformation:
509
      /* http://msdn.microsoft.com/en-us/library/cc232107.aspx */
510
0
      Stream_Write_UINT32(output, 24); /* Length */
511
512
0
      if (!Stream_EnsureRemainingCapacity(output, 24))
513
0
      {
514
0
        WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
515
0
        return CHANNEL_RC_NO_MEMORY;
516
0
      }
517
518
0
      Stream_Write_UINT64(output, lpTotalNumberOfClusters); /* TotalAllocationUnits */
519
0
      Stream_Write_UINT64(output, lpNumberOfFreeClusters);  /* AvailableAllocationUnits */
520
0
      Stream_Write_UINT32(output, lpSectorsPerCluster);     /* SectorsPerAllocationUnit */
521
0
      Stream_Write_UINT32(output, lpBytesPerSector);        /* BytesPerSector */
522
0
      break;
523
524
0
    case FileFsAttributeInformation:
525
0
    {
526
0
      WCHAR LabelBuffer[32] = WINPR_C_ARRAY_INIT;
527
      /* http://msdn.microsoft.com/en-us/library/cc232101.aspx */
528
0
      const WCHAR* diskType =
529
0
          InitializeConstWCharFromUtf8("FAT32", LabelBuffer, ARRAYSIZE(LabelBuffer));
530
0
      const size_t diskTypeLen = (_wcslen(diskType) + 1) * sizeof(WCHAR);
531
0
      const size_t length = 12ul + diskTypeLen;
532
533
0
      if ((length > UINT32_MAX) || (diskTypeLen > UINT32_MAX))
534
0
        return CHANNEL_RC_NO_BUFFER;
535
536
0
      Stream_Write_UINT32(output, (UINT32)length); /* Length */
537
538
0
      if (!Stream_EnsureRemainingCapacity(output, length))
539
0
      {
540
0
        WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
541
0
        return CHANNEL_RC_NO_MEMORY;
542
0
      }
543
544
0
      Stream_Write_UINT32(output, FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES |
545
0
                                      FILE_UNICODE_ON_DISK); /* FileSystemAttributes */
546
0
      Stream_Write_UINT32(output, MAX_PATH);                 /* MaximumComponentNameLength */
547
0
      Stream_Write_UINT32(output, (UINT32)diskTypeLen);      /* FileSystemNameLength */
548
0
      Stream_Write(output, diskType, diskTypeLen);           /* FileSystemName (Unicode) */
549
0
    }
550
0
    break;
551
552
0
    case FileFsFullSizeInformation:
553
      /* http://msdn.microsoft.com/en-us/library/cc232104.aspx */
554
0
      Stream_Write_UINT32(output, 32); /* Length */
555
556
0
      if (!Stream_EnsureRemainingCapacity(output, 32))
557
0
      {
558
0
        WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
559
0
        return CHANNEL_RC_NO_MEMORY;
560
0
      }
561
562
0
      Stream_Write_UINT64(output, lpTotalNumberOfClusters); /* TotalAllocationUnits */
563
0
      Stream_Write_UINT64(output,
564
0
                          lpNumberOfFreeClusters); /* CallerAvailableAllocationUnits */
565
0
      Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */
566
0
      Stream_Write_UINT32(output, lpSectorsPerCluster);    /* SectorsPerAllocationUnit */
567
0
      Stream_Write_UINT32(output, lpBytesPerSector);       /* BytesPerSector */
568
0
      break;
569
570
0
    case FileFsDeviceInformation:
571
      /* http://msdn.microsoft.com/en-us/library/cc232109.aspx */
572
0
      Stream_Write_UINT32(output, 8); /* Length */
573
574
0
      if (!Stream_EnsureRemainingCapacity(output, 8))
575
0
      {
576
0
        WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
577
0
        return CHANNEL_RC_NO_MEMORY;
578
0
      }
579
580
0
      Stream_Write_UINT32(output, FILE_DEVICE_DISK); /* DeviceType */
581
0
      Stream_Write_UINT32(output, 0);                /* Characteristics */
582
0
      break;
583
584
0
    default:
585
0
      WLog_WARN(TAG, "Unhandled FSInformationClass %s [0x%08" PRIx32 "]",
586
0
                FSInformationClass2Tag(FsInformationClass), FsInformationClass);
587
0
      irp->IoStatus = STATUS_UNSUCCESSFUL;
588
0
      Stream_Write_UINT32(output, 0); /* Length */
589
0
      break;
590
0
  }
591
592
0
  return CHANNEL_RC_OK;
593
0
}
594
595
/* http://msdn.microsoft.com/en-us/library/cc241518.aspx */
596
597
/**
598
 * Function description
599
 *
600
 * @return 0 on success, otherwise a Win32 error code
601
 */
602
static UINT drive_process_irp_silent_ignore(WINPR_ATTR_UNUSED DRIVE_DEVICE* drive, IRP* irp)
603
0
{
604
0
  WINPR_ASSERT(drive);
605
0
  WINPR_ASSERT(irp);
606
0
  if (!irp->output)
607
0
    return ERROR_INVALID_PARAMETER;
608
609
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
610
0
    return ERROR_INVALID_DATA;
611
612
0
  const uint32_t FsInformationClass = Stream_Get_UINT32(irp->input);
613
0
  WLog_VRB(TAG, "Silently ignore FSInformationClass %s [0x%08" PRIx32 "]",
614
0
           FSInformationClass2Tag(FsInformationClass), FsInformationClass);
615
0
  Stream_Write_UINT32(irp->output, 0); /* Length */
616
0
  return CHANNEL_RC_OK;
617
0
}
618
619
/**
620
 * Function description
621
 *
622
 * @return 0 on success, otherwise a Win32 error code
623
 */
624
static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp)
625
0
{
626
0
  const WCHAR* path = NULL;
627
0
  DRIVE_FILE* file = NULL;
628
0
  BYTE InitialQuery = 0;
629
0
  UINT32 PathLength = 0;
630
0
  UINT32 FsInformationClass = 0;
631
632
0
  WINPR_ASSERT(drive);
633
0
  WINPR_ASSERT(irp);
634
635
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
636
0
    return ERROR_INVALID_DATA;
637
638
0
  Stream_Read_UINT32(irp->input, FsInformationClass);
639
0
  Stream_Read_UINT8(irp->input, InitialQuery);
640
0
  Stream_Read_UINT32(irp->input, PathLength);
641
0
  Stream_Seek(irp->input, 23); /* Padding */
642
0
  path = Stream_ConstPointer(irp->input);
643
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, PathLength))
644
0
    return ERROR_INVALID_DATA;
645
646
0
  file = drive_get_file_by_id(drive, irp->FileId);
647
648
0
  if (file == NULL)
649
0
  {
650
0
    irp->IoStatus = STATUS_UNSUCCESSFUL;
651
0
    Stream_Write_UINT32(irp->output, 0); /* Length */
652
0
  }
653
0
  else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path,
654
0
                                       PathLength / sizeof(WCHAR), irp->output))
655
0
  {
656
0
    irp->IoStatus = drive_map_windows_err(GetLastError());
657
0
  }
658
659
0
  return CHANNEL_RC_OK;
660
0
}
661
662
/**
663
 * Function description
664
 *
665
 * @return 0 on success, otherwise a Win32 error code
666
 */
667
static UINT drive_process_irp_directory_control(DRIVE_DEVICE* drive, IRP* irp)
668
0
{
669
0
  WINPR_ASSERT(drive);
670
0
  WINPR_ASSERT(irp);
671
672
0
  switch (irp->MinorFunction)
673
0
  {
674
0
    case IRP_MN_QUERY_DIRECTORY:
675
0
      return drive_process_irp_query_directory(drive, irp);
676
677
0
    case IRP_MN_NOTIFY_CHANGE_DIRECTORY: /* TODO */
678
0
      irp->IoStatus = STATUS_NOT_SUPPORTED;
679
0
      Stream_Write_UINT32(irp->output, 0); /* Length */
680
0
      break;
681
682
0
    default:
683
0
      irp->IoStatus = STATUS_NOT_SUPPORTED;
684
0
      Stream_Write_UINT32(irp->output, 0); /* Length */
685
0
      break;
686
0
  }
687
688
0
  return CHANNEL_RC_OK;
689
0
}
690
691
/**
692
 * Function description
693
 *
694
 * @return 0 on success, otherwise a Win32 error code
695
 */
696
static UINT drive_process_irp_device_control(WINPR_ATTR_UNUSED DRIVE_DEVICE* drive, IRP* irp)
697
0
{
698
0
  WINPR_ASSERT(drive);
699
0
  WINPR_ASSERT(irp);
700
701
0
  Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
702
0
  return CHANNEL_RC_OK;
703
0
}
704
705
static UINT drive_evaluate(UINT error, IRP* irp)
706
0
{
707
0
  WINPR_ASSERT(irp);
708
0
  if (error == CHANNEL_RC_OK)
709
0
  {
710
0
    WINPR_ASSERT(irp->Complete);
711
0
    return irp->Complete(irp);
712
0
  }
713
714
0
  WLog_ERR(TAG, "IRP %s failed with %" PRIu32, rdpdr_irp_string(irp->MajorFunction), error);
715
0
  WINPR_ASSERT(irp->Discard);
716
0
  irp->Discard(irp);
717
0
  return error;
718
0
}
719
720
/**
721
 * Function description
722
 *
723
 * @return 0 on success, otherwise a Win32 error code
724
 */
725
static UINT drive_process_irp(DRIVE_DEVICE* drive, IRP* irp)
726
0
{
727
0
  UINT error = CHANNEL_RC_OK;
728
0
  WINPR_ASSERT(drive);
729
0
  WINPR_ASSERT(irp);
730
731
0
  irp->IoStatus = STATUS_SUCCESS;
732
733
0
  switch (irp->MajorFunction)
734
0
  {
735
0
    case IRP_MJ_CREATE:
736
0
      error = drive_process_irp_create(drive, irp);
737
0
      break;
738
739
0
    case IRP_MJ_CLOSE:
740
0
      error = drive_process_irp_close(drive, irp);
741
0
      break;
742
743
0
    case IRP_MJ_READ:
744
0
      error = drive_process_irp_read(drive, irp);
745
0
      break;
746
747
0
    case IRP_MJ_WRITE:
748
0
      error = drive_process_irp_write(drive, irp);
749
0
      break;
750
751
0
    case IRP_MJ_QUERY_INFORMATION:
752
0
      error = drive_process_irp_query_information(drive, irp);
753
0
      break;
754
755
0
    case IRP_MJ_SET_INFORMATION:
756
0
      error = drive_process_irp_set_information(drive, irp);
757
0
      break;
758
759
0
    case IRP_MJ_QUERY_VOLUME_INFORMATION:
760
0
      error = drive_process_irp_query_volume_information(drive, irp);
761
0
      break;
762
763
0
    case IRP_MJ_LOCK_CONTROL:
764
0
      error = drive_process_irp_silent_ignore(drive, irp);
765
0
      break;
766
767
0
    case IRP_MJ_DIRECTORY_CONTROL:
768
0
      error = drive_process_irp_directory_control(drive, irp);
769
0
      break;
770
771
0
    case IRP_MJ_DEVICE_CONTROL:
772
0
      error = drive_process_irp_device_control(drive, irp);
773
0
      break;
774
775
0
    default:
776
0
      irp->IoStatus = STATUS_NOT_SUPPORTED;
777
0
      break;
778
0
  }
779
780
0
  return drive_evaluate(error, irp);
781
0
}
782
783
static BOOL drive_poll_run(DRIVE_DEVICE* drive, IRP* irp)
784
0
{
785
0
  WINPR_ASSERT(drive);
786
787
0
  if (irp)
788
0
  {
789
0
    const UINT error = drive_process_irp(drive, irp);
790
0
    if (error)
791
0
    {
792
0
      WLog_ERR(TAG, "drive_process_irp failed with error %" PRIu32 "!", error);
793
0
      return FALSE;
794
0
    }
795
0
  }
796
797
0
  return TRUE;
798
0
}
799
800
static DWORD WINAPI drive_thread_func(LPVOID arg)
801
0
{
802
0
  DRIVE_DEVICE* drive = (DRIVE_DEVICE*)arg;
803
0
  UINT error = CHANNEL_RC_OK;
804
805
0
  if (!drive)
806
0
  {
807
0
    error = ERROR_INVALID_PARAMETER;
808
0
    goto fail;
809
0
  }
810
811
0
  while (1)
812
0
  {
813
0
    if (!MessageQueue_Wait(drive->IrpQueue))
814
0
    {
815
0
      WLog_ERR(TAG, "MessageQueue_Wait failed!");
816
0
      error = ERROR_INTERNAL_ERROR;
817
0
      break;
818
0
    }
819
820
0
    if (MessageQueue_Size(drive->IrpQueue) < 1)
821
0
      continue;
822
823
0
    wMessage message = WINPR_C_ARRAY_INIT;
824
0
    if (!MessageQueue_Peek(drive->IrpQueue, &message, TRUE))
825
0
    {
826
0
      WLog_ERR(TAG, "MessageQueue_Peek failed!");
827
0
      continue;
828
0
    }
829
830
0
    if (message.id == WMQ_QUIT)
831
0
      break;
832
833
0
    IRP* irp = (IRP*)message.wParam;
834
0
    if (!drive_poll_run(drive, irp))
835
0
      break;
836
0
  }
837
838
0
fail:
839
840
0
  if (error && drive && drive->rdpcontext)
841
0
    setChannelError(drive->rdpcontext, error, "drive_thread_func reported an error");
842
843
0
  ExitThread(error);
844
0
  return error;
845
0
}
846
847
/**
848
 * Function description
849
 *
850
 * @return 0 on success, otherwise a Win32 error code
851
 */
852
static UINT drive_irp_request(DEVICE* device, IRP* irp)
853
0
{
854
0
  DRIVE_DEVICE* drive = (DRIVE_DEVICE*)device;
855
856
0
  if (!drive)
857
0
    return ERROR_INVALID_PARAMETER;
858
859
0
  if (drive->async)
860
0
  {
861
0
    if (!MessageQueue_Post(drive->IrpQueue, NULL, 0, (void*)irp, NULL))
862
0
    {
863
0
      WLog_ERR(TAG, "MessageQueue_Post failed!");
864
0
      return ERROR_INTERNAL_ERROR;
865
0
    }
866
0
  }
867
0
  else
868
0
  {
869
0
    if (!drive_poll_run(drive, irp))
870
0
      return ERROR_INTERNAL_ERROR;
871
0
  }
872
873
0
  return CHANNEL_RC_OK;
874
0
}
875
876
static UINT drive_free_int(DRIVE_DEVICE* drive)
877
0
{
878
0
  UINT error = CHANNEL_RC_OK;
879
880
0
  if (!drive)
881
0
    return ERROR_INVALID_PARAMETER;
882
883
0
  (void)CloseHandle(drive->thread);
884
0
  ListDictionary_Free(drive->files);
885
0
  MessageQueue_Free(drive->IrpQueue);
886
0
  Stream_Free(drive->device.data, TRUE);
887
0
  free(drive->path);
888
0
  free(drive);
889
0
  return error;
890
0
}
891
892
/**
893
 * Function description
894
 *
895
 * @return 0 on success, otherwise a Win32 error code
896
 */
897
static UINT drive_free(DEVICE* device)
898
0
{
899
0
  DRIVE_DEVICE* drive = (DRIVE_DEVICE*)device;
900
0
  UINT error = CHANNEL_RC_OK;
901
902
0
  if (!drive)
903
0
    return ERROR_INVALID_PARAMETER;
904
905
0
  if (MessageQueue_PostQuit(drive->IrpQueue, 0) &&
906
0
      (WaitForSingleObject(drive->thread, INFINITE) == WAIT_FAILED))
907
0
  {
908
0
    error = GetLastError();
909
0
    WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
910
0
    return error;
911
0
  }
912
913
0
  return drive_free_int(drive);
914
0
}
915
916
/**
917
 * Helper function used for freeing list dictionary value object
918
 */
919
static void drive_file_objfree(void* obj)
920
0
{
921
0
  drive_file_free((DRIVE_FILE*)obj);
922
0
}
923
924
static void drive_message_free(void* obj)
925
0
{
926
0
  wMessage* msg = obj;
927
0
  if (!msg)
928
0
    return;
929
0
  if (msg->id != 0)
930
0
    return;
931
932
0
  IRP* irp = (IRP*)msg->wParam;
933
0
  if (!irp)
934
0
    return;
935
0
  WINPR_ASSERT(irp->Discard);
936
0
  irp->Discard(irp);
937
0
}
938
939
/**
940
 * Function description
941
 *
942
 * @return 0 on success, otherwise a Win32 error code
943
 */
944
static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, const char* name,
945
                                      const char* path, BOOL automount, uint32_t* pid)
946
0
{
947
0
  WINPR_ASSERT(pid);
948
949
0
  size_t length = 0;
950
0
  DRIVE_DEVICE* drive = NULL;
951
0
  UINT error = ERROR_INTERNAL_ERROR;
952
953
0
  if (!pEntryPoints || !name || !path)
954
0
  {
955
0
    WLog_ERR(TAG, "Invalid parameters: pEntryPoints=%p, name=%p, path=%p",
956
0
             WINPR_CXX_COMPAT_CAST(const void*, pEntryPoints),
957
0
             WINPR_CXX_COMPAT_CAST(const void*, name),
958
0
             WINPR_CXX_COMPAT_CAST(const void*, path));
959
0
    return ERROR_INVALID_PARAMETER;
960
0
  }
961
962
0
  if (name[0] && path[0])
963
0
  {
964
0
    size_t pathLength = strnlen(path, MAX_PATH);
965
0
    drive = (DRIVE_DEVICE*)calloc(1, sizeof(DRIVE_DEVICE));
966
967
0
    if (!drive)
968
0
    {
969
0
      WLog_ERR(TAG, "calloc failed!");
970
0
      return CHANNEL_RC_NO_MEMORY;
971
0
    }
972
973
0
    drive->device.type = RDPDR_DTYP_FILESYSTEM;
974
0
    drive->device.IRPRequest = drive_irp_request;
975
0
    drive->device.Free = drive_free;
976
0
    drive->rdpcontext = pEntryPoints->rdpcontext;
977
0
    drive->automount = automount;
978
0
    length = strlen(name);
979
0
    drive->device.data = Stream_New(NULL, length + 1);
980
981
0
    if (!drive->device.data)
982
0
    {
983
0
      WLog_ERR(TAG, "Stream_New failed!");
984
0
      error = CHANNEL_RC_NO_MEMORY;
985
0
      goto out_error;
986
0
    }
987
988
0
    for (size_t i = 0; i < length; i++)
989
0
    {
990
      /* Filter 2.2.1.3 Device Announce Header (DEVICE_ANNOUNCE) forbidden symbols */
991
0
      switch (name[i])
992
0
      {
993
0
        case ':':
994
0
        case '<':
995
0
        case '>':
996
0
        case '\"':
997
0
        case '/':
998
0
        case '\\':
999
0
        case '|':
1000
0
        case ' ':
1001
0
          Stream_Write_UINT8(drive->device.data, '_');
1002
0
          break;
1003
0
        default:
1004
0
          Stream_Write_UINT8(drive->device.data, (BYTE)name[i]);
1005
0
          break;
1006
0
      }
1007
0
    }
1008
0
    Stream_Write_UINT8(drive->device.data, '\0');
1009
1010
0
    drive->device.name = Stream_BufferAs(drive->device.data, char);
1011
0
    if (!drive->device.name)
1012
0
      goto out_error;
1013
1014
0
    if ((pathLength > 1) && (path[pathLength - 1] == '/'))
1015
0
      pathLength--;
1016
1017
0
    drive->path = ConvertUtf8NToWCharAlloc(path, pathLength, NULL);
1018
0
    if (!drive->path)
1019
0
    {
1020
0
      error = CHANNEL_RC_NO_MEMORY;
1021
0
      goto out_error;
1022
0
    }
1023
1024
0
    drive->files = ListDictionary_New(TRUE);
1025
1026
0
    if (!drive->files)
1027
0
    {
1028
0
      WLog_ERR(TAG, "ListDictionary_New failed!");
1029
0
      error = CHANNEL_RC_NO_MEMORY;
1030
0
      goto out_error;
1031
0
    }
1032
1033
0
    ListDictionary_ValueObject(drive->files)->fnObjectFree = drive_file_objfree;
1034
0
    drive->IrpQueue = MessageQueue_New(NULL);
1035
1036
0
    if (!drive->IrpQueue)
1037
0
    {
1038
0
      WLog_ERR(TAG, "ListDictionary_New failed!");
1039
0
      error = CHANNEL_RC_NO_MEMORY;
1040
0
      goto out_error;
1041
0
    }
1042
1043
0
    wObject* obj = MessageQueue_Object(drive->IrpQueue);
1044
0
    WINPR_ASSERT(obj);
1045
0
    obj->fnObjectFree = drive_message_free;
1046
1047
0
    if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &drive->device)))
1048
0
    {
1049
0
      WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error);
1050
0
      goto out_error;
1051
0
    }
1052
0
    *pid = drive->device.id;
1053
1054
0
    drive->async = !freerdp_settings_get_bool(drive->rdpcontext->settings,
1055
0
                                              FreeRDP_SynchronousStaticChannels);
1056
0
    if (drive->async)
1057
0
    {
1058
0
      if (!(drive->thread =
1059
0
                CreateThread(NULL, 0, drive_thread_func, drive, CREATE_SUSPENDED, NULL)))
1060
0
      {
1061
0
        WLog_ERR(TAG, "CreateThread failed!");
1062
0
        goto out_error;
1063
0
      }
1064
1065
0
      ResumeThread(drive->thread);
1066
0
    }
1067
0
  }
1068
1069
0
  return CHANNEL_RC_OK;
1070
0
out_error:
1071
0
  drive_free_int(drive);
1072
0
  return error;
1073
0
}
1074
1075
static BOOL drive_filtered(const WCHAR* drive)
1076
0
{
1077
0
  const WCHAR a[] = { 'A', '\0' };
1078
0
  const WCHAR b[] = { 'B', '\0' };
1079
0
  const WCHAR la[] = { 'a', '\0' };
1080
0
  const WCHAR lb[] = { 'b', '\0' };
1081
0
  const WCHAR* list[] = { a, b, la, lb };
1082
1083
0
  for (size_t x = 0; x < ARRAYSIZE(list); x++)
1084
0
  {
1085
0
    const WCHAR* cur = list[x];
1086
0
    if (_wcsncmp(drive, cur, 2) == 0)
1087
0
      return TRUE;
1088
0
  }
1089
0
  return FALSE;
1090
0
}
1091
1092
static UINT handle_all_drives(RDPDR_DRIVE* drive, PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
1093
0
{
1094
0
  UINT error = ERROR_INTERNAL_ERROR;
1095
1096
0
  WINPR_ASSERT(drive);
1097
0
  WINPR_ASSERT(pEntryPoints);
1098
1099
  /* Enumerate all devices: */
1100
0
  const DWORD dlen = GetLogicalDriveStringsW(0, NULL);
1101
1102
0
  WCHAR* devlist = calloc(dlen, sizeof(WCHAR));
1103
0
  if (!devlist)
1104
0
    return ERROR_OUTOFMEMORY;
1105
1106
0
  const DWORD rc = GetLogicalDriveStringsW(dlen, devlist);
1107
0
  if (rc >= dlen)
1108
0
    goto fail;
1109
1110
0
  for (size_t offset = 0, len = 0; offset < rc; offset += len + 1)
1111
0
  {
1112
0
    len = _wcsnlen(&devlist[offset], rc - offset);
1113
1114
0
    const WCHAR* dev = &devlist[offset];
1115
0
    if (!drive_filtered(dev))
1116
0
    {
1117
0
      char* bufdup = NULL;
1118
0
      char* devdup = ConvertWCharNToUtf8Alloc(dev, len, NULL);
1119
0
      if (!devdup)
1120
0
      {
1121
0
        error = ERROR_OUTOFMEMORY;
1122
0
        goto fail;
1123
0
      }
1124
0
      size_t size = 0;
1125
0
      winpr_asprintf(&bufdup, &size, "%s_%s", drive->device.Name, devdup);
1126
1127
0
      error =
1128
0
          drive_register_drive_path(pEntryPoints, bufdup, devdup, TRUE, &drive->device.Id);
1129
0
      free(devdup);
1130
0
      free(bufdup);
1131
0
      if (error != CHANNEL_RC_OK)
1132
0
        goto fail;
1133
0
    }
1134
0
  }
1135
1136
0
  error = CHANNEL_RC_OK;
1137
1138
0
fail:
1139
0
  free(devlist);
1140
1141
0
  return error;
1142
0
}
1143
1144
/**
1145
 * Function description
1146
 *
1147
 * @return 0 on success, otherwise a Win32 error code
1148
 */
1149
FREERDP_ENTRY_POINT(
1150
    UINT VCAPITYPE drive_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
1151
0
{
1152
0
  UINT error = 0;
1153
1154
0
  WINPR_ASSERT(pEntryPoints);
1155
1156
0
  RDPDR_DRIVE* drive = (RDPDR_DRIVE*)pEntryPoints->device;
1157
0
  WINPR_ASSERT(drive);
1158
1159
0
  const char all[] = "*";
1160
0
  const char home[] = "%";
1161
0
  if (strncmp(drive->Path, all, sizeof(all)) == 0)
1162
0
  {
1163
0
    error = handle_all_drives(drive, pEntryPoints);
1164
0
  }
1165
0
  else if (strncmp(drive->Path, home, sizeof(home)) == 0)
1166
0
  {
1167
0
    free(drive->Path);
1168
0
    drive->Path = GetKnownPath(KNOWN_PATH_HOME);
1169
1170
0
    if (!drive->Path)
1171
0
    {
1172
0
      WLog_ERR(TAG, "_strdup failed!");
1173
0
      return CHANNEL_RC_NO_MEMORY;
1174
0
    }
1175
0
    error = drive_register_drive_path(pEntryPoints, drive->device.Name, drive->Path,
1176
0
                                      drive->automount, &drive->device.Id);
1177
0
  }
1178
0
  else
1179
0
  {
1180
0
    error = drive_register_drive_path(pEntryPoints, drive->device.Name, drive->Path,
1181
0
                                      drive->automount, &drive->device.Id);
1182
0
  }
1183
0
  return error;
1184
0
}