Coverage Report

Created: 2026-03-04 06:17

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