Coverage Report

Created: 2026-10-15 06:30

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