Coverage Report

Created: 2026-10-15 06:30

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