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