Coverage Report

Created: 2024-05-20 06:11

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