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