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