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