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