Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/channels/drive/client/drive_file.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-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2010-2011 Vic Lee
7
 * Copyright 2012 Gerald Richter
8
 * Copyright 2015 Thincast Technologies GmbH
9
 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
10
 * Copyright 2016 Inuvika Inc.
11
 * Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
12
 * Copyright 2017 Armin Novak <armin.novak@thincast.com>
13
 * Copyright 2017 Thincast Technologies GmbH
14
 *
15
 * Licensed under the Apache License, Version 2.0 (the "License");
16
 * you may not use this file except in compliance with the License.
17
 * You may obtain a copy of the License at
18
 *
19
 *     http://www.apache.org/licenses/LICENSE-2.0
20
 *
21
 * Unless required by applicable law or agreed to in writing, software
22
 * distributed under the License is distributed on an "AS IS" BASIS,
23
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24
 * See the License for the specific language governing permissions and
25
 * limitations under the License.
26
 */
27
28
#include <freerdp/config.h>
29
30
#include <errno.h>
31
#include <stdio.h>
32
#include <stdlib.h>
33
#include <string.h>
34
#include <time.h>
35
36
#include <winpr/wtypes.h>
37
#include <winpr/crt.h>
38
#include <winpr/string.h>
39
#include <winpr/path.h>
40
#include <winpr/file.h>
41
#include <winpr/stream.h>
42
43
#include <freerdp/channels/rdpdr.h>
44
45
#include "drive_file.h"
46
47
#ifdef WITH_DEBUG_RDPDR
48
#define DEBUG_WSTR(msg, wstr)                                    \
49
  do                                                           \
50
  {                                                            \
51
    char lpstr[1024] = { 0 };                                \
52
    (void)ConvertWCharToUtf8(wstr, lpstr, ARRAYSIZE(lpstr)); \
53
    WLog_DBG(TAG, msg, lpstr);                               \
54
  } while (0)
55
#else
56
#define DEBUG_WSTR(msg, wstr) \
57
0
  do                        \
58
0
  {                         \
59
0
  } while (0)
60
#endif
61
62
static BOOL drive_file_fix_path(WCHAR* path, size_t length)
63
0
{
64
0
  if ((length == 0) || (length > UINT32_MAX))
65
0
    return FALSE;
66
67
0
  WINPR_ASSERT(path);
68
69
0
  for (size_t i = 0; i < length; i++)
70
0
  {
71
0
    if (path[i] == L'\\')
72
0
      path[i] = L'/';
73
0
  }
74
75
#ifdef WIN32
76
77
  if ((length == 3) && (path[1] == L':') && (path[2] == L'/'))
78
    return FALSE;
79
80
#else
81
82
0
  if ((length == 1) && (path[0] == L'/'))
83
0
    return FALSE;
84
85
0
#endif
86
87
0
  if ((length > 0) && (path[length - 1] == L'/'))
88
0
    path[length - 1] = L'\0';
89
90
0
  return TRUE;
91
0
}
92
93
static BOOL contains_dotdot(const WCHAR* path, size_t base_length, size_t path_length)
94
0
{
95
0
  WCHAR dotdotbuffer[6] = { 0 };
96
0
  const WCHAR* dotdot = InitializeConstWCharFromUtf8("..", dotdotbuffer, ARRAYSIZE(dotdotbuffer));
97
0
  const WCHAR* tst = path;
98
99
0
  if (path_length < 2)
100
0
    return FALSE;
101
102
0
  do
103
0
  {
104
0
    tst = _wcsstr(tst, dotdot);
105
0
    if (!tst)
106
0
      return FALSE;
107
108
    /* Filter .. sequences in file or directory names */
109
0
    if ((base_length == 0) || (*(tst - 1) == L'/') || (*(tst - 1) == L'\\'))
110
0
    {
111
0
      if (tst + 2 < path + path_length)
112
0
      {
113
0
        if ((tst[2] == '/') || (tst[2] == '\\'))
114
0
          return TRUE;
115
0
      }
116
0
    }
117
0
    tst += 2;
118
0
  } while (TRUE);
119
120
0
  return FALSE;
121
0
}
122
123
static WCHAR* drive_file_combine_fullpath(const WCHAR* base_path, const WCHAR* path,
124
                                          size_t PathWCharLength)
125
0
{
126
0
  BOOL ok = FALSE;
127
0
  WCHAR* fullpath = NULL;
128
129
0
  if (!base_path || (!path && (PathWCharLength > 0)))
130
0
    goto fail;
131
132
0
  const size_t base_path_length = _wcsnlen(base_path, MAX_PATH);
133
0
  const size_t length = base_path_length + PathWCharLength + 1;
134
0
  fullpath = (WCHAR*)calloc(length, sizeof(WCHAR));
135
136
0
  if (!fullpath)
137
0
    goto fail;
138
139
0
  CopyMemory(fullpath, base_path, base_path_length * sizeof(WCHAR));
140
0
  if (path)
141
0
    CopyMemory(&fullpath[base_path_length], path, PathWCharLength * sizeof(WCHAR));
142
143
0
  if (!drive_file_fix_path(fullpath, length))
144
0
    goto fail;
145
146
  /* Ensure the path does not contain sequences like '..' */
147
0
  if (contains_dotdot(&fullpath[base_path_length], base_path_length, PathWCharLength))
148
0
  {
149
0
    char abuffer[MAX_PATH] = { 0 };
150
0
    (void)ConvertWCharToUtf8(&fullpath[base_path_length], abuffer, ARRAYSIZE(abuffer));
151
152
0
    WLog_WARN(TAG, "[rdpdr] received invalid file path '%s' from server, aborting!",
153
0
              &abuffer[base_path_length]);
154
0
    goto fail;
155
0
  }
156
157
0
  ok = TRUE;
158
0
fail:
159
0
  if (!ok)
160
0
  {
161
0
    free(fullpath);
162
0
    fullpath = NULL;
163
0
  }
164
0
  return fullpath;
165
0
}
166
167
static BOOL drive_file_set_fullpath(DRIVE_FILE* file, const WCHAR* fullpath)
168
0
{
169
0
  if (!file || !fullpath)
170
0
    return FALSE;
171
172
0
  const size_t len = _wcslen(fullpath);
173
0
  free(file->fullpath);
174
0
  file->fullpath = NULL;
175
176
0
  if (len == 0)
177
0
    return TRUE;
178
179
0
  file->fullpath = _wcsdup(fullpath);
180
0
  if (!file->fullpath)
181
0
    return FALSE;
182
183
0
  const WCHAR sep[] = { PathGetSeparatorW(PATH_STYLE_NATIVE), '\0' };
184
0
  WCHAR* filename = _wcsrchr(file->fullpath, *sep);
185
0
  if (filename && _wcsncmp(filename, sep, ARRAYSIZE(sep)) == 0)
186
0
    *filename = '\0';
187
188
0
  return TRUE;
189
0
}
190
191
static BOOL drive_file_init(DRIVE_FILE* file)
192
0
{
193
0
  UINT CreateDisposition = 0;
194
0
  DWORD dwAttr = GetFileAttributesW(file->fullpath);
195
196
0
  if (dwAttr != INVALID_FILE_ATTRIBUTES)
197
0
  {
198
    /* The file exists */
199
0
    file->is_dir = (dwAttr & FILE_ATTRIBUTE_DIRECTORY) != 0;
200
201
0
    if (file->is_dir)
202
0
    {
203
0
      if (file->CreateDisposition == FILE_CREATE)
204
0
      {
205
0
        SetLastError(ERROR_ALREADY_EXISTS);
206
0
        return FALSE;
207
0
      }
208
209
0
      if (file->CreateOptions & FILE_NON_DIRECTORY_FILE)
210
0
      {
211
0
        SetLastError(ERROR_ACCESS_DENIED);
212
0
        return FALSE;
213
0
      }
214
215
0
      return TRUE;
216
0
    }
217
0
    else
218
0
    {
219
0
      if (file->CreateOptions & FILE_DIRECTORY_FILE)
220
0
      {
221
0
        SetLastError(ERROR_DIRECTORY);
222
0
        return FALSE;
223
0
      }
224
0
    }
225
0
  }
226
0
  else
227
0
  {
228
0
    file->is_dir = ((file->CreateOptions & FILE_DIRECTORY_FILE) ? TRUE : FALSE);
229
230
0
    if (file->is_dir)
231
0
    {
232
      /* Should only create the directory if the disposition allows for it */
233
0
      if ((file->CreateDisposition == FILE_OPEN_IF) ||
234
0
          (file->CreateDisposition == FILE_CREATE))
235
0
      {
236
0
        if (CreateDirectoryW(file->fullpath, NULL) != 0)
237
0
        {
238
0
          return TRUE;
239
0
        }
240
0
      }
241
242
0
      SetLastError(ERROR_FILE_NOT_FOUND);
243
0
      return FALSE;
244
0
    }
245
0
  }
246
247
0
  if (file->file_handle == INVALID_HANDLE_VALUE)
248
0
  {
249
0
    switch (file->CreateDisposition)
250
0
    {
251
0
      case FILE_SUPERSEDE: /* If the file already exists, replace it with the given file. If
252
                              it does not, create the given file. */
253
0
        CreateDisposition = CREATE_ALWAYS;
254
0
        break;
255
256
0
      case FILE_OPEN: /* If the file already exists, open it instead of creating a new file.
257
                         If it does not, fail the request and do not create a new file. */
258
0
        CreateDisposition = OPEN_EXISTING;
259
0
        break;
260
261
0
      case FILE_CREATE: /* If the file already exists, fail the request and do not create or
262
                           open the given file. If it does not, create the given file. */
263
0
        CreateDisposition = CREATE_NEW;
264
0
        break;
265
266
0
      case FILE_OPEN_IF: /* If the file already exists, open it. If it does not, create the
267
                            given file. */
268
0
        CreateDisposition = OPEN_ALWAYS;
269
0
        break;
270
271
0
      case FILE_OVERWRITE: /* If the file already exists, open it and overwrite it. If it does
272
                              not, fail the request. */
273
0
        CreateDisposition = TRUNCATE_EXISTING;
274
0
        break;
275
276
0
      case FILE_OVERWRITE_IF: /* If the file already exists, open it and overwrite it. If it
277
                                 does not, create the given file. */
278
0
        CreateDisposition = CREATE_ALWAYS;
279
0
        break;
280
281
0
      default:
282
0
        break;
283
0
    }
284
285
0
#ifndef WIN32
286
0
    file->SharedAccess = 0;
287
0
#endif
288
0
    file->file_handle = CreateFileW(file->fullpath, file->DesiredAccess, file->SharedAccess,
289
0
                                    NULL, CreateDisposition, file->FileAttributes, NULL);
290
0
  }
291
292
#ifdef WIN32
293
  if (file->file_handle == INVALID_HANDLE_VALUE)
294
  {
295
    /* Get the error message, if any. */
296
    DWORD errorMessageID = GetLastError();
297
298
    if (errorMessageID != 0)
299
    {
300
      LPSTR messageBuffer = NULL;
301
      size_t size =
302
          FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
303
                             FORMAT_MESSAGE_IGNORE_INSERTS,
304
                         NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
305
                         (LPSTR)&messageBuffer, 0, NULL);
306
      char fullpath[MAX_PATH] = { 0 };
307
      (void)ConvertWCharToUtf8(file->fullpath, fullpath, sizeof(fullpath));
308
      WLog_ERR(TAG, "Error in drive_file_init: %s %s", messageBuffer, fullpath);
309
      /* Free the buffer. */
310
      LocalFree(messageBuffer);
311
      /* restore original error code */
312
      SetLastError(errorMessageID);
313
    }
314
  }
315
#endif
316
317
0
  return file->file_handle != INVALID_HANDLE_VALUE;
318
0
}
319
320
DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathWCharLength,
321
                           UINT32 id, UINT32 DesiredAccess, UINT32 CreateDisposition,
322
                           UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess)
323
0
{
324
0
  if (!base_path || (!path && (PathWCharLength > 0)))
325
0
    return NULL;
326
327
0
  DRIVE_FILE* file = (DRIVE_FILE*)calloc(1, sizeof(DRIVE_FILE));
328
329
0
  if (!file)
330
0
  {
331
0
    WLog_ERR(TAG, "calloc failed!");
332
0
    return NULL;
333
0
  }
334
335
0
  file->file_handle = INVALID_HANDLE_VALUE;
336
0
  file->find_handle = INVALID_HANDLE_VALUE;
337
0
  file->id = id;
338
0
  file->basepath = base_path;
339
0
  file->FileAttributes = FileAttributes;
340
0
  file->DesiredAccess = DesiredAccess;
341
0
  file->CreateDisposition = CreateDisposition;
342
0
  file->CreateOptions = CreateOptions;
343
0
  file->SharedAccess = SharedAccess;
344
345
0
  WCHAR* p = drive_file_combine_fullpath(base_path, path, PathWCharLength);
346
0
  (void)drive_file_set_fullpath(file, p);
347
0
  free(p);
348
349
0
  if (!drive_file_init(file))
350
0
  {
351
0
    DWORD lastError = GetLastError();
352
0
    drive_file_free(file);
353
0
    SetLastError(lastError);
354
0
    return NULL;
355
0
  }
356
357
0
  return file;
358
0
}
359
360
BOOL drive_file_free(DRIVE_FILE* file)
361
0
{
362
0
  BOOL rc = FALSE;
363
364
0
  if (!file)
365
0
    return FALSE;
366
367
0
  if (file->file_handle != INVALID_HANDLE_VALUE)
368
0
  {
369
0
    (void)CloseHandle(file->file_handle);
370
0
    file->file_handle = INVALID_HANDLE_VALUE;
371
0
  }
372
373
0
  if (file->find_handle != INVALID_HANDLE_VALUE)
374
0
  {
375
0
    FindClose(file->find_handle);
376
0
    file->find_handle = INVALID_HANDLE_VALUE;
377
0
  }
378
379
0
  if (file->CreateOptions & FILE_DELETE_ON_CLOSE)
380
0
    file->delete_pending = TRUE;
381
382
0
  if (file->delete_pending)
383
0
  {
384
0
    if (file->is_dir)
385
0
    {
386
0
      if (!winpr_RemoveDirectory_RecursiveW(file->fullpath))
387
0
        goto fail;
388
0
    }
389
0
    else if (!DeleteFileW(file->fullpath))
390
0
      goto fail;
391
0
  }
392
393
0
  rc = TRUE;
394
0
fail:
395
0
  DEBUG_WSTR("Free %s", file->fullpath);
396
0
  free(file->fullpath);
397
0
  free(file);
398
0
  return rc;
399
0
}
400
401
BOOL drive_file_seek(DRIVE_FILE* file, UINT64 Offset)
402
0
{
403
0
  LARGE_INTEGER loffset = { 0 };
404
405
0
  if (!file)
406
0
    return FALSE;
407
408
0
  if (Offset > INT64_MAX)
409
0
    return FALSE;
410
411
0
  loffset.QuadPart = (LONGLONG)Offset;
412
0
  return SetFilePointerEx(file->file_handle, loffset, NULL, FILE_BEGIN);
413
0
}
414
415
BOOL drive_file_read(DRIVE_FILE* file, BYTE* buffer, UINT32* Length)
416
0
{
417
0
  DWORD read = 0;
418
419
0
  if (!file || !buffer || !Length)
420
0
    return FALSE;
421
422
0
  DEBUG_WSTR("Read file %s", file->fullpath);
423
424
0
  if (ReadFile(file->file_handle, buffer, *Length, &read, NULL))
425
0
  {
426
0
    *Length = read;
427
0
    return TRUE;
428
0
  }
429
430
0
  return FALSE;
431
0
}
432
433
BOOL drive_file_write(DRIVE_FILE* file, const BYTE* buffer, UINT32 Length)
434
0
{
435
0
  DWORD written = 0;
436
437
0
  if (!file || !buffer)
438
0
    return FALSE;
439
440
0
  DEBUG_WSTR("Write file %s", file->fullpath);
441
442
0
  while (Length > 0)
443
0
  {
444
0
    if (!WriteFile(file->file_handle, buffer, Length, &written, NULL))
445
0
      return FALSE;
446
447
0
    Length -= written;
448
0
    buffer += written;
449
0
  }
450
451
0
  return TRUE;
452
0
}
453
454
static BOOL drive_file_query_from_handle_information(const DRIVE_FILE* file,
455
                                                     const BY_HANDLE_FILE_INFORMATION* info,
456
                                                     UINT32 FsInformationClass, wStream* output)
457
0
{
458
0
  switch (FsInformationClass)
459
0
  {
460
0
    case FileBasicInformation:
461
462
      /* http://msdn.microsoft.com/en-us/library/cc232094.aspx */
463
0
      if (!Stream_EnsureRemainingCapacity(output, 4 + 36))
464
0
        return FALSE;
465
466
0
      Stream_Write_UINT32(output, 36);                                    /* Length */
467
0
      Stream_Write_UINT32(output, info->ftCreationTime.dwLowDateTime);    /* CreationTime */
468
0
      Stream_Write_UINT32(output, info->ftCreationTime.dwHighDateTime);   /* CreationTime */
469
0
      Stream_Write_UINT32(output, info->ftLastAccessTime.dwLowDateTime);  /* LastAccessTime */
470
0
      Stream_Write_UINT32(output, info->ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
471
0
      Stream_Write_UINT32(output, info->ftLastWriteTime.dwLowDateTime);   /* LastWriteTime */
472
0
      Stream_Write_UINT32(output, info->ftLastWriteTime.dwHighDateTime);  /* LastWriteTime */
473
0
      Stream_Write_UINT32(output, info->ftLastWriteTime.dwLowDateTime);   /* ChangeTime */
474
0
      Stream_Write_UINT32(output, info->ftLastWriteTime.dwHighDateTime);  /* ChangeTime */
475
0
      Stream_Write_UINT32(output, info->dwFileAttributes);                /* FileAttributes */
476
      /* Reserved(4), MUST NOT be added! */
477
0
      break;
478
479
0
    case FileStandardInformation:
480
481
      /*  http://msdn.microsoft.com/en-us/library/cc232088.aspx */
482
0
      if (!Stream_EnsureRemainingCapacity(output, 4 + 22))
483
0
        return FALSE;
484
485
0
      Stream_Write_UINT32(output, 22);                          /* Length */
486
0
      Stream_Write_UINT32(output, info->nFileSizeLow);          /* AllocationSize */
487
0
      Stream_Write_UINT32(output, info->nFileSizeHigh);         /* AllocationSize */
488
0
      Stream_Write_UINT32(output, info->nFileSizeLow);          /* EndOfFile */
489
0
      Stream_Write_UINT32(output, info->nFileSizeHigh);         /* EndOfFile */
490
0
      Stream_Write_UINT32(output, info->nNumberOfLinks);        /* NumberOfLinks */
491
0
      Stream_Write_UINT8(output, file->delete_pending ? 1 : 0); /* DeletePending */
492
0
      Stream_Write_UINT8(output, info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
493
0
                                     ? TRUE
494
0
                                     : FALSE); /* Directory */
495
      /* Reserved(2), MUST NOT be added! */
496
0
      break;
497
498
0
    case FileAttributeTagInformation:
499
500
      /* http://msdn.microsoft.com/en-us/library/cc232093.aspx */
501
0
      if (!Stream_EnsureRemainingCapacity(output, 4 + 8))
502
0
        return FALSE;
503
504
0
      Stream_Write_UINT32(output, 8);                      /* Length */
505
0
      Stream_Write_UINT32(output, info->dwFileAttributes); /* FileAttributes */
506
0
      Stream_Write_UINT32(output, 0);                      /* ReparseTag */
507
0
      break;
508
509
0
    default:
510
      /* Unhandled FsInformationClass */
511
0
      WLog_WARN(TAG, "Unhandled FSInformationClass %s [0x%08" PRIx32 "]",
512
0
                FSInformationClass2Tag(FsInformationClass), FsInformationClass);
513
0
      return FALSE;
514
0
  }
515
516
0
  return TRUE;
517
0
}
518
519
static BOOL drive_file_query_from_attributes(const DRIVE_FILE* file,
520
                                             const WIN32_FILE_ATTRIBUTE_DATA* attrib,
521
                                             UINT32 FsInformationClass, wStream* output)
522
0
{
523
0
  switch (FsInformationClass)
524
0
  {
525
0
    case FileBasicInformation:
526
527
      /* http://msdn.microsoft.com/en-us/library/cc232094.aspx */
528
0
      if (!Stream_EnsureRemainingCapacity(output, 4 + 36))
529
0
        return FALSE;
530
531
0
      Stream_Write_UINT32(output, 36);                                    /* Length */
532
0
      Stream_Write_UINT32(output, attrib->ftCreationTime.dwLowDateTime);  /* CreationTime */
533
0
      Stream_Write_UINT32(output, attrib->ftCreationTime.dwHighDateTime); /* CreationTime */
534
0
      Stream_Write_UINT32(output,
535
0
                          attrib->ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
536
0
      Stream_Write_UINT32(output,
537
0
                          attrib->ftLastAccessTime.dwHighDateTime);       /* LastAccessTime */
538
0
      Stream_Write_UINT32(output, attrib->ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
539
0
      Stream_Write_UINT32(output, attrib->ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
540
0
      Stream_Write_UINT32(output, attrib->ftLastWriteTime.dwLowDateTime);  /* ChangeTime */
541
0
      Stream_Write_UINT32(output, attrib->ftLastWriteTime.dwHighDateTime); /* ChangeTime */
542
0
      Stream_Write_UINT32(output, attrib->dwFileAttributes); /* FileAttributes */
543
      /* Reserved(4), MUST NOT be added! */
544
0
      break;
545
546
0
    case FileStandardInformation:
547
548
      /*  http://msdn.microsoft.com/en-us/library/cc232088.aspx */
549
0
      if (!Stream_EnsureRemainingCapacity(output, 4 + 22))
550
0
        return FALSE;
551
552
0
      Stream_Write_UINT32(output, 22);                          /* Length */
553
0
      Stream_Write_UINT32(output, attrib->nFileSizeLow);        /* AllocationSize */
554
0
      Stream_Write_UINT32(output, attrib->nFileSizeHigh);       /* AllocationSize */
555
0
      Stream_Write_UINT32(output, attrib->nFileSizeLow);        /* EndOfFile */
556
0
      Stream_Write_UINT32(output, attrib->nFileSizeHigh);       /* EndOfFile */
557
0
      Stream_Write_UINT32(output, 0);                           /* NumberOfLinks */
558
0
      Stream_Write_UINT8(output, file->delete_pending ? 1 : 0); /* DeletePending */
559
0
      Stream_Write_UINT8(output, attrib->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
560
0
                                     ? TRUE
561
0
                                     : FALSE); /* Directory */
562
      /* Reserved(2), MUST NOT be added! */
563
0
      break;
564
565
0
    case FileAttributeTagInformation:
566
567
      /* http://msdn.microsoft.com/en-us/library/cc232093.aspx */
568
0
      if (!Stream_EnsureRemainingCapacity(output, 4 + 8))
569
0
        return FALSE;
570
571
0
      Stream_Write_UINT32(output, 8);                        /* Length */
572
0
      Stream_Write_UINT32(output, attrib->dwFileAttributes); /* FileAttributes */
573
0
      Stream_Write_UINT32(output, 0);                        /* ReparseTag */
574
0
      break;
575
576
0
    default:
577
      /* Unhandled FsInformationClass */
578
0
      WLog_WARN(TAG, "Unhandled FSInformationClass %s [0x%08" PRIx32 "]",
579
0
                FSInformationClass2Tag(FsInformationClass), FsInformationClass);
580
0
      return FALSE;
581
0
  }
582
583
0
  return TRUE;
584
0
}
585
586
BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, wStream* output)
587
0
{
588
0
  BY_HANDLE_FILE_INFORMATION fileInformation = { 0 };
589
0
  BOOL status = 0;
590
0
  HANDLE hFile = NULL;
591
592
0
  if (!file || !output)
593
0
    return FALSE;
594
595
0
  if ((file->file_handle != INVALID_HANDLE_VALUE) &&
596
0
      GetFileInformationByHandle(file->file_handle, &fileInformation))
597
0
    return drive_file_query_from_handle_information(file, &fileInformation, FsInformationClass,
598
0
                                                    output);
599
600
0
  hFile = CreateFileW(file->fullpath, 0, FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
601
0
                      FILE_ATTRIBUTE_NORMAL, NULL);
602
0
  if (hFile != INVALID_HANDLE_VALUE)
603
0
  {
604
0
    status = GetFileInformationByHandle(hFile, &fileInformation);
605
0
    (void)CloseHandle(hFile);
606
0
    if (!status)
607
0
      goto out_fail;
608
609
0
    if (!drive_file_query_from_handle_information(file, &fileInformation, FsInformationClass,
610
0
                                                  output))
611
0
      goto out_fail;
612
613
0
    return TRUE;
614
0
  }
615
616
  /* If we failed before (i.e. if information for a drive is queried) fall back to
617
   * GetFileAttributesExW */
618
0
  WIN32_FILE_ATTRIBUTE_DATA fileAttributes = { 0 };
619
0
  if (!GetFileAttributesExW(file->fullpath, GetFileExInfoStandard, &fileAttributes))
620
0
    goto out_fail;
621
622
0
  if (!drive_file_query_from_attributes(file, &fileAttributes, FsInformationClass, output))
623
0
    goto out_fail;
624
625
0
  return TRUE;
626
0
out_fail:
627
0
  Stream_Write_UINT32(output, 0); /* Length */
628
0
  return FALSE;
629
0
}
630
631
static BOOL drive_file_set_basic_information(DRIVE_FILE* file, UINT32 Length, wStream* input)
632
0
{
633
0
  WINPR_ASSERT(file);
634
635
0
  const uint32_t expect = 36;
636
0
  if (Length != expect)
637
0
  {
638
0
    WLog_WARN(TAG, "Unexpected Length=%" PRIu32 ", expected %" PRIu32, Length, expect);
639
0
    return FALSE;
640
0
  }
641
642
  /* http://msdn.microsoft.com/en-us/library/cc232094.aspx */
643
0
  const ULARGE_INTEGER liCreationTime = { .QuadPart = Stream_Get_UINT64(input) };
644
0
  const ULARGE_INTEGER liLastAccessTime = { .QuadPart = Stream_Get_UINT64(input) };
645
0
  const ULARGE_INTEGER liLastWriteTime = { .QuadPart = Stream_Get_UINT64(input) };
646
0
  const ULARGE_INTEGER liChangeTime = { .QuadPart = Stream_Get_UINT64(input) };
647
0
  const uint32_t FileAttributes = Stream_Get_UINT32(input);
648
649
0
  if (!PathFileExistsW(file->fullpath))
650
0
    return FALSE;
651
652
0
  if (file->file_handle == INVALID_HANDLE_VALUE)
653
0
  {
654
0
    char fullpath[MAX_PATH] = { 0 };
655
0
    (void)ConvertWCharToUtf8(file->fullpath, fullpath, sizeof(fullpath) - 1);
656
657
0
    WLog_ERR(TAG, "Unable to set file time %s (%" PRId32 ")", fullpath, GetLastError());
658
0
    return FALSE;
659
0
  }
660
661
0
  FILETIME ftCreationTime = { 0 };
662
0
  FILETIME ftLastAccessTime = { 0 };
663
0
  FILETIME ftLastWriteTime = { 0 };
664
0
  FILETIME* pftCreationTime = NULL;
665
0
  FILETIME* pftLastAccessTime = NULL;
666
0
  FILETIME* pftLastWriteTime = NULL;
667
0
  if (liCreationTime.QuadPart != 0)
668
0
  {
669
0
    ftCreationTime.dwHighDateTime = liCreationTime.u.HighPart;
670
0
    ftCreationTime.dwLowDateTime = liCreationTime.u.LowPart;
671
0
    pftCreationTime = &ftCreationTime;
672
0
  }
673
674
0
  if (liLastAccessTime.QuadPart != 0)
675
0
  {
676
0
    ftLastAccessTime.dwHighDateTime = liLastAccessTime.u.HighPart;
677
0
    ftLastAccessTime.dwLowDateTime = liLastAccessTime.u.LowPart;
678
0
    pftLastAccessTime = &ftLastAccessTime;
679
0
  }
680
681
0
  if (liLastWriteTime.QuadPart != 0)
682
0
  {
683
0
    ftLastWriteTime.dwHighDateTime = liLastWriteTime.u.HighPart;
684
0
    ftLastWriteTime.dwLowDateTime = liLastWriteTime.u.LowPart;
685
0
    pftLastWriteTime = &ftLastWriteTime;
686
0
  }
687
688
0
  if (liChangeTime.QuadPart != 0 && liChangeTime.QuadPart > liLastWriteTime.QuadPart)
689
0
  {
690
0
    ftLastWriteTime.dwHighDateTime = liChangeTime.u.HighPart;
691
0
    ftLastWriteTime.dwLowDateTime = liChangeTime.u.LowPart;
692
0
    pftLastWriteTime = &ftLastWriteTime;
693
0
  }
694
695
0
  DEBUG_WSTR("SetFileTime %s", file->fullpath);
696
697
0
  if (!SetFileAttributesW(file->fullpath, FileAttributes))
698
0
  {
699
0
    char fullpath[MAX_PATH] = { 0 };
700
0
    (void)ConvertWCharToUtf8(file->fullpath, fullpath, sizeof(fullpath));
701
0
    WLog_ERR(TAG, "Unable to set file attributes for %s", fullpath);
702
0
    return FALSE;
703
0
  }
704
705
0
  if (!SetFileTime(file->file_handle, pftCreationTime, pftLastAccessTime, pftLastWriteTime))
706
0
  {
707
0
    char fullpath[MAX_PATH] = { 0 };
708
0
    (void)ConvertWCharToUtf8(file->fullpath, fullpath, sizeof(fullpath));
709
0
    WLog_ERR(TAG, "Unable to set file time for %s", fullpath);
710
0
    return FALSE;
711
0
  }
712
0
  return TRUE;
713
0
}
714
715
static BOOL drive_file_set_alloc_information(DRIVE_FILE* file, UINT32 Length, wStream* input)
716
0
{
717
0
  WINPR_ASSERT(file);
718
0
  const uint32_t expect = 8;
719
0
  if (Length != expect)
720
0
  {
721
0
    WLog_WARN(TAG, "Unexpected Length=%" PRIu32 ", expected %" PRIu32, Length, expect);
722
0
    return FALSE;
723
0
  }
724
725
  /* http://msdn.microsoft.com/en-us/library/cc232076.aspx */
726
0
  const int64_t size = Stream_Get_INT64(input);
727
728
0
  if (file->file_handle == INVALID_HANDLE_VALUE)
729
0
  {
730
0
    char fullpath[MAX_PATH] = { 0 };
731
0
    (void)ConvertWCharToUtf8(file->fullpath, fullpath, sizeof(fullpath));
732
0
    WLog_ERR(TAG, "Unable to truncate %s to %" PRId64 " (%" PRId32 ")", fullpath, size,
733
0
             GetLastError());
734
0
    return FALSE;
735
0
  }
736
737
0
  LARGE_INTEGER liSize = { .QuadPart = size };
738
739
0
  if (!SetFilePointerEx(file->file_handle, liSize, NULL, FILE_BEGIN))
740
0
  {
741
0
    char fullpath[MAX_PATH] = { 0 };
742
0
    (void)ConvertWCharToUtf8(file->fullpath, fullpath, sizeof(fullpath));
743
0
    WLog_ERR(TAG, "Unable to truncate %s to %" PRId64 " (%" PRId32 ")", fullpath, size,
744
0
             GetLastError());
745
0
    return FALSE;
746
0
  }
747
748
0
  DEBUG_WSTR("Truncate %s", file->fullpath);
749
750
0
  if (SetEndOfFile(file->file_handle) == 0)
751
0
  {
752
0
    char fullpath[MAX_PATH] = { 0 };
753
0
    (void)ConvertWCharToUtf8(file->fullpath, fullpath, sizeof(fullpath));
754
0
    WLog_ERR(TAG, "Unable to truncate %s to %" PRId64 " (%" PRId32 ")", fullpath, size,
755
0
             GetLastError());
756
0
    return FALSE;
757
0
  }
758
759
0
  return TRUE;
760
0
}
761
762
static BOOL drive_file_set_disposition_information(DRIVE_FILE* file, UINT32 Length, wStream* input)
763
0
{
764
0
  WINPR_ASSERT(file);
765
0
  uint8_t delete_pending = 0;
766
  /* http://msdn.microsoft.com/en-us/library/cc232098.aspx */
767
  /* http://msdn.microsoft.com/en-us/library/cc241371.aspx */
768
0
  if (file->is_dir && !PathIsDirectoryEmptyW(file->fullpath))
769
0
  {
770
0
    SetLastError(ERROR_DIR_NOT_EMPTY);
771
0
    return FALSE;
772
0
  }
773
774
0
  if (Length)
775
0
  {
776
0
    const uint32_t expect = 1;
777
0
    if (Length != expect)
778
0
    {
779
0
      WLog_WARN(TAG, "Unexpected Length=%" PRIu32 ", expected %" PRIu32, Length, expect);
780
0
      return FALSE;
781
0
    }
782
783
0
    delete_pending = Stream_Get_UINT8(input);
784
0
  }
785
0
  else
786
0
    delete_pending = 1;
787
788
0
  if (delete_pending)
789
0
  {
790
0
    DEBUG_WSTR("SetDeletePending %s", file->fullpath);
791
0
    const uint32_t attr = GetFileAttributesW(file->fullpath);
792
793
0
    if (attr & FILE_ATTRIBUTE_READONLY)
794
0
    {
795
0
      SetLastError(ERROR_ACCESS_DENIED);
796
0
      return FALSE;
797
0
    }
798
0
  }
799
800
0
  file->delete_pending = delete_pending;
801
0
  return TRUE;
802
0
}
803
804
static BOOL drive_file_set_rename_information(DRIVE_FILE* file, UINT32 Length, wStream* input)
805
0
{
806
0
  WINPR_ASSERT(file);
807
808
0
  const uint32_t expect = 6;
809
0
  if (Length < expect)
810
0
  {
811
0
    WLog_WARN(TAG, "Unexpected Length=%" PRIu32 ", expected at least %" PRIu32, Length, expect);
812
0
    return FALSE;
813
0
  }
814
815
  /* http://msdn.microsoft.com/en-us/library/cc232085.aspx */
816
0
  const uint8_t ReplaceIfExists = Stream_Get_UINT8(input);
817
0
  Stream_Seek_UINT8(input); /* RootDirectory */
818
0
  const uint32_t FileNameLength = Stream_Get_UINT32(input);
819
820
0
  if (Length != expect + FileNameLength)
821
0
  {
822
0
    WLog_WARN(TAG, "Unexpected Length=%" PRIu32 ", expected %" PRIu32, Length,
823
0
              expect + FileNameLength);
824
0
    return FALSE;
825
0
  }
826
827
0
  WCHAR* fullpath = drive_file_combine_fullpath(file->basepath, Stream_ConstPointer(input),
828
0
                                                FileNameLength / sizeof(WCHAR));
829
830
0
  if (!fullpath)
831
0
    return FALSE;
832
833
#ifdef _WIN32
834
835
  if (file->file_handle != INVALID_HANDLE_VALUE)
836
  {
837
    (void)CloseHandle(file->file_handle);
838
    file->file_handle = INVALID_HANDLE_VALUE;
839
  }
840
841
#endif
842
0
  DEBUG_WSTR("MoveFileExW %s", file->fullpath);
843
844
0
  if (MoveFileExW(file->fullpath, fullpath,
845
0
                  MOVEFILE_COPY_ALLOWED | (ReplaceIfExists ? MOVEFILE_REPLACE_EXISTING : 0)))
846
0
  {
847
0
    const BOOL rc = drive_file_set_fullpath(file, fullpath);
848
0
    free(fullpath);
849
0
    if (!rc)
850
0
      return FALSE;
851
0
  }
852
0
  else
853
0
  {
854
0
    free(fullpath);
855
0
    return FALSE;
856
0
  }
857
858
#ifdef _WIN32
859
  drive_file_init(file);
860
#endif
861
0
  return TRUE;
862
0
}
863
864
BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UINT32 Length,
865
                                wStream* input)
866
0
{
867
0
  if (!file || !input)
868
0
    return FALSE;
869
870
0
  if (!Stream_CheckAndLogRequiredLength(TAG, input, Length))
871
0
    return FALSE;
872
873
0
  switch (FsInformationClass)
874
0
  {
875
0
    case FileBasicInformation:
876
0
      return drive_file_set_basic_information(file, Length, input);
877
878
0
    case FileEndOfFileInformation:
879
    /* http://msdn.microsoft.com/en-us/library/cc232067.aspx */
880
0
    case FileAllocationInformation:
881
0
      return drive_file_set_alloc_information(file, Length, input);
882
883
0
    case FileDispositionInformation:
884
0
      return drive_file_set_disposition_information(file, Length, input);
885
886
0
    case FileRenameInformation:
887
0
      return drive_file_set_rename_information(file, Length, input);
888
889
0
    default:
890
0
      WLog_WARN(TAG, "Unhandled FSInformationClass %s [0x%08" PRIx32 "]",
891
0
                FSInformationClass2Tag(FsInformationClass), FsInformationClass);
892
0
      return FALSE;
893
0
  }
894
895
0
  return TRUE;
896
0
}
897
898
static BOOL drive_file_query_dir_info(DRIVE_FILE* file, wStream* output, size_t length)
899
0
{
900
0
  WINPR_ASSERT(file);
901
0
  WINPR_ASSERT(output);
902
903
  /* http://msdn.microsoft.com/en-us/library/cc232097.aspx */
904
0
  if (!Stream_EnsureRemainingCapacity(output, 4 + 64 + length))
905
0
    return FALSE;
906
907
0
  if (length > UINT32_MAX - 64)
908
0
    return FALSE;
909
910
0
  Stream_Write_UINT32(output, (UINT32)(64 + length));                        /* Length */
911
0
  Stream_Write_UINT32(output, 0);                                            /* NextEntryOffset */
912
0
  Stream_Write_UINT32(output, 0);                                            /* FileIndex */
913
0
  Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */
914
0
  Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */
915
0
  Stream_Write_UINT32(output,
916
0
                      file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
917
0
  Stream_Write_UINT32(output,
918
0
                      file->find_data.ftLastAccessTime.dwHighDateTime);       /* LastAccessTime */
919
0
  Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
920
0
  Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
921
0
  Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime);  /* ChangeTime */
922
0
  Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
923
0
  Stream_Write_UINT32(output, file->find_data.nFileSizeLow);                   /* EndOfFile */
924
0
  Stream_Write_UINT32(output, file->find_data.nFileSizeHigh);                  /* EndOfFile */
925
0
  Stream_Write_UINT32(output, file->find_data.nFileSizeLow);     /* AllocationSize */
926
0
  Stream_Write_UINT32(output, file->find_data.nFileSizeHigh);    /* AllocationSize */
927
0
  Stream_Write_UINT32(output, file->find_data.dwFileAttributes); /* FileAttributes */
928
0
  Stream_Write_UINT32(output, (UINT32)length);                   /* FileNameLength */
929
0
  Stream_Write(output, file->find_data.cFileName, length);
930
0
  return TRUE;
931
0
}
932
933
static BOOL drive_file_query_full_dir_info(DRIVE_FILE* file, wStream* output, size_t length)
934
0
{
935
0
  WINPR_ASSERT(file);
936
0
  WINPR_ASSERT(output);
937
  /* http://msdn.microsoft.com/en-us/library/cc232068.aspx */
938
0
  if (!Stream_EnsureRemainingCapacity(output, 4 + 68 + length))
939
0
    return FALSE;
940
941
0
  if (length > UINT32_MAX - 68)
942
0
    return FALSE;
943
944
0
  Stream_Write_UINT32(output, (UINT32)(68 + length));                        /* Length */
945
0
  Stream_Write_UINT32(output, 0);                                            /* NextEntryOffset */
946
0
  Stream_Write_UINT32(output, 0);                                            /* FileIndex */
947
0
  Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */
948
0
  Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */
949
0
  Stream_Write_UINT32(output,
950
0
                      file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
951
0
  Stream_Write_UINT32(output,
952
0
                      file->find_data.ftLastAccessTime.dwHighDateTime);       /* LastAccessTime */
953
0
  Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
954
0
  Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
955
0
  Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime);  /* ChangeTime */
956
0
  Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
957
0
  Stream_Write_UINT32(output, file->find_data.nFileSizeLow);                   /* EndOfFile */
958
0
  Stream_Write_UINT32(output, file->find_data.nFileSizeHigh);                  /* EndOfFile */
959
0
  Stream_Write_UINT32(output, file->find_data.nFileSizeLow);     /* AllocationSize */
960
0
  Stream_Write_UINT32(output, file->find_data.nFileSizeHigh);    /* AllocationSize */
961
0
  Stream_Write_UINT32(output, file->find_data.dwFileAttributes); /* FileAttributes */
962
0
  Stream_Write_UINT32(output, (UINT32)length);                   /* FileNameLength */
963
0
  Stream_Write_UINT32(output, 0);                                /* EaSize */
964
0
  Stream_Write(output, file->find_data.cFileName, length);
965
0
  return TRUE;
966
0
}
967
968
static BOOL drive_file_query_both_dir_info(DRIVE_FILE* file, wStream* output, size_t length)
969
0
{
970
0
  WINPR_ASSERT(file);
971
0
  WINPR_ASSERT(output);
972
  /* http://msdn.microsoft.com/en-us/library/cc232095.aspx */
973
0
  if (!Stream_EnsureRemainingCapacity(output, 4 + 93 + length))
974
0
    return FALSE;
975
976
0
  if (length > UINT32_MAX - 93)
977
0
    return FALSE;
978
979
0
  Stream_Write_UINT32(output, (UINT32)(93 + length));                        /* Length */
980
0
  Stream_Write_UINT32(output, 0);                                            /* NextEntryOffset */
981
0
  Stream_Write_UINT32(output, 0);                                            /* FileIndex */
982
0
  Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */
983
0
  Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */
984
0
  Stream_Write_UINT32(output,
985
0
                      file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
986
0
  Stream_Write_UINT32(output,
987
0
                      file->find_data.ftLastAccessTime.dwHighDateTime);       /* LastAccessTime */
988
0
  Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
989
0
  Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
990
0
  Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime);  /* ChangeTime */
991
0
  Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
992
0
  Stream_Write_UINT32(output, file->find_data.nFileSizeLow);                   /* EndOfFile */
993
0
  Stream_Write_UINT32(output, file->find_data.nFileSizeHigh);                  /* EndOfFile */
994
0
  Stream_Write_UINT32(output, file->find_data.nFileSizeLow);     /* AllocationSize */
995
0
  Stream_Write_UINT32(output, file->find_data.nFileSizeHigh);    /* AllocationSize */
996
0
  Stream_Write_UINT32(output, file->find_data.dwFileAttributes); /* FileAttributes */
997
0
  Stream_Write_UINT32(output, (UINT32)length);                   /* FileNameLength */
998
0
  Stream_Write_UINT32(output, 0);                                /* EaSize */
999
0
  Stream_Write_UINT8(output, 0);                                 /* ShortNameLength */
1000
  /* Reserved(1), MUST NOT be added! */
1001
0
  Stream_Zero(output, 24); /* ShortName */
1002
0
  Stream_Write(output, file->find_data.cFileName, length);
1003
0
  return TRUE;
1004
0
}
1005
1006
static BOOL drive_file_query_names_info(DRIVE_FILE* file, wStream* output, size_t length)
1007
0
{
1008
0
  WINPR_ASSERT(file);
1009
0
  WINPR_ASSERT(output);
1010
  /* http://msdn.microsoft.com/en-us/library/cc232077.aspx */
1011
0
  if (!Stream_EnsureRemainingCapacity(output, 4 + 12 + length))
1012
0
    return FALSE;
1013
1014
0
  if (length > UINT32_MAX - 12)
1015
0
    return FALSE;
1016
1017
0
  Stream_Write_UINT32(output, (UINT32)(12 + length)); /* Length */
1018
0
  Stream_Write_UINT32(output, 0);                     /* NextEntryOffset */
1019
0
  Stream_Write_UINT32(output, 0);                     /* FileIndex */
1020
0
  Stream_Write_UINT32(output, (UINT32)length);        /* FileNameLength */
1021
0
  Stream_Write(output, file->find_data.cFileName, length);
1022
0
  return TRUE;
1023
0
}
1024
1025
BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery,
1026
                                const WCHAR* path, UINT32 PathWCharLength, wStream* output)
1027
0
{
1028
0
  BOOL rc = FALSE;
1029
0
  size_t length = 0;
1030
0
  WCHAR* ent_path = NULL;
1031
1032
0
  if (!file || !path || !output)
1033
0
    return FALSE;
1034
1035
0
  if (InitialQuery != 0)
1036
0
  {
1037
    /* release search handle */
1038
0
    if (file->find_handle != INVALID_HANDLE_VALUE)
1039
0
      FindClose(file->find_handle);
1040
1041
0
    ent_path = drive_file_combine_fullpath(file->basepath, path, PathWCharLength);
1042
    /* open new search handle and retrieve the first entry */
1043
0
    file->find_handle = FindFirstFileW(ent_path, &file->find_data);
1044
0
    free(ent_path);
1045
1046
0
    if (file->find_handle == INVALID_HANDLE_VALUE)
1047
0
      goto out_fail;
1048
0
  }
1049
0
  else if (!FindNextFileW(file->find_handle, &file->find_data))
1050
0
    goto out_fail;
1051
1052
0
  length = _wcslen(file->find_data.cFileName) * 2;
1053
1054
0
  switch (FsInformationClass)
1055
0
  {
1056
0
    case FileDirectoryInformation:
1057
0
      rc = drive_file_query_dir_info(file, output, length);
1058
0
      break;
1059
1060
0
    case FileFullDirectoryInformation:
1061
0
      rc = drive_file_query_full_dir_info(file, output, length);
1062
0
      break;
1063
1064
0
    case FileBothDirectoryInformation:
1065
0
      rc = drive_file_query_both_dir_info(file, output, length);
1066
0
      break;
1067
1068
0
    case FileNamesInformation:
1069
0
      rc = drive_file_query_names_info(file, output, length);
1070
0
      break;
1071
1072
0
    default:
1073
0
      WLog_WARN(TAG, "Unhandled FSInformationClass %s [0x%08" PRIx32 "]",
1074
0
                FSInformationClass2Tag(FsInformationClass), FsInformationClass);
1075
      /* Unhandled FsInformationClass */
1076
0
      goto out_fail;
1077
0
  }
1078
1079
0
out_fail:
1080
0
  if (!rc)
1081
0
  {
1082
0
    Stream_Write_UINT32(output, 0); /* Length */
1083
0
    Stream_Write_UINT8(output, 0);  /* Padding */
1084
0
  }
1085
0
  return rc;
1086
0
}