Coverage Report

Created: 2026-04-12 07:03

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