Coverage Report

Created: 2024-09-08 06:20

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