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