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