Coverage Report

Created: 2025-07-01 06:46

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