Coverage Report

Created: 2026-03-04 06:17

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