Coverage Report

Created: 2026-04-12 07:03

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