VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/SharedFolders/driver/info.cpp@ 78563

Last change on this file since 78563 was 78563, checked in by vboxsync, 6 years ago

winnt/vboxsf: Investigated all the file info queries after opening a file and used TTL on the stat info to try avoid those. The TTL code needed some fixing. bugref:9172

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 106.9 KB
Line 
1/* $Id: info.cpp 78563 2019-05-17 11:46:23Z vboxsync $ */
2/** @file
3 * VirtualBox Windows Guest Shared Folders FSD - Information Querying & Setting Routines.
4 */
5
6/*
7 * Copyright (C) 2012-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include "vbsf.h"
23#include <iprt/asm.h>
24#include <iprt/err.h>
25#include <iprt/mem.h>
26
27extern "C" NTSTATUS NTAPI RxSetEndOfFileInfo(PRX_CONTEXT, PIRP, PFCB, PFOBX);
28
29
30/*********************************************************************************************************************************
31* Defined Constants And Macros *
32*********************************************************************************************************************************/
33/** Macro for copying a SHFLSTRING file name into a FILE_DIRECTORY_INFORMATION structure. */
34#define INIT_FILE_NAME(obj, str) \
35 do { \
36 ULONG cbLength = (str).u16Length; \
37 (obj)->FileNameLength = cbLength; \
38 RtlCopyMemory((obj)->FileName, &(str).String.ucs2[0], cbLength + 2); \
39 } while (0)
40
41
42NTSTATUS VBoxMRxQueryDirectory(IN OUT PRX_CONTEXT RxContext)
43{
44 NTSTATUS Status = STATUS_SUCCESS;
45
46 RxCaptureFobx;
47 RxCaptureFcb;
48
49 PMRX_VBOX_NETROOT_EXTENSION pNetRootExtension = VBoxMRxGetNetRootExtension(capFcb->pNetRoot);
50 PMRX_VBOX_FOBX pVBoxFobx = VBoxMRxGetFileObjectExtension(capFobx);
51
52 PUNICODE_STRING DirectoryName = GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext);
53 PUNICODE_STRING Template = &capFobx->UnicodeQueryTemplate;
54 FILE_INFORMATION_CLASS FileInformationClass = RxContext->Info.FileInformationClass;
55 PCHAR pInfoBuffer = (PCHAR)RxContext->Info.Buffer;
56 LONG cbMaxSize = RxContext->Info.Length;
57 LONG *pLengthRemaining = (LONG *)&RxContext->Info.LengthRemaining;
58
59 LONG cbToCopy;
60 int vrc;
61 uint8_t *pHGCMBuffer;
62 uint32_t index, fSFFlags, cFiles, u32BufSize;
63 LONG cbHGCMBuffer;
64 PSHFLDIRINFO pDirEntry;
65
66 ULONG *pNextOffset = 0;
67 PSHFLSTRING ParsedPath = NULL;
68
69 Log(("VBOXSF: MrxQueryDirectory: FileInformationClass %d, pVBoxFobx %p, hFile %RX64, pInfoBuffer %p\n",
70 FileInformationClass, pVBoxFobx, pVBoxFobx->hFile, pInfoBuffer));
71
72 if (!pVBoxFobx)
73 {
74 Log(("VBOXSF: MrxQueryDirectory: pVBoxFobx is invalid!\n"));
75 return STATUS_INVALID_PARAMETER;
76 }
77
78 if (!DirectoryName)
79 return STATUS_INVALID_PARAMETER;
80
81 if (DirectoryName->Length == 0)
82 Log(("VBOXSF: MrxQueryDirectory: DirectoryName = \\ (null string)\n"));
83 else
84 Log(("VBOXSF: MrxQueryDirectory: DirectoryName = %.*ls\n",
85 DirectoryName->Length / sizeof(WCHAR), DirectoryName->Buffer));
86
87 if (!Template)
88 return STATUS_INVALID_PARAMETER;
89
90 if (Template->Length == 0)
91 Log(("VBOXSF: MrxQueryDirectory: Template = \\ (null string)\n"));
92 else
93 Log(("VBOXSF: MrxQueryDirectory: Template = %.*ls\n",
94 Template->Length / sizeof(WCHAR), Template->Buffer));
95
96 cbHGCMBuffer = RT_MAX(cbMaxSize, PAGE_SIZE);
97
98 Log(("VBOXSF: MrxQueryDirectory: Allocating cbHGCMBuffer = %d\n",
99 cbHGCMBuffer));
100
101 pHGCMBuffer = (uint8_t *)vbsfNtAllocNonPagedMem(cbHGCMBuffer);
102 if (!pHGCMBuffer)
103 {
104 AssertFailed();
105 return STATUS_INSUFFICIENT_RESOURCES;
106 }
107
108 /* Assume start from the beginning. */
109 index = 0;
110 if (RxContext->QueryDirectory.IndexSpecified == TRUE)
111 {
112 Log(("VBOXSF: MrxQueryDirectory: Index specified %d\n",
113 index));
114 index = RxContext->QueryDirectory.FileIndex;
115 }
116
117 fSFFlags = SHFL_LIST_NONE;
118 if (RxContext->QueryDirectory.ReturnSingleEntry == TRUE)
119 {
120 Log(("VBOXSF: MrxQueryDirectory: Query single entry\n"));
121 fSFFlags |= SHFL_LIST_RETURN_ONE;
122 }
123 if ( RxContext->QueryDirectory.RestartScan == TRUE
124 && RxContext->QueryDirectory.InitialQuery == FALSE)
125 {
126 Log(("VBOXSF: MrxQueryDirectory: Restart scan\n"));
127 fSFFlags |= SHFL_LIST_RESTART;
128 }
129
130 if (Template->Length)
131 {
132 ULONG ParsedPathSize, cch;
133
134 /* Calculate size required for parsed path: dir + \ + template + 0. */
135 ParsedPathSize = SHFLSTRING_HEADER_SIZE + Template->Length + sizeof(WCHAR);
136 if (DirectoryName->Length)
137 ParsedPathSize += DirectoryName->Length + sizeof(WCHAR);
138 Log(("VBOXSF: MrxQueryDirectory: ParsedPathSize = %d\n", ParsedPathSize));
139
140 ParsedPath = (PSHFLSTRING)vbsfNtAllocNonPagedMem(ParsedPathSize);
141 if (!ParsedPath)
142 {
143 Status = STATUS_INSUFFICIENT_RESOURCES;
144 goto end;
145 }
146
147 if (!ShflStringInitBuffer(ParsedPath, ParsedPathSize))
148 {
149 Status = STATUS_INSUFFICIENT_RESOURCES;
150 goto end;
151 }
152
153 cch = 0;
154 if (DirectoryName->Length)
155 {
156 /* Copy directory name into ParsedPath. */
157 RtlCopyMemory(ParsedPath->String.ucs2, DirectoryName->Buffer, DirectoryName->Length);
158 cch += DirectoryName->Length / sizeof(WCHAR);
159
160 /* Add terminating backslash. */
161 ParsedPath->String.ucs2[cch] = L'\\';
162 cch++;
163 }
164
165 RtlCopyMemory (&ParsedPath->String.ucs2[cch], Template->Buffer, Template->Length);
166 cch += Template->Length / sizeof(WCHAR);
167
168 /* Add terminating nul. */
169 ParsedPath->String.ucs2[cch] = 0;
170
171 /* cch is the number of chars without trailing nul. */
172 ParsedPath->u16Length = (uint16_t)(cch * sizeof(WCHAR));
173
174 AssertMsg(ParsedPath->u16Length + sizeof(WCHAR) == ParsedPath->u16Size,
175 ("u16Length %d, u16Size %d\n", ParsedPath->u16Length, ParsedPath->u16Size));
176
177 Log(("VBOXSF: MrxQueryDirectory: ParsedPath = %.*ls\n",
178 ParsedPath->u16Length / sizeof(WCHAR), ParsedPath->String.ucs2));
179 }
180
181 cFiles = 0;
182
183 /* VbglR0SfDirInfo requires a pointer to uint32_t. */
184 u32BufSize = cbHGCMBuffer;
185
186 Log(("VBOXSF: MrxQueryDirectory: CallDirInfo: File = 0x%08x, Flags = 0x%08x, Index = %d, u32BufSize = %d\n",
187 pVBoxFobx->hFile, fSFFlags, index, u32BufSize));
188 vrc = VbglR0SfDirInfo(&g_SfClient, &pNetRootExtension->map, pVBoxFobx->hFile,
189 ParsedPath, fSFFlags, index, &u32BufSize, (PSHFLDIRINFO)pHGCMBuffer, &cFiles);
190 Log(("VBOXSF: MrxQueryDirectory: u32BufSize after CallDirInfo = %d, rc = %Rrc\n",
191 u32BufSize, vrc));
192
193 switch (vrc)
194 {
195 case VINF_SUCCESS:
196 /* Nothing to do here. */
197 break;
198
199 case VERR_NO_TRANSLATION:
200 Log(("VBOXSF: MrxQueryDirectory: Host could not translate entry!\n"));
201 break;
202
203 case VERR_NO_MORE_FILES:
204 if (cFiles <= 0) /* VERR_NO_MORE_FILES appears at the first lookup when just returning the current dir ".".
205 * So we also have to check for the cFiles counter. */
206 {
207 /* Not an error, but we have to handle the return value. */
208 Log(("VBOXSF: MrxQueryDirectory: Host reported no more files!\n"));
209
210 if (RxContext->QueryDirectory.InitialQuery)
211 {
212 /* First call. MSDN on FindFirstFile: "If the function fails because no matching files
213 * can be found, the GetLastError function returns ERROR_FILE_NOT_FOUND."
214 * So map this rc to file not found.
215 */
216 Status = STATUS_NO_SUCH_FILE;
217 }
218 else
219 {
220 /* Search continued. */
221 Status = STATUS_NO_MORE_FILES;
222 }
223 }
224 break;
225
226 case VERR_FILE_NOT_FOUND:
227 Status = STATUS_NO_SUCH_FILE;
228 Log(("VBOXSF: MrxQueryDirectory: no such file!\n"));
229 break;
230
231 default:
232 Status = vbsfNtVBoxStatusToNt(vrc);
233 Log(("VBOXSF: MrxQueryDirectory: Error %Rrc from CallDirInfo (cFiles=%d)!\n",
234 vrc, cFiles));
235 break;
236 }
237
238 if (Status != STATUS_SUCCESS)
239 goto end;
240
241 /* Verify that the returned buffer length is not greater than the original one. */
242 if (u32BufSize > (uint32_t)cbHGCMBuffer)
243 {
244 Log(("VBOXSF: MrxQueryDirectory: returned buffer size (%u) is invalid!!!\n",
245 u32BufSize));
246 Status = STATUS_INVALID_NETWORK_RESPONSE;
247 goto end;
248 }
249
250 /* How many bytes remain in the buffer. */
251 cbHGCMBuffer = u32BufSize;
252
253 pDirEntry = (PSHFLDIRINFO)pHGCMBuffer;
254 Status = STATUS_SUCCESS;
255
256 Log(("VBOXSF: MrxQueryDirectory: cFiles=%d, Length=%d\n",
257 cFiles, cbHGCMBuffer));
258
259 while ((*pLengthRemaining) && (cFiles > 0) && (pDirEntry != NULL))
260 {
261 int cbEntry = RT_UOFFSETOF(SHFLDIRINFO, name.String) + pDirEntry->name.u16Size;
262
263 if (cbEntry > cbHGCMBuffer)
264 {
265 Log(("VBOXSF: MrxQueryDirectory: Entry size (%d) exceeds the buffer size (%d)!!!\n",
266 cbEntry, cbHGCMBuffer));
267 Status = STATUS_INVALID_NETWORK_RESPONSE;
268 goto end;
269 }
270
271 switch (FileInformationClass)
272 {
273 case FileDirectoryInformation:
274 {
275 PFILE_DIRECTORY_INFORMATION pInfo = (PFILE_DIRECTORY_INFORMATION)pInfoBuffer;
276 Log(("VBOXSF: MrxQueryDirectory: FileDirectoryInformation\n"));
277
278 cbToCopy = sizeof(FILE_DIRECTORY_INFORMATION);
279 /* Struct already contains one char for null terminator. */
280 cbToCopy += pDirEntry->name.u16Size;
281
282 if (*pLengthRemaining >= cbToCopy)
283 {
284 RtlZeroMemory(pInfo, cbToCopy);
285
286 pInfo->CreationTime.QuadPart = RTTimeSpecGetNtTime(&pDirEntry->Info.BirthTime); /* ridiculous name */
287 pInfo->LastAccessTime.QuadPart = RTTimeSpecGetNtTime(&pDirEntry->Info.AccessTime);
288 pInfo->LastWriteTime.QuadPart = RTTimeSpecGetNtTime(&pDirEntry->Info.ModificationTime);
289 pInfo->ChangeTime.QuadPart = RTTimeSpecGetNtTime(&pDirEntry->Info.ChangeTime);
290 pInfo->AllocationSize.QuadPart = pDirEntry->Info.cbAllocated;
291 pInfo->EndOfFile.QuadPart = pDirEntry->Info.cbObject;
292 pInfo->FileIndex = index;
293 pInfo->FileAttributes = VBoxToNTFileAttributes(pDirEntry->Info.Attr.fMode);
294
295 INIT_FILE_NAME(pInfo, pDirEntry->name);
296
297 /* Align to 8 byte boundary */
298 cbToCopy = RT_ALIGN(cbToCopy, sizeof(LONGLONG));
299 pInfo->NextEntryOffset = cbToCopy;
300 pNextOffset = &pInfo->NextEntryOffset;
301 }
302 else
303 {
304 pInfo->NextEntryOffset = 0; /* last item */
305 Status = STATUS_BUFFER_OVERFLOW;
306 }
307 break;
308 }
309
310 case FileFullDirectoryInformation:
311 {
312 PFILE_FULL_DIR_INFORMATION pInfo = (PFILE_FULL_DIR_INFORMATION)pInfoBuffer;
313 Log(("VBOXSF: MrxQueryDirectory: FileFullDirectoryInformation\n"));
314
315 cbToCopy = sizeof(FILE_FULL_DIR_INFORMATION);
316 /* Struct already contains one char for null terminator. */
317 cbToCopy += pDirEntry->name.u16Size;
318
319 if (*pLengthRemaining >= cbToCopy)
320 {
321 RtlZeroMemory(pInfo, cbToCopy);
322
323 pInfo->CreationTime.QuadPart = RTTimeSpecGetNtTime(&pDirEntry->Info.BirthTime); /* ridiculous name */
324 pInfo->LastAccessTime.QuadPart = RTTimeSpecGetNtTime(&pDirEntry->Info.AccessTime);
325 pInfo->LastWriteTime.QuadPart = RTTimeSpecGetNtTime(&pDirEntry->Info.ModificationTime);
326 pInfo->ChangeTime.QuadPart = RTTimeSpecGetNtTime(&pDirEntry->Info.ChangeTime);
327 pInfo->AllocationSize.QuadPart = pDirEntry->Info.cbAllocated;
328 pInfo->EndOfFile.QuadPart = pDirEntry->Info.cbObject;
329 pInfo->EaSize = 0;
330 pInfo->FileIndex = index;
331 pInfo->FileAttributes = VBoxToNTFileAttributes(pDirEntry->Info.Attr.fMode);
332
333 INIT_FILE_NAME(pInfo, pDirEntry->name);
334
335 /* Align to 8 byte boundary */
336 cbToCopy = RT_ALIGN(cbToCopy, sizeof(LONGLONG));
337 pInfo->NextEntryOffset = cbToCopy;
338 pNextOffset = &pInfo->NextEntryOffset;
339 }
340 else
341 {
342 pInfo->NextEntryOffset = 0; /* last item */
343 Status = STATUS_BUFFER_OVERFLOW;
344 }
345 break;
346 }
347
348 case FileBothDirectoryInformation:
349 {
350 PFILE_BOTH_DIR_INFORMATION pInfo = (PFILE_BOTH_DIR_INFORMATION)pInfoBuffer;
351 Log(("VBOXSF: MrxQueryDirectory: FileBothDirectoryInformation\n"));
352
353 cbToCopy = sizeof(FILE_BOTH_DIR_INFORMATION);
354 /* struct already contains one char for null terminator */
355 cbToCopy += pDirEntry->name.u16Size;
356
357 if (*pLengthRemaining >= cbToCopy)
358 {
359 RtlZeroMemory(pInfo, cbToCopy);
360
361 pInfo->CreationTime.QuadPart = RTTimeSpecGetNtTime(&pDirEntry->Info.BirthTime); /* ridiculous name */
362 pInfo->LastAccessTime.QuadPart = RTTimeSpecGetNtTime(&pDirEntry->Info.AccessTime);
363 pInfo->LastWriteTime.QuadPart = RTTimeSpecGetNtTime(&pDirEntry->Info.ModificationTime);
364 pInfo->ChangeTime.QuadPart = RTTimeSpecGetNtTime(&pDirEntry->Info.ChangeTime);
365 pInfo->AllocationSize.QuadPart = pDirEntry->Info.cbAllocated;
366 pInfo->EndOfFile.QuadPart = pDirEntry->Info.cbObject;
367 pInfo->EaSize = 0;
368 pInfo->ShortNameLength = 0; /** @todo ? */
369 pInfo->FileIndex = index;
370 pInfo->FileAttributes = VBoxToNTFileAttributes(pDirEntry->Info.Attr.fMode);
371
372 INIT_FILE_NAME(pInfo, pDirEntry->name);
373
374 Log(("VBOXSF: MrxQueryDirectory: FileBothDirectoryInformation cbAlloc = %x cbObject = %x\n",
375 pDirEntry->Info.cbAllocated, pDirEntry->Info.cbObject));
376 Log(("VBOXSF: MrxQueryDirectory: FileBothDirectoryInformation cbToCopy = %d, name size=%d name len=%d\n",
377 cbToCopy, pDirEntry->name.u16Size, pDirEntry->name.u16Length));
378 Log(("VBOXSF: MrxQueryDirectory: FileBothDirectoryInformation File name %.*ls (DirInfo)\n",
379 pInfo->FileNameLength / sizeof(WCHAR), pInfo->FileName));
380 Log(("VBOXSF: MrxQueryDirectory: FileBothDirectoryInformation File name %.*ls (DirEntry)\n",
381 pDirEntry->name.u16Size / sizeof(WCHAR), pDirEntry->name.String.ucs2));
382
383 /* Align to 8 byte boundary. */
384 cbToCopy = RT_ALIGN(cbToCopy, sizeof(LONGLONG));
385 pInfo->NextEntryOffset = cbToCopy;
386 pNextOffset = &pInfo->NextEntryOffset;
387 }
388 else
389 {
390 pInfo->NextEntryOffset = 0; /* Last item. */
391 Status = STATUS_BUFFER_OVERFLOW;
392 }
393 break;
394 }
395
396 case FileIdBothDirectoryInformation:
397 {
398 PFILE_ID_BOTH_DIR_INFORMATION pInfo = (PFILE_ID_BOTH_DIR_INFORMATION)pInfoBuffer;
399 Log(("VBOXSF: MrxQueryDirectory: FileIdBothDirectoryInformation\n"));
400
401 cbToCopy = sizeof(FILE_ID_BOTH_DIR_INFORMATION);
402 /* struct already contains one char for null terminator */
403 cbToCopy += pDirEntry->name.u16Size;
404
405 if (*pLengthRemaining >= cbToCopy)
406 {
407 RtlZeroMemory(pInfo, cbToCopy);
408
409 pInfo->CreationTime.QuadPart = RTTimeSpecGetNtTime(&pDirEntry->Info.BirthTime); /* ridiculous name */
410 pInfo->LastAccessTime.QuadPart = RTTimeSpecGetNtTime(&pDirEntry->Info.AccessTime);
411 pInfo->LastWriteTime.QuadPart = RTTimeSpecGetNtTime(&pDirEntry->Info.ModificationTime);
412 pInfo->ChangeTime.QuadPart = RTTimeSpecGetNtTime(&pDirEntry->Info.ChangeTime);
413 pInfo->AllocationSize.QuadPart = pDirEntry->Info.cbAllocated;
414 pInfo->EndOfFile.QuadPart = pDirEntry->Info.cbObject;
415 pInfo->EaSize = 0;
416 pInfo->ShortNameLength = 0; /** @todo ? */
417 pInfo->EaSize = 0;
418 pInfo->FileId.QuadPart = 0;
419 pInfo->FileAttributes = VBoxToNTFileAttributes(pDirEntry->Info.Attr.fMode);
420
421 INIT_FILE_NAME(pInfo, pDirEntry->name);
422
423 Log(("VBOXSF: MrxQueryDirectory: FileIdBothDirectoryInformation cbAlloc = 0x%RX64 cbObject = 0x%RX64\n",
424 pDirEntry->Info.cbAllocated, pDirEntry->Info.cbObject));
425 Log(("VBOXSF: MrxQueryDirectory: FileIdBothDirectoryInformation cbToCopy = %d, name size=%d name len=%d\n",
426 cbToCopy, pDirEntry->name.u16Size, pDirEntry->name.u16Length));
427 Log(("VBOXSF: MrxQueryDirectory: FileIdBothDirectoryInformation File name %.*ls (DirInfo)\n",
428 pInfo->FileNameLength / sizeof(WCHAR), pInfo->FileName));
429 Log(("VBOXSF: MrxQueryDirectory: FileIdBothDirectoryInformation File name %.*ls (DirEntry)\n",
430 pDirEntry->name.u16Size / sizeof(WCHAR), pDirEntry->name.String.ucs2));
431
432 /* Align to 8 byte boundary. */
433 cbToCopy = RT_ALIGN(cbToCopy, sizeof(LONGLONG));
434 pInfo->NextEntryOffset = cbToCopy;
435 pNextOffset = &pInfo->NextEntryOffset;
436 }
437 else
438 {
439 pInfo->NextEntryOffset = 0; /* Last item. */
440 Status = STATUS_BUFFER_OVERFLOW;
441 }
442 break;
443 }
444
445 case FileNamesInformation:
446 {
447 PFILE_NAMES_INFORMATION pInfo = (PFILE_NAMES_INFORMATION)pInfoBuffer;
448 Log(("VBOXSF: MrxQueryDirectory: FileNamesInformation\n"));
449
450 cbToCopy = sizeof(FILE_NAMES_INFORMATION);
451 /* Struct already contains one char for null terminator. */
452 cbToCopy += pDirEntry->name.u16Size;
453
454 if (*pLengthRemaining >= cbToCopy)
455 {
456 RtlZeroMemory(pInfo, cbToCopy);
457
458 pInfo->FileIndex = index;
459
460 INIT_FILE_NAME(pInfo, pDirEntry->name);
461
462 Log(("VBOXSF: MrxQueryDirectory: FileNamesInformation: File name [%.*ls]\n",
463 pInfo->FileNameLength / sizeof(WCHAR), pInfo->FileName));
464
465 /* Align to 8 byte boundary. */
466 cbToCopy = RT_ALIGN(cbToCopy, sizeof(LONGLONG));
467 pInfo->NextEntryOffset = cbToCopy;
468 pNextOffset = &pInfo->NextEntryOffset;
469 }
470 else
471 {
472 pInfo->NextEntryOffset = 0; /* Last item. */
473 Status = STATUS_BUFFER_OVERFLOW;
474 }
475 break;
476 }
477
478 default:
479 Log(("VBOXSF: MrxQueryDirectory: Not supported FileInformationClass %d!\n",
480 FileInformationClass));
481 Status = STATUS_INVALID_PARAMETER;
482 goto end;
483 }
484
485 cbHGCMBuffer -= cbEntry;
486 pDirEntry = (PSHFLDIRINFO)((uintptr_t)pDirEntry + cbEntry);
487
488 Log(("VBOXSF: MrxQueryDirectory: %d bytes left in HGCM buffer\n",
489 cbHGCMBuffer));
490
491 if (*pLengthRemaining >= cbToCopy)
492 {
493 pInfoBuffer += cbToCopy;
494 *pLengthRemaining -= cbToCopy;
495 }
496 else
497 break;
498
499 if (RxContext->QueryDirectory.ReturnSingleEntry)
500 break;
501
502 /* More left? */
503 if (cbHGCMBuffer <= 0)
504 break;
505
506 index++; /* File Index. */
507
508 cFiles--;
509 }
510
511 if (pNextOffset)
512 *pNextOffset = 0; /* Last pInfo->NextEntryOffset should be set to zero! */
513
514end:
515 if (pHGCMBuffer)
516 vbsfNtFreeNonPagedMem(pHGCMBuffer);
517
518 if (ParsedPath)
519 vbsfNtFreeNonPagedMem(ParsedPath);
520
521 Log(("VBOXSF: MrxQueryDirectory: Returned 0x%08X\n",
522 Status));
523 return Status;
524}
525
526
527/*********************************************************************************************************************************
528* NtQueryVolumeInformationFile *
529*********************************************************************************************************************************/
530
531/**
532 * Updates VBSFNTFCBEXT::VolInfo.
533 *
534 * Currently no kind of FCB lock is normally held.
535 */
536static NTSTATUS vbsfNtUpdateFcbVolInfo(PVBSFNTFCBEXT pVBoxFcbX, PMRX_VBOX_NETROOT_EXTENSION pNetRootExtension,
537 PMRX_VBOX_FOBX pVBoxFobx)
538{
539 NTSTATUS rcNt;
540 VBOXSFVOLINFOREQ *pReq = (VBOXSFVOLINFOREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq));
541 if (pReq)
542 {
543 int vrc = VbglR0SfHostReqQueryVolInfo(pNetRootExtension->map.root, pReq, pVBoxFobx->hFile);
544 if (RT_SUCCESS(vrc))
545 {
546 /* Make the units compatible with NT before assigning. */
547 if (pReq->VolInfo.ulBytesPerSector != 0)
548 {
549 if (pReq->VolInfo.ulBytesPerAllocationUnit > pReq->VolInfo.ulBytesPerSector)
550 {
551 uint32_t cSectorsPerUnit = pReq->VolInfo.ulBytesPerAllocationUnit / pReq->VolInfo.ulBytesPerSector;
552 pReq->VolInfo.ulBytesPerAllocationUnit = pReq->VolInfo.ulBytesPerSector * cSectorsPerUnit;
553 }
554 else if (pReq->VolInfo.ulBytesPerAllocationUnit < pReq->VolInfo.ulBytesPerSector)
555 pReq->VolInfo.ulBytesPerAllocationUnit = pReq->VolInfo.ulBytesPerSector;
556 }
557 else if (pReq->VolInfo.ulBytesPerAllocationUnit == 0)
558 pReq->VolInfo.ulBytesPerSector = pReq->VolInfo.ulBytesPerAllocationUnit = 512;
559 else
560 pReq->VolInfo.ulBytesPerSector = pReq->VolInfo.ulBytesPerAllocationUnit;
561
562 /* Copy the info assigning: */
563 ASMCompilerBarrier();
564 pVBoxFcbX->VolInfo.ullTotalAllocationBytes = pReq->VolInfo.ullTotalAllocationBytes;
565 pVBoxFcbX->VolInfo.ullAvailableAllocationBytes = pReq->VolInfo.ullAvailableAllocationBytes;
566 pVBoxFcbX->VolInfo.ulBytesPerAllocationUnit = pReq->VolInfo.ulBytesPerAllocationUnit;
567 pVBoxFcbX->VolInfo.ulBytesPerSector = pReq->VolInfo.ulBytesPerSector;
568 pVBoxFcbX->VolInfo.ulSerial = pReq->VolInfo.ulSerial;
569 pVBoxFcbX->VolInfo.fsProperties.cbMaxComponent = pReq->VolInfo.fsProperties.cbMaxComponent;
570 pVBoxFcbX->VolInfo.fsProperties.fRemote = pReq->VolInfo.fsProperties.fRemote;
571 pVBoxFcbX->VolInfo.fsProperties.fCaseSensitive = pReq->VolInfo.fsProperties.fCaseSensitive;
572 pVBoxFcbX->VolInfo.fsProperties.fReadOnly = pReq->VolInfo.fsProperties.fReadOnly;
573 /** @todo Use SHFL_FN_QUERY_MAP_INFO to get the correct read-only status of
574 * the share. */
575 pVBoxFcbX->VolInfo.fsProperties.fSupportsUnicode = pReq->VolInfo.fsProperties.fSupportsUnicode;
576 pVBoxFcbX->VolInfo.fsProperties.fCompressed = pReq->VolInfo.fsProperties.fCompressed;
577 pVBoxFcbX->VolInfo.fsProperties.fFileCompression = pReq->VolInfo.fsProperties.fFileCompression;
578 ASMWriteFence();
579 pVBoxFcbX->nsVolInfoUpToDate = RTTimeSystemNanoTS();
580 ASMWriteFence();
581
582 rcNt = STATUS_SUCCESS;
583 }
584 else
585 rcNt = vbsfNtVBoxStatusToNt(vrc);
586 VbglR0PhysHeapFree(pReq);
587 }
588 else
589 rcNt = STATUS_INSUFFICIENT_RESOURCES;
590 return rcNt;
591}
592
593/**
594 * Handles NtQueryVolumeInformationFile / FileFsVolumeInformation
595 */
596static NTSTATUS vbsfNtQueryFsVolumeInfo(IN OUT PRX_CONTEXT pRxContext,
597 PFILE_FS_VOLUME_INFORMATION pInfo,
598 ULONG cbInfo,
599 PMRX_NET_ROOT pNetRoot,
600 PMRX_VBOX_NETROOT_EXTENSION pNetRootExtension,
601 PMRX_VBOX_FOBX pVBoxFobx,
602 PVBSFNTFCBEXT pVBoxFcbX)
603{
604 /*
605 * NtQueryVolumeInformationFile should've checked the minimum buffer size
606 * but just in case.
607 */
608 AssertReturnStmt(cbInfo >= RT_UOFFSETOF(FILE_FS_VOLUME_INFORMATION, VolumeLabel),
609 pRxContext->InformationToReturn = RT_UOFFSETOF(FILE_FS_VOLUME_INFORMATION, VolumeLabel),
610 STATUS_BUFFER_TOO_SMALL);
611
612 /*
613 * Get up-to-date serial number.
614 *
615 * If we have a unixy host, we'll get additional unix attributes and the
616 * serial number is the same as INodeIdDevice.
617 *
618 * Note! Because it's possible that the host has mount points within the
619 * shared folder as well as symbolic links pointing out files or
620 * directories outside the tree, we cannot just cache the serial
621 * number in the net root extension data and skip querying it here.
622 *
623 * OTOH, only we don't report inode info from the host, so the only
624 * thing the serial number can be used for is to cache/whatever
625 * volume space information. So, we should probably provide a
626 * shortcut here via mount option, registry and guest properties.
627 */
628 /** @todo Make See OTOH above wrt. one serial per net root. */
629 uint64_t nsNow = RTTimeSystemNanoTS();
630 if ( pVBoxFobx->Info.Attr.enmAdditional == SHFLFSOBJATTRADD_UNIX
631 && pVBoxFobx->Info.Attr.u.Unix.INodeIdDevice != 0
632 && pVBoxFobx->nsUpToDate - nsNow < RT_NS_100US /** @todo implement proper TTL */)
633 pInfo->VolumeSerialNumber = pVBoxFobx->Info.Attr.u.Unix.INodeIdDevice;
634 else if (pVBoxFcbX->nsVolInfoUpToDate - nsNow < RT_NS_100MS /** @todo implement proper volume info TTL */ )
635 pInfo->VolumeSerialNumber = pVBoxFcbX->VolInfo.ulSerial;
636 else
637 {
638 /* Must fetch the info. */
639 NTSTATUS Status = vbsfNtUpdateFcbVolInfo(pVBoxFcbX, pNetRootExtension, pVBoxFobx);
640 if (NT_SUCCESS(Status))
641 pInfo->VolumeSerialNumber = pVBoxFcbX->VolInfo.ulSerial;
642 else
643 return Status;
644 }
645 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsVolumeInformation: VolumeSerialNumber=%#010RX32\n", pInfo->VolumeSerialNumber));
646
647 /*
648 * Fill in the static info.
649 */
650 pInfo->VolumeCreationTime.QuadPart = 0;
651 pInfo->SupportsObjects = FALSE;
652
653 /*
654 * The volume label.
655 *
656 * We may get queries with insufficient buffer space for the whole (or any)
657 * volume label. In those cases we're to return STATUS_BUFFER_OVERFLOW,
658 * return the returned number of bytes in Ios.Information and set the
659 * VolumeLabelLength to the actual length (rather than the returned). At
660 * least this is was FAT and NTFS does (however, it is not what the NulMrx
661 * sample from the 6.1.6001.18002 does).
662 *
663 * Note! VolumeLabelLength is a byte count.
664 * Note! NTFS does not include a terminator, so neither do we.
665 */
666 uint32_t const cbShareName = pNetRoot->pNetRootName->Length
667 - pNetRoot->pSrvCall->pSrvCallName->Length
668 - sizeof(WCHAR) /* Remove the leading backslash. */;
669 uint32_t const cbVolLabel = VBOX_VOLNAME_PREFIX_SIZE + cbShareName;
670 pInfo->VolumeLabelLength = cbVolLabel;
671
672 WCHAR const *pwcShareName = &pNetRoot->pNetRootName->Buffer[pNetRoot->pSrvCall->pSrvCallName->Length / sizeof(WCHAR) + 1];
673 uint32_t cbCopied = RT_UOFFSETOF(FILE_FS_VOLUME_INFORMATION, VolumeLabel);
674 NTSTATUS Status;
675 if (cbInfo >= cbCopied + cbVolLabel)
676 {
677 memcpy(pInfo->VolumeLabel, VBOX_VOLNAME_PREFIX, VBOX_VOLNAME_PREFIX_SIZE);
678 memcpy(&pInfo->VolumeLabel[VBOX_VOLNAME_PREFIX_SIZE / sizeof(WCHAR)], pwcShareName, cbShareName);
679 cbCopied += cbVolLabel;
680 Status = STATUS_SUCCESS;
681 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsVolumeInformation: full result (%#x)\n", cbCopied));
682 }
683 else
684 {
685 if (cbInfo > cbCopied)
686 {
687 uint32_t cbLeft = cbInfo - cbCopied;
688 memcpy(pInfo->VolumeLabel, VBOX_VOLNAME_PREFIX, RT_MIN(cbLeft, VBOX_VOLNAME_PREFIX_SIZE));
689 if (cbLeft > VBOX_VOLNAME_PREFIX_SIZE)
690 {
691 cbLeft -= VBOX_VOLNAME_PREFIX_SIZE;
692 memcpy(&pInfo->VolumeLabel[VBOX_VOLNAME_PREFIX_SIZE / sizeof(WCHAR)], pwcShareName, RT_MIN(cbLeft, cbShareName));
693 }
694 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsVolumeInformation: partial result (%#x, needed %#x)\n",
695 cbCopied, cbCopied + cbVolLabel));
696 cbCopied = cbInfo;
697 }
698 else
699 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsVolumeInformation: partial result no label (%#x, needed %#x)\n",
700 cbCopied, cbCopied + cbVolLabel));
701 Status = STATUS_BUFFER_OVERFLOW;
702 }
703
704 /*
705 * Update the return length in the context.
706 */
707 pRxContext->Info.LengthRemaining = cbInfo - cbCopied;
708 pRxContext->InformationToReturn = cbCopied;
709
710 return Status;
711}
712
713/**
714 * Handles NtQueryVolumeInformationFile / FileFsSizeInformation
715 *
716 * @note Almost identical to vbsfNtQueryFsFullSizeInfo.
717 */
718static NTSTATUS vbsfNtQueryFsSizeInfo(IN OUT PRX_CONTEXT pRxContext,
719 PFILE_FS_SIZE_INFORMATION pInfo,
720 ULONG cbInfo,
721 PMRX_VBOX_NETROOT_EXTENSION pNetRootExtension,
722 PMRX_VBOX_FOBX pVBoxFobx,
723 PVBSFNTFCBEXT pVBoxFcbX)
724{
725 /*
726 * NtQueryVolumeInformationFile should've checked the buffer size but just in case.
727 */
728 AssertReturnStmt(cbInfo >= sizeof(*pInfo), pRxContext->InformationToReturn = sizeof(*pInfo), STATUS_BUFFER_TOO_SMALL);
729
730 /*
731 * Get up-to-date information.
732 * For the time being we always re-query this information from the host.
733 */
734 /** @todo don't requery this if it happens with XXXX ns of a _different_ info
735 * request to the same handle. */
736 {
737 /* Must fetch the info. */
738 NTSTATUS Status = vbsfNtUpdateFcbVolInfo(pVBoxFcbX, pNetRootExtension, pVBoxFobx);
739 if (NT_SUCCESS(Status))
740 { /* likely */ }
741 else
742 return Status;
743 }
744
745 /* Make a copy of the info for paranoid reasons: */
746 SHFLVOLINFO VolInfoCopy;
747 memcpy(&VolInfoCopy, (void *)&pVBoxFcbX->VolInfo, sizeof(VolInfoCopy));
748 ASMCompilerBarrier();
749
750 /*
751 * Produce the requested data.
752 */
753 pInfo->BytesPerSector = RT_MIN(VolInfoCopy.ulBytesPerSector, 1);
754 pInfo->SectorsPerAllocationUnit = VolInfoCopy.ulBytesPerAllocationUnit / pInfo->BytesPerSector;
755 AssertReturn(pInfo->SectorsPerAllocationUnit > 0, STATUS_INTERNAL_ERROR);
756 pInfo->TotalAllocationUnits.QuadPart = pVBoxFcbX->VolInfo.ullTotalAllocationBytes
757 / VolInfoCopy.ulBytesPerAllocationUnit;
758 pInfo->AvailableAllocationUnits.QuadPart = pVBoxFcbX->VolInfo.ullAvailableAllocationBytes
759 / VolInfoCopy.ulBytesPerAllocationUnit;
760
761 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsSizeInformation: BytesPerSector = %#010RX32\n",
762 pInfo->BytesPerSector));
763 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsSizeInformation: SectorsPerAllocationUnit = %#010RX32\n",
764 pInfo->SectorsPerAllocationUnit));
765 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsSizeInformation: TotalAllocationUnits = %#018RX32\n",
766 pInfo->TotalAllocationUnits.QuadPart));
767 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsSizeInformation: AvailableAllocationUnits = %#018RX32\n",
768 pInfo->AvailableAllocationUnits.QuadPart));
769
770 /*
771 * Update the return length in the context.
772 */
773 pRxContext->Info.LengthRemaining = cbInfo - sizeof(*pInfo);
774 pRxContext->InformationToReturn = sizeof(*pInfo);
775 return STATUS_SUCCESS;
776}
777
778/**
779 * Handles NtQueryVolumeInformationFile / FileFsFullSizeInformation
780 *
781 * @note Almost identical to vbsfNtQueryFsSizeInfo.
782 */
783static NTSTATUS vbsfNtQueryFsFullSizeInfo(IN OUT PRX_CONTEXT pRxContext,
784 PFILE_FS_FULL_SIZE_INFORMATION pInfo,
785 ULONG cbInfo,
786 PMRX_VBOX_NETROOT_EXTENSION pNetRootExtension,
787 PMRX_VBOX_FOBX pVBoxFobx,
788 PVBSFNTFCBEXT pVBoxFcbX)
789{
790 /*
791 * NtQueryVolumeInformationFile should've checked the buffer size but just in case.
792 */
793 AssertReturnStmt(cbInfo >= sizeof(*pInfo), pRxContext->InformationToReturn = sizeof(*pInfo), STATUS_BUFFER_TOO_SMALL);
794
795 /*
796 * Get up-to-date information.
797 * For the time being we always re-query this information from the host.
798 */
799 /** @todo don't requery this if it happens with XXXX ns of a _different_ info
800 * request to the same handle. */
801 {
802 /* Must fetch the info. */
803 NTSTATUS Status = vbsfNtUpdateFcbVolInfo(pVBoxFcbX, pNetRootExtension, pVBoxFobx);
804 if (NT_SUCCESS(Status))
805 { /* likely */ }
806 else
807 return Status;
808 }
809
810 /* Make a copy of the info for paranoid reasons: */
811 SHFLVOLINFO VolInfoCopy;
812 memcpy(&VolInfoCopy, (void *)&pVBoxFcbX->VolInfo, sizeof(VolInfoCopy));
813 ASMCompilerBarrier();
814
815 /*
816 * Produce the requested data.
817 */
818 pInfo->BytesPerSector = RT_MIN(VolInfoCopy.ulBytesPerSector, 1);
819 pInfo->SectorsPerAllocationUnit = VolInfoCopy.ulBytesPerAllocationUnit / pInfo->BytesPerSector;
820 AssertReturn(pInfo->SectorsPerAllocationUnit > 0, STATUS_INTERNAL_ERROR);
821 pInfo->TotalAllocationUnits.QuadPart = pVBoxFcbX->VolInfo.ullTotalAllocationBytes
822 / VolInfoCopy.ulBytesPerAllocationUnit;
823 pInfo->ActualAvailableAllocationUnits.QuadPart = pVBoxFcbX->VolInfo.ullAvailableAllocationBytes
824 / VolInfoCopy.ulBytesPerAllocationUnit;
825 pInfo->CallerAvailableAllocationUnits.QuadPart = pInfo->ActualAvailableAllocationUnits.QuadPart;
826
827 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsFullSizeInformation: BytesPerSector = %#010RX32\n",
828 pInfo->BytesPerSector));
829 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsFullSizeInformation: SectorsPerAllocationUnit = %#010RX32\n",
830 pInfo->SectorsPerAllocationUnit));
831 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsFullSizeInformation: TotalAllocationUnits = %#018RX32\n",
832 pInfo->TotalAllocationUnits.QuadPart));
833 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsFullSizeInformation: ActualAvailableAllocationUnits = %#018RX32\n",
834 pInfo->ActualAvailableAllocationUnits.QuadPart));
835 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsFullSizeInformation: CallerAvailableAllocationUnits = %#018RX32\n",
836 pInfo->CallerAvailableAllocationUnits.QuadPart));
837
838 /*
839 * Update the return length in the context.
840 */
841 pRxContext->Info.LengthRemaining = cbInfo - sizeof(*pInfo);
842 pRxContext->InformationToReturn = sizeof(*pInfo);
843 return STATUS_SUCCESS;
844}
845
846/**
847 * Handles NtQueryVolumeInformationFile / FileFsDeviceInformation
848 */
849static NTSTATUS vbsfNtQueryFsDeviceInfo(IN OUT PRX_CONTEXT pRxContext,
850 PFILE_FS_DEVICE_INFORMATION pInfo,
851 ULONG cbInfo,
852 PMRX_NET_ROOT pNetRoot)
853{
854 /*
855 * NtQueryVolumeInformationFile should've checked the buffer size but just in case.
856 */
857 AssertReturnStmt(cbInfo >= sizeof(*pInfo), pRxContext->InformationToReturn = sizeof(*pInfo), STATUS_BUFFER_TOO_SMALL);
858
859 /*
860 * Produce the requested data.
861 */
862 pInfo->DeviceType = pNetRoot->DeviceType;
863 pInfo->Characteristics = FILE_REMOTE_DEVICE;
864
865 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsFullSizeInformation: DeviceType = %#x\n", pInfo->DeviceType));
866 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsFullSizeInformation: Characteristics = %#x (FILE_REMOTE_DEVICE)\n", FILE_REMOTE_DEVICE));
867
868 /*
869 * Update the return length in the context.
870 */
871 pRxContext->Info.LengthRemaining = cbInfo - sizeof(*pInfo);
872 pRxContext->InformationToReturn = sizeof(*pInfo);
873 return STATUS_SUCCESS;
874}
875
876/**
877 * Handles NtQueryVolumeInformationFile / FileFsDeviceInformation
878 */
879static NTSTATUS vbsfNtQueryFsAttributeInfo(IN OUT PRX_CONTEXT pRxContext,
880 PFILE_FS_ATTRIBUTE_INFORMATION pInfo,
881 ULONG cbInfo,
882 PMRX_VBOX_NETROOT_EXTENSION pNetRootExtension,
883 PMRX_VBOX_FOBX pVBoxFobx,
884 PVBSFNTFCBEXT pVBoxFcbX)
885{
886 static WCHAR const s_wszFsName[] = MRX_VBOX_FILESYS_NAME_U;
887 static ULONG const s_cbFsName = sizeof(s_wszFsName) - sizeof(s_wszFsName[0]);
888 ULONG const cbNeeded = RT_UOFFSETOF(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName) + s_cbFsName;
889
890 /*
891 * NtQueryVolumeInformationFile should've checked the buffer size but just in case.
892 */
893 AssertReturnStmt(cbInfo >= RT_UOFFSETOF(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName),
894 pRxContext->InformationToReturn = cbNeeded,
895 STATUS_BUFFER_TOO_SMALL);
896
897 /*
898 * Get up-to-date information about filename length and such.
899 */
900 if (pVBoxFcbX->nsVolInfoUpToDate - RTTimeSystemNanoTS() < RT_NS_100MS /** @todo implement proper volume info TTL */ )
901 {
902 /* Must fetch the info. */
903 NTSTATUS Status = vbsfNtUpdateFcbVolInfo(pVBoxFcbX, pNetRootExtension, pVBoxFobx);
904 if (NT_SUCCESS(Status))
905 { /* likely */ }
906 else
907 return Status;
908 }
909
910 /*
911 * Produce the requested data.
912 *
913 * Note! The MaximumComponentNameLength is documented (1) to be in bytes, but
914 * NTFS and FAT32 both return 255, indicating that it is really a UTF-16 char count.
915 *
916 * Note! Both NTFS and FAT32 seems to be setting Ios.Information and FileSystemNameLength
917 * the number of bytes returned in the STATUS_BUFFER_OVERFLOW case, making it
918 * impossible to guess the length from the returned data. RDR2 forwards information
919 * from the server, and samba returns a fixed FileSystemNameLength.
920 *
921 * (1) https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/ns-ntifs-_file_fs_attribute_information
922 */
923 pInfo->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES;
924 /** @todo Implement FILE_RETURNS_CLEANUP_RESULT_INFO. */
925 if (pVBoxFcbX->VolInfo.fsProperties.fSupportsUnicode)
926 pInfo->FileSystemAttributes |= FILE_UNICODE_ON_DISK;
927 if (pVBoxFcbX->VolInfo.fsProperties.fReadOnly)
928 pInfo->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
929 if (pVBoxFcbX->VolInfo.fsProperties.fFileCompression)
930 pInfo->FileSystemAttributes |= FILE_FILE_COMPRESSION;
931 else if (pVBoxFcbX->VolInfo.fsProperties.fCompressed)
932 pInfo->FileSystemAttributes |= FILE_VOLUME_IS_COMPRESSED;
933 pInfo->MaximumComponentNameLength = pVBoxFcbX->VolInfo.fsProperties.cbMaxComponent
934 ? pVBoxFcbX->VolInfo.fsProperties.cbMaxComponent : 255;
935 ULONG const cbStrCopied = RT_MIN(cbInfo - RT_UOFFSETOF(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName), s_cbFsName);
936 pInfo->FileSystemNameLength = s_cbFsName;
937 if (cbStrCopied > 0)
938 memcpy(pInfo->FileSystemName, MRX_VBOX_FILESYS_NAME_U, cbStrCopied);
939
940 /*
941 * Update the return length in the context.
942 */
943 pRxContext->Info.LengthRemaining = cbInfo - cbStrCopied - RT_UOFFSETOF(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName);
944 pRxContext->InformationToReturn = cbStrCopied + RT_UOFFSETOF(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName);
945 return cbInfo >= cbNeeded ? STATUS_SUCCESS : STATUS_BUFFER_OVERFLOW;
946}
947
948/**
949 * Handles NtQueryVolumeInformationFile / FileFsSectorSizeInformation
950 */
951static NTSTATUS vbsfNtQueryFsSectorSizeInfo(IN OUT PRX_CONTEXT pRxContext,
952 PFILE_FS_SECTOR_SIZE_INFORMATION pInfo,
953 ULONG cbInfo,
954 PMRX_VBOX_NETROOT_EXTENSION pNetRootExtension,
955 PMRX_VBOX_FOBX pVBoxFobx,
956 PVBSFNTFCBEXT pVBoxFcbX)
957{
958 /*
959 * NtQueryVolumeInformationFile should've checked the buffer size but just in case.
960 */
961 AssertReturnStmt(cbInfo >= sizeof(*pInfo), pRxContext->InformationToReturn = sizeof(*pInfo), STATUS_BUFFER_TOO_SMALL);
962
963 /*
964 * Get up-to-date sector size info.
965 */
966 if (pVBoxFcbX->nsVolInfoUpToDate - RTTimeSystemNanoTS() < RT_NS_100MS /** @todo implement proper volume info TTL */ )
967 {
968 /* Must fetch the info. */
969 NTSTATUS Status = vbsfNtUpdateFcbVolInfo(pVBoxFcbX, pNetRootExtension, pVBoxFobx);
970 if (NT_SUCCESS(Status))
971 { /* likely */ }
972 else
973 return Status;
974 }
975
976 /*
977 * Produce the requested data (currently no way to query more than the
978 * basic sector size here, so just repeat it).
979 */
980 uint32_t const cbSector = pVBoxFcbX->VolInfo.ulBytesPerSector ? pVBoxFcbX->VolInfo.ulBytesPerSector : 512;
981 pInfo->LogicalBytesPerSector = cbSector;
982 pInfo->PhysicalBytesPerSectorForAtomicity = cbSector;
983 pInfo->PhysicalBytesPerSectorForPerformance = cbSector;
984 pInfo->FileSystemEffectivePhysicalBytesPerSectorForAtomicity = cbSector;
985 pInfo->Flags = 0;
986 pInfo->ByteOffsetForSectorAlignment = SSINFO_OFFSET_UNKNOWN;
987 pInfo->ByteOffsetForPartitionAlignment = SSINFO_OFFSET_UNKNOWN;
988
989 /*
990 * Update the return length in the context.
991 */
992 pRxContext->Info.LengthRemaining = cbInfo - sizeof(*pInfo);
993 pRxContext->InformationToReturn = sizeof(*pInfo);
994 return STATUS_SUCCESS;
995}
996
997
998/**
999 * Handles NtQueryVolumeInformationFile and similar.
1000 *
1001 * The RDBSS library does not do a whole lot for these queries. No FCB locking.
1002 * The IO_STATUS_BLOCK updating differs too, setting of Ios.Information is
1003 * limited to cbInitialBuf - RxContext->Info.LengthRemaining.
1004 */
1005NTSTATUS VBoxMRxQueryVolumeInfo(IN OUT PRX_CONTEXT RxContext)
1006{
1007#ifdef LOG_ENABLED
1008 static const char * const s_apszNames[] =
1009 {
1010 "FileFsInvalidZeroEntry", "FileFsVolumeInformation", "FileFsLabelInformation",
1011 "FileFsSizeInformation", "FileFsDeviceInformation", "FileFsAttributeInformation",
1012 "FileFsControlInformation", "FileFsFullSizeInformation", "FileFsObjectIdInformation",
1013 "FileFsDriverPathInformation", "FileFsVolumeFlagsInformation", "FileFsSectorSizeInformation",
1014 "FileFsDataCopyInformation", "FileFsMetadataSizeInformation", "FileFsFullSizeInformationEx",
1015 };
1016#endif
1017 RxCaptureFcb;
1018 RxCaptureFobx;
1019 PMRX_VBOX_NETROOT_EXTENSION pNetRootExtension = VBoxMRxGetNetRootExtension(capFcb->pNetRoot);
1020 PMRX_VBOX_FOBX pVBoxFobx = VBoxMRxGetFileObjectExtension(capFobx);
1021 NTSTATUS Status;
1022
1023 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: pInfoBuffer = %p, cbInfoBuffer = %d\n",
1024 RxContext->Info.Buffer, RxContext->Info.LengthRemaining));
1025 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: vboxFobx = %p, Handle = 0x%RX64\n",
1026 pVBoxFobx, pVBoxFobx ? pVBoxFobx->hFile : 0));
1027
1028 switch (RxContext->Info.FsInformationClass)
1029 {
1030 case FileFsVolumeInformation:
1031 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsVolumeInformation\n"));
1032 AssertReturn(pVBoxFobx, STATUS_INVALID_PARAMETER);
1033 Status = vbsfNtQueryFsVolumeInfo(RxContext, (PFILE_FS_VOLUME_INFORMATION)RxContext->Info.Buffer,
1034 RxContext->Info.Length, capFcb->pNetRoot, pNetRootExtension, pVBoxFobx,
1035 VBoxMRxGetFcbExtension(capFcb));
1036 break;
1037
1038 case FileFsSizeInformation:
1039 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsSizeInformation\n"));
1040 AssertReturn(pVBoxFobx, STATUS_INVALID_PARAMETER);
1041 Status = vbsfNtQueryFsSizeInfo(RxContext, (PFILE_FS_SIZE_INFORMATION)RxContext->Info.Buffer,
1042 RxContext->Info.Length, pNetRootExtension, pVBoxFobx,
1043 VBoxMRxGetFcbExtension(capFcb));
1044 break;
1045
1046 case FileFsFullSizeInformation:
1047 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsFullSizeInformation\n"));
1048 AssertReturn(pVBoxFobx, STATUS_INVALID_PARAMETER);
1049 Status = vbsfNtQueryFsFullSizeInfo(RxContext, (PFILE_FS_FULL_SIZE_INFORMATION)RxContext->Info.Buffer,
1050 RxContext->Info.Length, pNetRootExtension, pVBoxFobx,
1051 VBoxMRxGetFcbExtension(capFcb));
1052 break;
1053
1054 case FileFsDeviceInformation:
1055 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsDeviceInformation\n"));
1056 AssertReturn(pVBoxFobx, STATUS_INVALID_PARAMETER);
1057 Status = vbsfNtQueryFsDeviceInfo(RxContext, (PFILE_FS_DEVICE_INFORMATION)RxContext->Info.Buffer,
1058 RxContext->Info.Length, capFcb->pNetRoot);
1059 break;
1060
1061 case FileFsAttributeInformation:
1062 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsAttributeInformation\n"));
1063 AssertReturn(pVBoxFobx, STATUS_INVALID_PARAMETER);
1064 Status = vbsfNtQueryFsAttributeInfo(RxContext, (PFILE_FS_ATTRIBUTE_INFORMATION)RxContext->Info.Buffer,
1065 RxContext->Info.Length, pNetRootExtension, pVBoxFobx,
1066 VBoxMRxGetFcbExtension(capFcb));
1067 break;
1068
1069 case FileFsSectorSizeInformation:
1070 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: FileFsSectorSizeInformation\n"));
1071 AssertReturn(pVBoxFobx, STATUS_INVALID_PARAMETER);
1072 Status = vbsfNtQueryFsSectorSizeInfo(RxContext, (PFILE_FS_SECTOR_SIZE_INFORMATION)RxContext->Info.Buffer,
1073 RxContext->Info.Length, pNetRootExtension, pVBoxFobx,
1074 VBoxMRxGetFcbExtension(capFcb));
1075 break;
1076
1077 case FileFsLabelInformation:
1078 AssertFailed(/* Only for setting, not for querying. */);
1079 RT_FALL_THRU();
1080 default:
1081 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: Not supported FS_INFORMATION_CLASS value: %d (%s)!\n",
1082 RxContext->Info.FsInformationClass,
1083 (ULONG)RxContext->Info.FsInformationClass < RT_ELEMENTS(s_apszNames)
1084 ? s_apszNames[RxContext->Info.FsInformationClass] : "??"));
1085 Status = STATUS_INVALID_PARAMETER;
1086 RxContext->InformationToReturn = 0;
1087 break;
1088 }
1089
1090 /* Here is a weird issue I couldn't quite figure out. When working directories, I
1091 seem to get semi-random stuff back in the IO_STATUS_BLOCK when returning failures
1092 for unsupported classes. The difference between directories and files seemed to
1093 be the IRP_SYNCHRONOUS_API flag. Poking around a little bit more, the UserIosb
1094 seems to be a ring-0 stack address rather than the usermode one and
1095 IopSynchronousApiServiceTail being used for copying it back to user mode because
1096 the handle wasn't synchronous or something.
1097
1098 So, the following is kludge to make the IOS values 0,0 like FAT does it. The
1099 real fix for this escapes me, but this should do the trick for now... */
1100 PIRP pIrp = RxContext->CurrentIrp;
1101 if ( pIrp
1102 && (pIrp->Flags & IRP_SYNCHRONOUS_API)
1103 && RTR0MemKernelIsValidAddr(pIrp->UserIosb))
1104 {
1105 Log2(("VBOXSF: VBoxMRxQueryVolumeInfo: IRP_SYNCHRONOUS_API hack: Setting UserIosb (%p) values!\n", pIrp->UserIosb));
1106 __try
1107 {
1108 pIrp->UserIosb->Status = 0;
1109 pIrp->UserIosb->Information = RxContext->InformationToReturn;
1110 }
1111 __except(EXCEPTION_EXECUTE_HANDLER)
1112 {
1113#ifdef LOG_ENABLED
1114 NTSTATUS rcNt = GetExceptionCode();
1115 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: Oops %#x accessing %p\n", rcNt, pIrp->UserIosb));
1116#endif
1117 }
1118 }
1119 Log(("VBOXSF: VBoxMRxQueryVolumeInfo: Returned %#010x\n", Status));
1120 return Status;
1121}
1122
1123
1124/*********************************************************************************************************************************
1125* VBoxMRxQueryFileInfo *
1126*********************************************************************************************************************************/
1127
1128/**
1129 * Updates the FCBs copy of the file size.
1130 *
1131 * The RDBSS is using the file size from the FCB in a few places without giving
1132 * us the chance to make sure that the value is up to date and properly
1133 * reflecting the size of the actual file on the host. Thus this mess to try
1134 * keep the the size up to date where ever possible as well as some hacks to
1135 * bypass RDBSS' use of the FCB file size. (And no, we cannot just make the
1136 * FCB_STATE_FILESIZECACHEING_ENABLED flag isn't set, because it was never
1137 * implemented.)
1138 *
1139 * @param pFileObj The file object.
1140 * @param pFcb The FCB.
1141 * @param pVBoxFobX Out file object extension data.
1142 * @param cbFileNew The new file size.
1143 * @param cbFileOld The old file size from the FCB/RDBSS.
1144 * @param cbAllocated The allocated size for the file, -1 if not
1145 * available.
1146 *
1147 * @note Will acquire the paging I/O resource lock in exclusive mode. Caller
1148 * must not be holding it in shared mode.
1149 */
1150void vbsfNtUpdateFcbSize(PFILE_OBJECT pFileObj, PMRX_FCB pFcb, PMRX_VBOX_FOBX pVBoxFobX,
1151 LONGLONG cbFileNew, LONGLONG cbFileOld, LONGLONG cbAllocated)
1152{
1153 Assert(cbFileNew != cbFileOld);
1154 Assert(cbFileNew >= 0);
1155 Assert( !ExIsResourceAcquiredSharedLite(pFcb->Header.PagingIoResource)
1156 || ExIsResourceAcquiredExclusiveLite(pFcb->Header.PagingIoResource));
1157
1158 /*
1159 * Lock the paging I/O resources before trying to modify the header variables.
1160 *
1161 * Note! RxAcquirePagingIoResource and RxReleasePagingIoResource are unsafe
1162 * macros in need of {} wrappers when used with if statements.
1163 */
1164 NTSTATUS rcNtLock = RxAcquirePagingIoResource(NULL, pFcb);
1165
1166 LONGLONG cbFileOldRecheck;
1167 RxGetFileSizeWithLock((PFCB)pFcb, &cbFileOldRecheck);
1168 if (cbFileOldRecheck == cbFileOld)
1169 {
1170 LONGLONG cbFileNewCopy = cbFileNew;
1171 RxSetFileSizeWithLock((PFCB)pFcb, &cbFileNewCopy);
1172
1173 /* The valid data length is the same as the file size for us. */
1174 if (pFcb->Header.ValidDataLength.QuadPart != cbFileNew)
1175 pFcb->Header.ValidDataLength.QuadPart = cbFileNew;
1176
1177 /* The allocation size must be larger or equal to the file size says https://www.osronline.com/article.cfm%5Eid=167.htm . */
1178 if (cbAllocated >= cbFileNew)
1179 {
1180 if (pFcb->Header.AllocationSize.QuadPart != cbAllocated)
1181 pFcb->Header.AllocationSize.QuadPart = cbAllocated;
1182 }
1183 else if (pFcb->Header.AllocationSize.QuadPart < cbFileNew)
1184 pFcb->Header.AllocationSize.QuadPart = cbFileNew;
1185
1186 /* Update our copy. */
1187 pVBoxFobX->Info.cbObject = cbFileNew;
1188 if (cbAllocated >= 0)
1189 pVBoxFobX->Info.cbAllocated = cbAllocated;
1190
1191 /*
1192 * Tell the cache manager if we can.
1193 *
1194 * According to the MSDN documentation, we must update the cache manager when
1195 * the file size changes, allocation size increases, valid data length descreases,
1196 * and when a non-cached I/O operation increases the valid data length.
1197 */
1198 SECTION_OBJECT_POINTERS *pSectPtrs = pFileObj->SectionObjectPointer;
1199 if (pSectPtrs)
1200 {
1201 LARGE_INTEGER NewSize;
1202 NewSize.QuadPart = cbFileNew;
1203 if ( cbFileNew >= cbFileOld
1204 || MmCanFileBeTruncated(pSectPtrs, &NewSize)) /** @todo do we need to check this? */
1205 {
1206 CC_FILE_SIZES FileSizes;
1207 FileSizes.AllocationSize = pFcb->Header.AllocationSize;
1208 FileSizes.FileSize.QuadPart = cbFileNew;
1209 FileSizes.ValidDataLength.QuadPart = cbFileNew;
1210
1211
1212 /* RDBSS leave the lock before calling CcSetFileSizes, so we do that too then.*/
1213 if (NT_SUCCESS(rcNtLock))
1214 {
1215 RxReleasePagingIoResource(NULL, pFcb);
1216 }
1217
1218 __try
1219 {
1220 CcSetFileSizes(pFileObj, &FileSizes);
1221 }
1222 __except(EXCEPTION_EXECUTE_HANDLER)
1223 {
1224#ifdef LOG_ENABLED
1225 NTSTATUS rcNt = GetExceptionCode();
1226 Log(("vbsfNtUpdateFcbSize: CcSetFileSizes -> %#x\n", rcNt));
1227#endif
1228 return;
1229 }
1230 Log2(("vbsfNtUpdateFcbSize: Updated Size+VDL from %#RX64 to %#RX64; Alloc %#RX64\n",
1231 cbFileOld, cbFileNew, FileSizes.AllocationSize));
1232 return;
1233 }
1234 /** @todo should we flag this so we can try again later? */
1235 }
1236
1237 Log2(("vbsfNtUpdateFcbSize: Updated sizes: cb=%#RX64 VDL=%#RX64 Alloc=%#RX64 (old cb=#RX64)\n",
1238 pFcb->Header.FileSize.QuadPart, pFcb->Header.ValidDataLength.QuadPart, pFcb->Header.AllocationSize.QuadPart, cbFileOld));
1239 }
1240 else
1241 Log(("vbsfNtUpdateFcbSize: Seems we raced someone updating the file size: old size = %#RX64, new size = %#RX64, current size = %#RX64\n",
1242 cbFileOld, cbFileNew, cbFileOldRecheck));
1243
1244 if (NT_SUCCESS(rcNtLock))
1245 {
1246 RxReleasePagingIoResource(NULL, pFcb); /* requires {} */
1247 }
1248}
1249
1250
1251/**
1252 * Updates the object info to the VBox file object extension data.
1253 *
1254 * @param pVBoxFobX The VBox file object extension data.
1255 * @param pObjInfo The fresh data from the host. Okay to modify.
1256 * @param pVBoxFcbX The VBox FCB extension data.
1257 * @param fTimestampsToCopyAnyway VBOX_FOBX_F_INFO_XXX mask of timestamps to
1258 * copy regardless of their suppressed state.
1259 * This is used by the info setter function to
1260 * get current copies of newly modified and
1261 * suppressed fields.
1262 * @param pFileObj Pointer to the file object if we should
1263 * update the cache manager, otherwise NULL.
1264 * @param pFcb Pointer to the FCB if we should update its
1265 * copy of the file size, NULL if we should
1266 * leave it be. Must be NULL when pFileObj is.
1267 */
1268static void vbsfNtCopyInfo(PMRX_VBOX_FOBX pVBoxFobX, PSHFLFSOBJINFO pObjInfo, PVBSFNTFCBEXT pVBoxFcbX,
1269 uint8_t fTimestampsToCopyAnyway, PFILE_OBJECT pFileObj, PMRX_FCB pFcb)
1270{
1271 LogFlow(("vbsfNtCopyInfo: hFile=%#RX64 pVBoxFobX=%p\n", pVBoxFobX->hFile, pVBoxFobX));
1272 uint64_t const nsNow = RTTimeSystemNanoTS();
1273
1274 /*
1275 * Check if the size changed because RDBSS and the cache manager have
1276 * cached copies of the file and allocation sizes.
1277 */
1278 if (pFcb && pFileObj)
1279 {
1280 LONGLONG cbFileRdbss;
1281 RxGetFileSizeWithLock((PFCB)pFcb, &cbFileRdbss);
1282 if (pObjInfo->cbObject != cbFileRdbss)
1283 vbsfNtUpdateFcbSize(pFileObj, pFcb, pVBoxFobX, pObjInfo->cbObject, cbFileRdbss, pObjInfo->cbAllocated);
1284 }
1285
1286 /*
1287 * Check if the modified timestamp changed and try guess if it was the host.
1288 */
1289 /** @todo use modification timestamp to detect host changes? We do on linux. */
1290
1291 /*
1292 * Copy the object info over. To simplify preserving the value of timestamps
1293 * which implict updating is currently disabled, copy them over to the source
1294 * structure before preforming the copy.
1295 */
1296 Assert((pVBoxFobX->fTimestampsSetByUser & ~pVBoxFobX->fTimestampsUpdatingSuppressed) == 0);
1297 uint8_t fCopyTs = pVBoxFobX->fTimestampsUpdatingSuppressed & ~fTimestampsToCopyAnyway;
1298 if (fCopyTs)
1299 {
1300 if ( (fCopyTs & VBOX_FOBX_F_INFO_LASTACCESS_TIME)
1301 && pVBoxFcbX->pFobxLastAccessTime == pVBoxFobX)
1302 pObjInfo->AccessTime = pVBoxFobX->Info.AccessTime;
1303
1304 if ( (fCopyTs & VBOX_FOBX_F_INFO_LASTWRITE_TIME)
1305 && pVBoxFcbX->pFobxLastWriteTime == pVBoxFobX)
1306 pObjInfo->ModificationTime = pVBoxFobX->Info.ModificationTime;
1307
1308 if ( (fCopyTs & VBOX_FOBX_F_INFO_CHANGE_TIME)
1309 && pVBoxFcbX->pFobxChangeTime == pVBoxFobX)
1310 pObjInfo->ChangeTime = pVBoxFobX->Info.ChangeTime;
1311 }
1312 pVBoxFobX->Info = *pObjInfo;
1313 pVBoxFobX->nsUpToDate = nsNow;
1314}
1315
1316/**
1317 * Queries the current file stats from the host and updates the RDBSS' copy of
1318 * the file size if necessary.
1319 *
1320 * @returns IPRT status code
1321 * @param pNetRootX Our net root extension data.
1322 * @param pFileObj The file object.
1323 * @param pVBoxFobX Our file object extension data.
1324 * @param pFcb The FCB.
1325 * @param pVBoxFcbX Our FCB extension data.
1326 */
1327int vbsfNtQueryAndUpdateFcbSize(PMRX_VBOX_NETROOT_EXTENSION pNetRootX, PFILE_OBJECT pFileObj,
1328 PMRX_VBOX_FOBX pVBoxFobX, PMRX_FCB pFcb, PVBSFNTFCBEXT pVBoxFcbX)
1329{
1330 VBOXSFOBJINFOREQ *pReq = (VBOXSFOBJINFOREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq));
1331 AssertReturn(pReq, VERR_NO_MEMORY);
1332
1333 int vrc = VbglR0SfHostReqQueryObjInfo(pNetRootX->map.root, pReq, pVBoxFobX->hFile);
1334 if (RT_SUCCESS(vrc))
1335 vbsfNtCopyInfo(pVBoxFobX, &pReq->ObjInfo, pVBoxFcbX, 0, pFileObj, pFcb);
1336 else
1337 AssertMsgFailed(("vrc=%Rrc\n", vrc));
1338
1339 VbglR0PhysHeapFree(pReq);
1340 return vrc;
1341}
1342
1343/**
1344 * Handle NtQueryInformationFile and similar requests.
1345 *
1346 * The RDBSS code has done various things before we get here wrt locking and
1347 * request pre-processing. Unless this is a paging file (FCB_STATE_PAGING_FILE)
1348 * or FileNameInformation is being queried, the FCB is locked. For all except
1349 * for FileCompressionInformation, a shared FCB access (FCB.Header.Resource) is
1350 * acquired, where as for FileCompressionInformation it is taken exclusively.
1351 */
1352NTSTATUS VBoxMRxQueryFileInfo(IN PRX_CONTEXT RxContext)
1353{
1354 RxCaptureFcb;
1355 RxCaptureFobx;
1356 NTSTATUS Status = STATUS_SUCCESS;
1357 PMRX_VBOX_NETROOT_EXTENSION pNetRootExtension = VBoxMRxGetNetRootExtension(capFcb->pNetRoot);
1358 PMRX_VBOX_FOBX pVBoxFobx = VBoxMRxGetFileObjectExtension(capFobx);
1359 ULONG cbToCopy = 0;
1360
1361 Log(("VBOXSF: VBoxMRxQueryFileInfo: Buffer = %p, Length = %x (%d) bytes, FileInformationClass = %d\n",
1362 RxContext->Info.Buffer, RxContext->Info.Length, RxContext->Info.Length, RxContext->Info.FileInformationClass));
1363
1364 AssertReturn(pVBoxFobx, STATUS_INVALID_PARAMETER);
1365 AssertReturn(RxContext->Info.Buffer, STATUS_INVALID_PARAMETER);
1366
1367#define CHECK_SIZE_BREAK(a_RxContext, a_cbNeeded) \
1368 /* IO_STACK_LOCATION::Parameters::SetFile::Length is signed, the RxContext bugger is LONG. See end of function for why. */ \
1369 if ((ULONG)(a_RxContext)->Info.Length >= (a_cbNeeded)) \
1370 { /*likely */ } \
1371 else if (1) { Status = STATUS_BUFFER_TOO_SMALL; break; } else do { } while (0)
1372
1373 switch (RxContext->Info.FileInformationClass)
1374 {
1375 /*
1376 * Queries we can satisfy without calling the host:
1377 */
1378
1379 case FileNamesInformation:
1380 {
1381 PFILE_NAMES_INFORMATION pInfo = (PFILE_NAMES_INFORMATION)RxContext->Info.Buffer;
1382 PUNICODE_STRING FileName = GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext);
1383 Log(("VBOXSF: VBoxMRxQueryFileInfo: FileNamesInformation\n"));
1384
1385 cbToCopy = RT_UOFFSETOF_DYN(FILE_NAMES_INFORMATION, FileName[FileName->Length / 2 + 1]);
1386 CHECK_SIZE_BREAK(RxContext, cbToCopy);
1387
1388 pInfo->NextEntryOffset = 0;
1389 pInfo->FileIndex = 0;
1390 pInfo->FileNameLength = FileName->Length;
1391
1392 RtlCopyMemory(pInfo->FileName, FileName->Buffer, FileName->Length);
1393 pInfo->FileName[FileName->Length] = 0;
1394 break;
1395 }
1396
1397 case FileInternalInformation:
1398 {
1399 PFILE_INTERNAL_INFORMATION pInfo = (PFILE_INTERNAL_INFORMATION)RxContext->Info.Buffer;
1400 Log(("VBOXSF: VBoxMRxQueryFileInfo: FileInternalInformation\n"));
1401
1402 cbToCopy = sizeof(FILE_INTERNAL_INFORMATION);
1403 CHECK_SIZE_BREAK(RxContext, cbToCopy);
1404
1405 /* A 8-byte file reference number for the file. */
1406 pInfo->IndexNumber.QuadPart = (ULONG_PTR)capFcb;
1407 break;
1408 }
1409
1410 case FileEaInformation:
1411 {
1412 PFILE_EA_INFORMATION pInfo = (PFILE_EA_INFORMATION)RxContext->Info.Buffer;
1413 Log(("VBOXSF: VBoxMRxQueryFileInfo: FileEaInformation\n"));
1414
1415 cbToCopy = sizeof(FILE_EA_INFORMATION);
1416 CHECK_SIZE_BREAK(RxContext, cbToCopy);
1417
1418 pInfo->EaSize = 0;
1419 break;
1420 }
1421
1422 case FileStreamInformation:
1423 Log(("VBOXSF: VBoxMRxQueryFileInfo: FileStreamInformation: not supported\n"));
1424 Status = STATUS_INVALID_PARAMETER;
1425 break;
1426
1427 case FileAlternateNameInformation:
1428 Log(("VBOXSF: VBoxMRxQueryFileInfo: FileStreamInformation: not implemented\n"));
1429 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1430 break;
1431
1432 case FileNumaNodeInformation:
1433 Log(("VBOXSF: VBoxMRxQueryFileInfo: FileNumaNodeInformation: not supported\n"));
1434 Status = STATUS_NO_SUCH_DEVICE; /* what's returned on a samba share */
1435 break;
1436
1437 case FileStandardLinkInformation:
1438 Log(("VBOXSF: VBoxMRxQueryFileInfo: FileStandardLinkInformation: not supported\n"));
1439 Status = STATUS_NOT_SUPPORTED; /* what's returned on a samba share */
1440 break;
1441
1442 /*
1443 * Queries where we need info from the host.
1444 *
1445 * For directories we don't necessarily go to the host but use info from when we
1446 * opened the them, why we do this is a little unclear as all the clues that r9630
1447 * give is "fixes".
1448 *
1449 * The TTL here works around two issues in particular:
1450 *
1451 * 1. We don't want to go to the host three times during a
1452 * FileAllInformation query (RDBSS splits it up).
1453 *
1454 * 2. There are several filter drivers which will query info at the end of the
1455 * IRP_MJ_CREATE processing. On a W10 guest here, FileFinder.sys (belived to
1456 * be related to the prefetcher) first queries FileStandardInformation, then
1457 * WdFilter.sys (Windows Defender) will query FileBasicInformation,
1458 * FileStandardInformation and (not relevant here) FileInternalInformation.
1459 * It would be complete waste of time to requery the data from the host for
1460 * each of the three queries.
1461 *
1462 * The current hardcoded 100us value was choosen by experimentation with FsPerf
1463 * on a decent intel system (6700K). This is however subject to the timer tick
1464 * granularity on systems without KeQueryInterruptTimePrecise (i.e. pre win8).
1465 *
1466 * Note! We verify the buffer size after talking to the host, assuming that there
1467 * won't be a problem and saving an extra switch statement. IIRC the
1468 * NtQueryInformationFile code verifies the sizes too.
1469 */
1470 /** @todo r=bird: install a hack so we get FileAllInformation directly up here
1471 * rather than 5 individual queries. We may end up going 3 times to
1472 * the host (depending on the TTL hack) to fetch the same info over
1473 * and over again. */
1474 case FileEndOfFileInformation:
1475 case FileAllocationInformation:
1476 case FileBasicInformation:
1477 case FileStandardInformation:
1478 case FileNetworkOpenInformation:
1479 case FileAttributeTagInformation:
1480 case FileCompressionInformation:
1481 {
1482 /* Query the information if necessary. */
1483 if ( !(pVBoxFobx->Info.Attr.fMode & RTFS_DOS_DIRECTORY) /** @todo figure out why we don't return up-to-date info for directories! */
1484 && ( !pVBoxFobx->nsUpToDate
1485 || RTTimeSystemNanoTS() - pVBoxFobx->nsUpToDate > RT_NS_100US /** @todo implement proper TTL */ ) )
1486 {
1487 PVBSFNTFCBEXT pVBoxFcbx = VBoxMRxGetFcbExtension(capFcb);
1488 AssertReturn(pVBoxFcbx, STATUS_INTERNAL_ERROR);
1489
1490 VBOXSFOBJINFOREQ *pReq = (VBOXSFOBJINFOREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq));
1491 AssertBreakStmt(pReq, Status = STATUS_NO_MEMORY);
1492
1493 int vrc = VbglR0SfHostReqQueryObjInfo(pNetRootExtension->map.root, pReq, pVBoxFobx->hFile);
1494 if (RT_SUCCESS(vrc))
1495 vbsfNtCopyInfo(pVBoxFobx, &pReq->ObjInfo, pVBoxFcbx, 0, /* ASSUMES that PageingIoResource is not */
1496 RxContext->pFobx->AssociatedFileObject, capFcb); /* held in shared mode here! */
1497 else
1498 {
1499 Status = vbsfNtVBoxStatusToNt(vrc);
1500 VbglR0PhysHeapFree(pReq);
1501 break;
1502 }
1503 VbglR0PhysHeapFree(pReq);
1504 }
1505
1506 /* Copy it into the return buffer. */
1507 switch (RxContext->Info.FileInformationClass)
1508 {
1509 case FileBasicInformation:
1510 {
1511 PFILE_BASIC_INFORMATION pInfo = (PFILE_BASIC_INFORMATION)RxContext->Info.Buffer;
1512 Log(("VBOXSF: VBoxMRxQueryFileInfo: FileBasicInformation\n"));
1513
1514 cbToCopy = sizeof(FILE_BASIC_INFORMATION);
1515 CHECK_SIZE_BREAK(RxContext, cbToCopy);
1516
1517 pInfo->CreationTime.QuadPart = RTTimeSpecGetNtTime(&pVBoxFobx->Info.BirthTime);
1518 pInfo->LastAccessTime.QuadPart = RTTimeSpecGetNtTime(&pVBoxFobx->Info.AccessTime);
1519 pInfo->LastWriteTime.QuadPart = RTTimeSpecGetNtTime(&pVBoxFobx->Info.ModificationTime);
1520 pInfo->ChangeTime.QuadPart = RTTimeSpecGetNtTime(&pVBoxFobx->Info.ChangeTime);
1521 pInfo->FileAttributes = VBoxToNTFileAttributes(pVBoxFobx->Info.Attr.fMode);
1522 Log(("VBOXSF: VBoxMRxQueryFileInfo: FileBasicInformation: File attributes: 0x%x\n",
1523 pInfo->FileAttributes));
1524 break;
1525 }
1526
1527 case FileStandardInformation:
1528 {
1529 PFILE_STANDARD_INFORMATION pInfo = (PFILE_STANDARD_INFORMATION)RxContext->Info.Buffer;
1530 Log(("VBOXSF: VBoxMRxQueryFileInfo: FileStandardInformation\n"));
1531
1532 cbToCopy = sizeof(FILE_STANDARD_INFORMATION);
1533 CHECK_SIZE_BREAK(RxContext, cbToCopy);
1534
1535 /* Note! We didn't used to set allocation size and end-of-file for directories.
1536 NTFS reports these, though, so why shouldn't we. */
1537 pInfo->AllocationSize.QuadPart = pVBoxFobx->Info.cbAllocated;
1538 pInfo->EndOfFile.QuadPart = pVBoxFobx->Info.cbObject;
1539 pInfo->NumberOfLinks = 1; /** @todo 0? */
1540 pInfo->DeletePending = FALSE;
1541 pInfo->Directory = pVBoxFobx->Info.Attr.fMode & RTFS_DOS_DIRECTORY ? TRUE : FALSE;
1542 break;
1543 }
1544
1545 case FileNetworkOpenInformation:
1546 {
1547 PFILE_NETWORK_OPEN_INFORMATION pInfo = (PFILE_NETWORK_OPEN_INFORMATION)RxContext->Info.Buffer;
1548 Log(("VBOXSF: VBoxMRxQueryFileInfo: FileNetworkOpenInformation\n"));
1549
1550 cbToCopy = sizeof(FILE_NETWORK_OPEN_INFORMATION);
1551 CHECK_SIZE_BREAK(RxContext, cbToCopy);
1552
1553 pInfo->CreationTime.QuadPart = RTTimeSpecGetNtTime(&pVBoxFobx->Info.BirthTime);
1554 pInfo->LastAccessTime.QuadPart = RTTimeSpecGetNtTime(&pVBoxFobx->Info.AccessTime);
1555 pInfo->LastWriteTime.QuadPart = RTTimeSpecGetNtTime(&pVBoxFobx->Info.ModificationTime);
1556 pInfo->ChangeTime.QuadPart = RTTimeSpecGetNtTime(&pVBoxFobx->Info.ChangeTime);
1557 /* Note! We didn't used to set allocation size and end-of-file for directories.
1558 NTFS reports these, though, so why shouldn't we. */
1559 pInfo->AllocationSize.QuadPart = pVBoxFobx->Info.cbAllocated;
1560 pInfo->EndOfFile.QuadPart = pVBoxFobx->Info.cbObject;
1561 pInfo->FileAttributes = VBoxToNTFileAttributes(pVBoxFobx->Info.Attr.fMode);
1562 break;
1563 }
1564
1565 case FileEndOfFileInformation:
1566 {
1567 PFILE_END_OF_FILE_INFORMATION pInfo = (PFILE_END_OF_FILE_INFORMATION)RxContext->Info.Buffer;
1568 Log(("VBOXSF: VBoxMRxQueryFileInfo: FileEndOfFileInformation\n"));
1569
1570 cbToCopy = sizeof(FILE_END_OF_FILE_INFORMATION);
1571 CHECK_SIZE_BREAK(RxContext, cbToCopy);
1572
1573 /* Note! We didn't used to set allocation size and end-of-file for directories.
1574 NTFS reports these, though, so why shouldn't we. */
1575 pInfo->EndOfFile.QuadPart = pVBoxFobx->Info.cbObject;
1576 break;
1577 }
1578
1579 case FileAllocationInformation:
1580 {
1581 PFILE_ALLOCATION_INFORMATION pInfo = (PFILE_ALLOCATION_INFORMATION)RxContext->Info.Buffer;
1582 Log(("VBOXSF: VBoxMRxQueryFileInfo: FileAllocationInformation\n"));
1583
1584 cbToCopy = sizeof(FILE_ALLOCATION_INFORMATION);
1585 CHECK_SIZE_BREAK(RxContext, cbToCopy);
1586
1587 /* Note! We didn't used to set allocation size and end-of-file for directories.
1588 NTFS reports these, though, so why shouldn't we. */
1589 pInfo->AllocationSize.QuadPart = pVBoxFobx->Info.cbAllocated;
1590 break;
1591 }
1592
1593 case FileAttributeTagInformation:
1594 {
1595 PFILE_ATTRIBUTE_TAG_INFORMATION pInfo = (PFILE_ATTRIBUTE_TAG_INFORMATION)RxContext->Info.Buffer;
1596 Log(("VBOXSF: VBoxMRxQueryFileInfo: FileAttributeTagInformation\n"));
1597
1598 cbToCopy = sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
1599 CHECK_SIZE_BREAK(RxContext, cbToCopy);
1600
1601 pInfo->FileAttributes = VBoxToNTFileAttributes(pVBoxFobx->Info.Attr.fMode);
1602 pInfo->ReparseTag = 0;
1603 break;
1604 }
1605
1606 case FileCompressionInformation:
1607 {
1608 //PFILE_COMPRESSION_INFO pInfo = (PFILE_COMPRESSION_INFO)RxContext->Info.Buffer;
1609 struct MY_FILE_COMPRESSION_INFO
1610 {
1611 LARGE_INTEGER CompressedFileSize;
1612 WORD CompressionFormat;
1613 UCHAR CompressionUnitShift;
1614 UCHAR ChunkShift;
1615 UCHAR ClusterShift;
1616 UCHAR Reserved[3];
1617 } *pInfo = (struct MY_FILE_COMPRESSION_INFO *)RxContext->Info.Buffer;
1618 Log(("VBOXSF: VBoxMRxQueryFileInfo: FileCompressionInformation\n"));
1619
1620 cbToCopy = sizeof(*pInfo);
1621 CHECK_SIZE_BREAK(RxContext, cbToCopy);
1622
1623 pInfo->CompressedFileSize.QuadPart = pVBoxFobx->Info.cbObject;
1624 pInfo->CompressionFormat = 0;
1625 pInfo->CompressionUnitShift = 0;
1626 pInfo->ChunkShift = 0;
1627 pInfo->ClusterShift = 0;
1628 pInfo->Reserved[0] = 0;
1629 pInfo->Reserved[1] = 0;
1630 pInfo->Reserved[2] = 0;
1631 AssertCompile(sizeof(pInfo->Reserved) == 3);
1632 break;
1633 }
1634
1635 default:
1636 AssertLogRelMsgFailed(("FileInformationClass=%d\n",
1637 RxContext->Info.FileInformationClass));
1638 Status = STATUS_INTERNAL_ERROR;
1639 break;
1640 }
1641 break;
1642 }
1643
1644
1645/** @todo Implement:
1646 * FileHardLinkInformation: rcNt=0 (STATUS_SUCCESS) Ios.Status=0 (STATUS_SUCCESS) Ios.Information=0000000000000048
1647 * FileProcessIdsUsingFileInformation: rcNt=0 (STATUS_SUCCESS) Ios.Status=0 (STATUS_SUCCESS) Ios.Information=0000000000000010
1648 * FileNormalizedNameInformation: rcNt=0 (STATUS_SUCCESS) Ios.Status=0 (STATUS_SUCCESS) Ios.Information=00000000000000AA
1649 * => See during MoveFileEx call on W10.
1650 * FileNetworkPhysicalNameInformation: rcNt=0xc000000d (STATUS_INVALID_PARAMETER) Ios={not modified}
1651 * FileShortNameInformation?
1652 * FileNetworkPhysicalNameInformation
1653 */
1654
1655 /*
1656 * Unsupported ones (STATUS_INVALID_PARAMETER is correct here if you
1657 * go by what fat + ntfs return, however samba mounts generally returns
1658 * STATUS_INVALID_INFO_CLASS except for pipe info - see queryfileinfo-1).
1659 */
1660 default:
1661 Log(("VBOXSF: VBoxMRxQueryFileInfo: Not supported FileInformationClass: %d!\n",
1662 RxContext->Info.FileInformationClass));
1663 Status = STATUS_INVALID_PARAMETER;
1664 break;
1665
1666 }
1667#undef CHECK_SIZE_BREAK
1668
1669 /* Note! InformationToReturn doesn't seem to be used, instead Info.LengthRemaining should underflow
1670 so it can be used together with RxContext->CurrentIrpSp->Parameters.QueryFile.Length
1671 to calc the Ios.Information value. This explain the weird LONG type choice. */
1672 RxContext->InformationToReturn = cbToCopy;
1673 RxContext->Info.LengthRemaining -= cbToCopy;
1674 AssertStmt(RxContext->Info.LengthRemaining >= 0 || Status != STATUS_SUCCESS, Status = STATUS_BUFFER_TOO_SMALL);
1675
1676 Log(("VBOXSF: VBoxMRxQueryFileInfo: Returns %#x, Remaining length = %d, cbToCopy = %u (%#x)\n",
1677 Status, RxContext->Info.Length, cbToCopy));
1678 return Status;
1679}
1680
1681
1682/*********************************************************************************************************************************
1683* VBoxMRxSetFileInfo *
1684*********************************************************************************************************************************/
1685
1686/**
1687 * Worker for VBoxMRxSetFileInfo.
1688 */
1689static NTSTATUS vbsfNtSetBasicInfo(PMRX_VBOX_NETROOT_EXTENSION pNetRootExtension, PFILE_OBJECT pFileObj, PMRX_VBOX_FOBX pVBoxFobx,
1690 PMRX_FCB pFcb, PVBSFNTFCBEXT pVBoxFcbx, PFILE_BASIC_INFORMATION pBasicInfo)
1691{
1692 Log(("VBOXSF: MRxSetFileInfo: FileBasicInformation: CreationTime %RX64\n", pBasicInfo->CreationTime.QuadPart));
1693 Log(("VBOXSF: MRxSetFileInfo: FileBasicInformation: LastAccessTime %RX64\n", pBasicInfo->LastAccessTime.QuadPart));
1694 Log(("VBOXSF: MRxSetFileInfo: FileBasicInformation: LastWriteTime %RX64\n", pBasicInfo->LastWriteTime.QuadPart));
1695 Log(("VBOXSF: MRxSetFileInfo: FileBasicInformation: ChangeTime %RX64\n", pBasicInfo->ChangeTime.QuadPart));
1696 Log(("VBOXSF: MRxSetFileInfo: FileBasicInformation: FileAttributes %RX32\n", pBasicInfo->FileAttributes));
1697 AssertReturn(pVBoxFobx, STATUS_INTERNAL_ERROR);
1698 AssertReturn(pVBoxFcbx, STATUS_INTERNAL_ERROR);
1699 AssertReturn(pNetRootExtension, STATUS_INTERNAL_ERROR);
1700
1701 /** @todo r=bird: The attempt at implementing the disable-timestamp-update
1702 * behaviour here needs a little adjusting. I'll get to that later.
1703 *
1704 * Reminders:
1705 *
1706 * X1. Drop VBOX_FOBX_F_INFO_CREATION_TIME.
1707 *
1708 * X2. Drop unused VBOX_FOBX_F_INFO_ATTRIBUTES.
1709 *
1710 * X3. Only act on VBOX_FOBX_F_INFO_CHANGE_TIME if modified attributes or grown
1711 * the file (?) so we don't cancel out updates by other parties (like the
1712 * host).
1713 *
1714 * X4. Only act on VBOX_FOBX_F_INFO_LASTWRITE_TIME if we've written to the
1715 * file.
1716 *
1717 * X5. Only act on VBOX_FOBX_F_INFO_LASTACCESS_TIME if we've read from the file
1718 * or done whatever else might modify the access time.
1719 *
1720 * 6. Don't bother calling the host if there are only zeros and -1 values.
1721 * => Not done / better use it to update FCB info?
1722 *
1723 * X7. Client application should probably be allowed to modify the timestamps
1724 * explicitly using this API after disabling updating, given the wording of
1725 * the footnote referenced above.
1726 * => Only verified via fastfat sample, need FsPerf test.
1727 *
1728 * 8. Extend the host interface to let the host handle this crap instead as it
1729 * can do a better job, like on windows it's done implicitly if we let -1
1730 * pass thru IPRT.
1731 * => We're actually better equipped to handle it than the host, given the
1732 * FCB/inode. New plan is to detect windows host and let it implement -1,
1733 * but use the old stuff as fallback for non-windows hosts.
1734 *
1735 * One worry here is that we hide timestamp updates made by the host or other
1736 * guest side processes. This could account for some of the issues we've been
1737 * having with the guest not noticing host side changes.
1738 */
1739
1740
1741 /*
1742 * The properties that need to be changed are set to something other
1743 * than zero and -1. (According to the fastfat sample code, -1 only
1744 * disable implicit timestamp updating, not explicit thru this code.)
1745 */
1746
1747 /*
1748 * In the host request, zero values are ignored.
1749 *
1750 * As for the NT request, the same is true but with a slight twist for the
1751 * timestamp fields. If a timestamp value is non-zero, the client disables
1752 * implicit updating of that timestamp via this handle when reading, writing
1753 * and * changing attributes. The special -1 value is used to just disable
1754 * implicit updating without modifying the timestamp. While the value is
1755 * allowed for the CreationTime field, it will be treated as zero.
1756 *
1757 * More clues to the NT behaviour can be found here:
1758 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/16023025-8a78-492f-8b96-c873b042ac50
1759 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/d4bc551b-7aaf-4b4f-ba0e-3a75e7c528f0#Appendix_A_86
1760 *
1761 * P.S. One of the reasons behind suppressing of timestamp updating after setting
1762 * them is likely related to the need of opening objects to modify them. There are
1763 * no utimes() or chmod() function in NT, on the futimes() and fchmod() variants.
1764 */
1765 VBOXSFOBJINFOREQ *pReq = (VBOXSFOBJINFOREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq));
1766 if (pReq)
1767 RT_ZERO(pReq->ObjInfo);
1768 else
1769 return STATUS_INSUFFICIENT_RESOURCES;
1770 uint32_t fModified = 0;
1771 uint32_t fSuppressed = 0;
1772
1773 /** @todo FsPerf need to check what is supposed to happen if modified
1774 * against after -1 is specified. As state above, fastfat will not suppress
1775 * further setting of the timestamp like we used to do prior to revision
1776 * r130337 or thereabouts. */
1777
1778 if (pBasicInfo->CreationTime.QuadPart && pBasicInfo->CreationTime.QuadPart != -1)
1779 RTTimeSpecSetNtTime(&pReq->ObjInfo.BirthTime, pBasicInfo->CreationTime.QuadPart);
1780
1781 if (pBasicInfo->LastAccessTime.QuadPart)
1782 {
1783 if (pBasicInfo->LastAccessTime.QuadPart != -1)
1784 {
1785 RTTimeSpecSetNtTime(&pReq->ObjInfo.AccessTime, pBasicInfo->LastAccessTime.QuadPart);
1786 fModified |= VBOX_FOBX_F_INFO_LASTACCESS_TIME;
1787 }
1788 fSuppressed |= VBOX_FOBX_F_INFO_LASTACCESS_TIME;
1789 }
1790
1791 if (pBasicInfo->LastWriteTime.QuadPart)
1792 {
1793 if (pBasicInfo->LastWriteTime.QuadPart != -1)
1794 {
1795 RTTimeSpecSetNtTime(&pReq->ObjInfo.ModificationTime, pBasicInfo->LastWriteTime.QuadPart);
1796 fModified |= VBOX_FOBX_F_INFO_LASTWRITE_TIME;
1797 }
1798 fSuppressed |= VBOX_FOBX_F_INFO_LASTWRITE_TIME;
1799 }
1800
1801 if (pBasicInfo->ChangeTime.QuadPart)
1802 {
1803 if (pBasicInfo->ChangeTime.QuadPart != -1)
1804 {
1805 RTTimeSpecSetNtTime(&pReq->ObjInfo.ChangeTime, pBasicInfo->ChangeTime.QuadPart);
1806 fModified |= VBOX_FOBX_F_INFO_CHANGE_TIME;
1807 }
1808 fSuppressed |= VBOX_FOBX_F_INFO_CHANGE_TIME;
1809 }
1810
1811 if (pBasicInfo->FileAttributes)
1812 {
1813 pReq->ObjInfo.Attr.fMode = NTToVBoxFileAttributes(pBasicInfo->FileAttributes);
1814 Assert(pReq->ObjInfo.Attr.fMode != 0);
1815 }
1816
1817 /*
1818 * Call the host to do the actual updating.
1819 * Note! This may be a noop, but we want up-to-date info for any -1 timestamp.
1820 */
1821 int vrc = VbglR0SfHostReqSetObjInfo(pNetRootExtension->map.root, pReq, pVBoxFobx->hFile);
1822 NTSTATUS Status;
1823 if (RT_SUCCESS(vrc))
1824 {
1825 /*
1826 * Update our timestamp state tracking both in the file object and the file
1827 * control block extensions.
1828 */
1829 if (pBasicInfo->FileAttributes || fModified)
1830 {
1831 if ( pVBoxFcbx->pFobxChangeTime != pVBoxFobx
1832 && !(pVBoxFobx->fTimestampsUpdatingSuppressed & VBOX_FOBX_F_INFO_CHANGE_TIME))
1833 pVBoxFcbx->pFobxChangeTime = NULL;
1834 pVBoxFobx->fTimestampsImplicitlyUpdated |= VBOX_FOBX_F_INFO_CHANGE_TIME;
1835 }
1836 pVBoxFobx->fTimestampsImplicitlyUpdated &= ~fModified;
1837 pVBoxFobx->fTimestampsSetByUser |= fModified;
1838 pVBoxFobx->fTimestampsUpdatingSuppressed |= fSuppressed;
1839
1840 if (fSuppressed)
1841 {
1842 if (fSuppressed & VBOX_FOBX_F_INFO_LASTACCESS_TIME)
1843 pVBoxFcbx->pFobxLastAccessTime = pVBoxFobx;
1844 if (fSuppressed & VBOX_FOBX_F_INFO_LASTWRITE_TIME)
1845 pVBoxFcbx->pFobxLastWriteTime = pVBoxFobx;
1846 if (fSuppressed & VBOX_FOBX_F_INFO_CHANGE_TIME)
1847 pVBoxFcbx->pFobxChangeTime = pVBoxFobx;
1848 }
1849
1850 vbsfNtCopyInfo(pVBoxFobx, &pReq->ObjInfo, pVBoxFcbx, fSuppressed, pFileObj, pFcb);
1851
1852 /*
1853 * Copy timestamps and attributes from the host into the return buffer to let
1854 * RDBSS update the FCB data when we return. Not sure if the FCB timestamps
1855 * are ever used for anything, but caller doesn't check for -1 so there will
1856 * be some funny/invalid timestamps in the FCB it ever does. (I seriously
1857 * doubt -1 is supposed to be there given that the FCB is shared and the -1
1858 * only applies to a given FILE_OBJECT/HANDLE.)
1859 */
1860 if (pBasicInfo->FileAttributes)
1861 pBasicInfo->FileAttributes = (pBasicInfo->FileAttributes & FILE_ATTRIBUTE_TEMPORARY)
1862 | VBoxToNTFileAttributes(pReq->ObjInfo.Attr.fMode);
1863 if (pBasicInfo->CreationTime.QuadPart)
1864 pBasicInfo->CreationTime.QuadPart = RTTimeSpecGetNtTime(&pReq->ObjInfo.BirthTime);
1865 if (pBasicInfo->LastAccessTime.QuadPart)
1866 pBasicInfo->LastAccessTime.QuadPart = RTTimeSpecGetNtTime(&pReq->ObjInfo.AccessTime);
1867 if (pBasicInfo->LastWriteTime.QuadPart)
1868 pBasicInfo->LastWriteTime.QuadPart = RTTimeSpecGetNtTime(&pReq->ObjInfo.ModificationTime);
1869 if (pBasicInfo->ChangeTime.QuadPart)
1870 pBasicInfo->ChangeTime.QuadPart = RTTimeSpecGetNtTime(&pReq->ObjInfo.ChangeTime);
1871
1872 Status = STATUS_SUCCESS;
1873 }
1874 else
1875 Status = vbsfNtVBoxStatusToNt(vrc);
1876
1877 VbglR0PhysHeapFree(pReq);
1878 return Status;
1879}
1880
1881/**
1882 * Worker for VBoxMRxSetFileInfo.
1883 */
1884static NTSTATUS vbsfNtSetEndOfFile(IN OUT struct _RX_CONTEXT * RxContext, IN uint64_t cbNewFileSize)
1885{
1886 NTSTATUS Status = STATUS_SUCCESS;
1887
1888 RxCaptureFcb;
1889 RxCaptureFobx;
1890
1891 PMRX_VBOX_NETROOT_EXTENSION pNetRootExtension = VBoxMRxGetNetRootExtension(capFcb->pNetRoot);
1892 PVBSFNTFCBEXT pVBoxFcbx = VBoxMRxGetFcbExtension(capFcb);
1893 PMRX_VBOX_FOBX pVBoxFobx = VBoxMRxGetFileObjectExtension(capFobx);
1894
1895 PSHFLFSOBJINFO pObjInfo;
1896 uint32_t cbBuffer;
1897 int vrc;
1898
1899 Log(("VBOXSF: vbsfNtSetEndOfFile: New size = %RX64\n",
1900 cbNewFileSize));
1901
1902 Assert(pVBoxFobx && pNetRootExtension);
1903
1904 cbBuffer = sizeof(SHFLFSOBJINFO);
1905 pObjInfo = (SHFLFSOBJINFO *)vbsfNtAllocNonPagedMem(cbBuffer);
1906 if (!pObjInfo)
1907 {
1908 AssertFailed();
1909 return STATUS_INSUFFICIENT_RESOURCES;
1910 }
1911
1912 RtlZeroMemory(pObjInfo, cbBuffer);
1913 pObjInfo->cbObject = cbNewFileSize;
1914
1915 vrc = VbglR0SfFsInfo(&g_SfClient, &pNetRootExtension->map, pVBoxFobx->hFile,
1916 SHFL_INFO_SET | SHFL_INFO_SIZE, &cbBuffer, (PSHFLDIRINFO)pObjInfo);
1917
1918 Log(("VBOXSF: vbsfNtSetEndOfFile: VbglR0SfFsInfo returned %Rrc\n", vrc));
1919
1920 Status = vbsfNtVBoxStatusToNt(vrc);
1921 if (Status == STATUS_SUCCESS)
1922 {
1923 pVBoxFobx->fTimestampsImplicitlyUpdated |= VBOX_FOBX_F_INFO_LASTWRITE_TIME;
1924 if (pVBoxFcbx->pFobxLastWriteTime != pVBoxFobx)
1925 pVBoxFcbx->pFobxLastWriteTime = NULL;
1926
1927 Log(("VBOXSF: vbsfNtSetEndOfFile: VbglR0SfFsInfo new allocation size = %RX64\n",
1928 pObjInfo->cbAllocated));
1929
1930 /** @todo update the file stats! */
1931 }
1932
1933 if (pObjInfo)
1934 vbsfNtFreeNonPagedMem(pObjInfo);
1935
1936 Log(("VBOXSF: vbsfNtSetEndOfFile: Returned 0x%08X\n", Status));
1937 return Status;
1938}
1939
1940/**
1941 * Worker for VBoxMRxSetFileInfo handling FileRenameInformation.
1942 *
1943 * @note Renaming files from the guest is _very_ expensive:
1944 * - 52175 ns/call on the host
1945 * - 844237 ns/call from the guest
1946 *
1947 * The explanation for this is that RTPathRename translates to a
1948 * MoveFileEx call, which ends up doing a lot more than opening the
1949 * file and setting rename information on that handle (W10):
1950 * - Opens the file.
1951 * - Queries FileAllInformation.
1952 * - Tries to open the new filename (result: 0x00000000 but not
1953 * opened by our code - weird).
1954 * - Queries FileNormalizedNameInformation (result: 0xc000000d).
1955 * - Does IOCTL_REDIR_QUERY_PATH_EX on \vboxsvr\IPC$.
1956 * - Tries to open \vboxsvr\IPC$ (result: 0xc0000016)
1957 * - Opens the parent directory.
1958 * - Queries directory info with old name as filter.
1959 * - Closes parent directory handle.
1960 * - Finally does FileRenameInformation.
1961 * - Closes the handle to the renamed file.
1962 */
1963static NTSTATUS vbsfNtRename(IN PRX_CONTEXT RxContext,
1964 IN PFILE_RENAME_INFORMATION pRenameInfo,
1965 IN ULONG cbInfo)
1966{
1967 RxCaptureFcb;
1968 RxCaptureFobx;
1969 PMRX_VBOX_NETROOT_EXTENSION pNetRootExtension = VBoxMRxGetNetRootExtension(capFcb->pNetRoot);
1970 PMRX_VBOX_FOBX pVBoxFobx = VBoxMRxGetFileObjectExtension(capFobx);
1971 PMRX_SRV_OPEN pSrvOpen = capFobx->pSrvOpen;
1972
1973 /* Make sure we've got valid buffer and filename sizes: */
1974 AssertReturn(cbInfo >= RT_UOFFSETOF(FILE_RENAME_INFORMATION, FileName), STATUS_INFO_LENGTH_MISMATCH);
1975 size_t const cbFilename = pRenameInfo->FileNameLength;
1976 AssertReturn(cbFilename < _64K - 2, STATUS_INVALID_PARAMETER);
1977 AssertReturn(cbInfo - RT_UOFFSETOF(FILE_RENAME_INFORMATION, FileName) >= cbFilename, STATUS_INFO_LENGTH_MISMATCH);
1978
1979 Log(("VBOXSF: vbsfNtRename: FileNameLength = %#x (%d), FileName = %.*ls\n",
1980 cbFilename, cbFilename, cbFilename / sizeof(WCHAR), &pRenameInfo->FileName[0]));
1981
1982/** @todo Add new function that also closes the handle, like for remove, saving a host call. */
1983
1984 /* Must close the file before renaming it! */
1985 if (pVBoxFobx->hFile != SHFL_HANDLE_NIL)
1986 {
1987 Log(("VBOXSF: vbsfNtRename: Closing handle %#RX64...\n", pVBoxFobx->hFile));
1988 vbsfNtCloseFileHandle(pNetRootExtension, pVBoxFobx, VBoxMRxGetFcbExtension(capFcb));
1989 }
1990
1991 /* Mark it as renamed, so we do nothing during close. */
1992 /** @todo r=bird: Isn't this a bit premature? */
1993 SetFlag(pSrvOpen->Flags, SRVOPEN_FLAG_FILE_RENAMED);
1994
1995 /*
1996 * Allocate a request embedding the destination string.
1997 */
1998 NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
1999 size_t const cbReq = RT_UOFFSETOF(VBOXSFRENAMEWITHSRCBUFREQ, StrDstPath.String) + cbFilename + sizeof(RTUTF16);
2000 VBOXSFRENAMEWITHSRCBUFREQ *pReq = (VBOXSFRENAMEWITHSRCBUFREQ *)VbglR0PhysHeapAlloc((uint32_t)cbReq);
2001 if (pReq)
2002 {
2003 /* The destination path string. */
2004 pReq->StrDstPath.u16Size = (uint16_t)(cbFilename + sizeof(RTUTF16));
2005 pReq->StrDstPath.u16Length = (uint16_t)cbFilename;
2006 memcpy(&pReq->StrDstPath.String, pRenameInfo->FileName, cbFilename);
2007 pReq->StrDstPath.String.utf16[cbFilename / sizeof(RTUTF16)] = '\0';
2008
2009 /* The source path string. */
2010 PUNICODE_STRING pNtSrcPath = GET_ALREADY_PREFIXED_NAME(pSrvOpen, capFcb);
2011 uint16_t const cbSrcPath = pNtSrcPath->Length;
2012 PSHFLSTRING pShflSrcPath = (PSHFLSTRING)VbglR0PhysHeapAlloc(SHFLSTRING_HEADER_SIZE + cbSrcPath + sizeof(RTUTF16));
2013 if (pShflSrcPath)
2014 {
2015 pShflSrcPath->u16Length = cbSrcPath;
2016 pShflSrcPath->u16Size = cbSrcPath + (uint16_t)sizeof(RTUTF16);
2017 memcpy(&pShflSrcPath->String, pNtSrcPath->Buffer, cbSrcPath);
2018 pShflSrcPath->String.utf16[cbSrcPath / sizeof(RTUTF16)] = '\0';
2019
2020 /*
2021 * Call the host.
2022 */
2023 uint32_t fRename = pVBoxFobx->Info.Attr.fMode & RTFS_DOS_DIRECTORY ? SHFL_RENAME_DIR : SHFL_RENAME_FILE;
2024 if (pRenameInfo->ReplaceIfExists)
2025 fRename |= SHFL_RENAME_REPLACE_IF_EXISTS;
2026 Log(("VBOXSF: vbsfNtRename: Calling VbglR0SfHostReqRenameWithSrcBuf fFlags=%#x SrcPath=%.*ls, DstPath=%.*ls\n",
2027 fRename, pShflSrcPath->u16Length / sizeof(RTUTF16), pShflSrcPath->String.utf16,
2028 pReq->StrDstPath.u16Size / sizeof(RTUTF16), pReq->StrDstPath.String.utf16));
2029 int vrc = VbglR0SfHostReqRenameWithSrcBuf(pNetRootExtension->map.root, pReq, pShflSrcPath, fRename);
2030 if (RT_SUCCESS(vrc))
2031 Status = STATUS_SUCCESS;
2032 else
2033 {
2034 Status = vbsfNtVBoxStatusToNt(vrc);
2035 Log(("VBOXSF: vbsfNtRename: VbglR0SfHostReqRenameWithSrcBuf failed with %Rrc (Status=%#x)\n", vrc, Status));
2036 }
2037
2038 VbglR0PhysHeapFree(pShflSrcPath);
2039 }
2040 VbglR0PhysHeapFree(pReq);
2041 }
2042 Log(("VBOXSF: vbsfNtRename: Returned 0x%08X\n", Status));
2043 return Status;
2044}
2045
2046/**
2047 * Handle NtSetInformationFile and similar requests.
2048 *
2049 * The RDBSS code has done various things before we get here wrt locking and
2050 * request pre-processing. It will normally acquire an exclusive FCB lock, but
2051 * not if this is related to a page file (FCB_STATE_PAGING_FILE set).
2052 */
2053NTSTATUS VBoxMRxSetFileInfo(IN PRX_CONTEXT RxContext)
2054{
2055 RxCaptureFcb;
2056 RxCaptureFobx;
2057 PMRX_VBOX_NETROOT_EXTENSION pNetRootExtension = VBoxMRxGetNetRootExtension(capFcb->pNetRoot);
2058 PMRX_VBOX_FOBX pVBoxFobx = VBoxMRxGetFileObjectExtension(capFobx);
2059 NTSTATUS Status = STATUS_SUCCESS;
2060
2061 Log(("VBOXSF: MrxSetFileInfo: Buffer = %p, Length = %#x (%d), FileInformationClass = %d\n",
2062 RxContext->Info.Buffer, RxContext->Info.Length, RxContext->Info.Length, RxContext->Info.FileInformationClass));
2063
2064 /*
2065 * The essence of the size validation table for NtSetInformationFile from w10 build 17763:
2066 * UCHAR IoCheckQuerySetFileInformation[77]:
2067 * db 28h ; 4 FileBasicInformation, w7
2068 * db 18h ; 10 FileRenameInformation, w7
2069 * db 18h ; 11 FileLinkInformation, w7
2070 * db 1 ; 13 FileDispositionInformation, w7
2071 * db 8 ; 14 FilePositionInformation, w7
2072 * db 4 ; 16 FileModeInformation,
2073 * db 8 ; 19 FileAllocationInformation, w7
2074 * db 8 ; 20 FileEndOfFileInformation, w7
2075 * db 8 ; 23 FilePipeInformation, w7
2076 * db 10h ; 25 FilePipeRemoteInformation, w7
2077 * db 8 ; 27 FileMailslotSetInformation,
2078 * db 48h ; 29 FileObjectIdInformation,
2079 * db 10h ; 30 FileCompletionInformation, - "reserved for system use"
2080 * db 18h ; 31 FileMoveClusterInformation, w7 - "reserved for system use"
2081 * db 38h ; 32 FileQuotaInformation,
2082 * db 10h ; 36 FileTrackingInformation, - "reserved for system use"
2083 * db 8 ; 39 FileValidDataLengthInformation, w7
2084 * db 8 ; 40 FileShortNameInformation, w7
2085 * db 4 ; 41 FileIoCompletionNotificationInformation, - "reserved for system use"
2086 * db 10h ; 42 FileIoStatusBlockRangeInformation, - "reserved for system use"
2087 * db 4 ; 43 FileIoPriorityHintInformation,
2088 * db 14h ; 44 FileSfioReserveInformation, - "reserved for system use"
2089 * db 10h ; 61 FileReplaceCompletionInformation,
2090 * db 4 ; 64 FileDispositionInformationEx, - Adds posix semantics and stuff.
2091 * db 18h ; 65 FileRenameInformationEx, - Adds posix semantics and stuff.
2092 * db 8 ; 67 FileDesiredStorageClassInformation,
2093 * db 10h ; 69 FileMemoryPartitionInformation, - "reserved for system use", W10-1709
2094 * db 4 ; 71 FileCaseSensitiveInformation, - Per dir case sensitivity. (For linux?)
2095 * db 18h ; 72 FileLinkInformationEx, - Adds posix semantics and stuff.
2096 * db 4 ; 74 FileStorageReserveIdInformation,
2097 * db 4 ; 75 FileCaseSensitiveInformationForceAccessCheck, - for the i/o manager, w10-1809.
2098 *
2099 * Note! Using WDK 7600.16385.1/wnet, we're limited in what gets passed along, unknown
2100 * stuff will be rejected with STATUS_INVALID_PARAMETER and never get here. OTOH,
2101 * the 10.00.16299.0 WDK will forward anything it doesn't know from what I can tell.
2102 * Not sure exactly when this changed.
2103 */
2104 switch ((int)RxContext->Info.FileInformationClass)
2105 {
2106 /*
2107 * This is used to modify timestamps and attributes.
2108 *
2109 * Upon successful return, RDBSS will ensure that FILE_ATTRIBUTE_DIRECTORY is set
2110 * according to the FCB object type (see RxFinishFcbInitialization in path.cpp),
2111 * and that the FILE_ATTRIBUTE_TEMPORARY attribute is reflected in FcbState
2112 * (FCB_STATE_TEMPORARY) and the file object flags (FO_TEMPORARY_FILE). It will
2113 * also copy each non-zero timestamp into the FCB and set the corresponding
2114 * FOBX_FLAG_USER_SET_xxxx flag in the FOBX.
2115 *
2116 * RDBSS behaviour is idential between 16299.0/w10 and 7600.16385.1/wnet.
2117 */
2118 case FileBasicInformation:
2119 {
2120 Assert(RxContext->Info.Length >= sizeof(FILE_BASIC_INFORMATION));
2121 Status = vbsfNtSetBasicInfo(pNetRootExtension, RxContext->pFobx->AssociatedFileObject, pVBoxFobx, capFcb,
2122 VBoxMRxGetFcbExtension(capFcb), (PFILE_BASIC_INFORMATION)RxContext->Info.Buffer);
2123 break;
2124 }
2125
2126 /*
2127 * This is used to rename a file.
2128 */
2129 case FileRenameInformation:
2130 {
2131#ifdef LOG_ENABLED
2132 PFILE_RENAME_INFORMATION pInfo = (PFILE_RENAME_INFORMATION)RxContext->Info.Buffer;
2133 Log(("VBOXSF: MrxSetFileInfo: FileRenameInformation: ReplaceIfExists = %d, RootDirectory = 0x%x = [%.*ls]\n",
2134 pInfo->ReplaceIfExists, pInfo->RootDirectory, pInfo->FileNameLength / sizeof(WCHAR), pInfo->FileName));
2135#endif
2136
2137 Status = vbsfNtRename(RxContext, (PFILE_RENAME_INFORMATION)RxContext->Info.Buffer, RxContext->Info.Length);
2138 break;
2139 }
2140
2141 /*
2142 * This is presumably used for hardlinking purposes. We don't support that.
2143 */
2144 case FileLinkInformation:
2145 {
2146#ifdef LOG_ENABLED
2147 PFILE_LINK_INFORMATION pInfo = (PFILE_LINK_INFORMATION )RxContext->Info.Buffer;
2148 Log(("VBOXSF: MrxSetFileInfo: FileLinkInformation: ReplaceIfExists = %d, RootDirectory = 0x%x = [%.*ls]. Not implemented!\n",
2149 pInfo->ReplaceIfExists, pInfo->RootDirectory, pInfo->FileNameLength / sizeof(WCHAR), pInfo->FileName));
2150#endif
2151
2152 Status = STATUS_NOT_IMPLEMENTED;
2153 break;
2154 }
2155
2156 /*
2157 * This is used to delete file.
2158 */
2159 case FileDispositionInformation:
2160 {
2161 PFILE_DISPOSITION_INFORMATION pInfo = (PFILE_DISPOSITION_INFORMATION)RxContext->Info.Buffer;
2162 Log(("VBOXSF: MrxSetFileInfo: FileDispositionInformation: Delete = %d\n",
2163 pInfo->DeleteFile));
2164
2165 if (pInfo->DeleteFile && capFcb->OpenCount == 1)
2166 Status = vbsfNtRemove(RxContext);
2167 else
2168 Status = STATUS_SUCCESS;
2169 break;
2170 }
2171
2172 /*
2173 * The file position is handled by the RDBSS library (RxSetPositionInfo)
2174 * and we should never see this request.
2175 */
2176 case FilePositionInformation:
2177 AssertMsgFailed(("VBOXSF: MrxSetFileInfo: FilePositionInformation: CurrentByteOffset = 0x%RX64. Unsupported!\n",
2178 ((PFILE_POSITION_INFORMATION)RxContext->Info.Buffer)->CurrentByteOffset.QuadPart));
2179 Status = STATUS_INTERNAL_ERROR;
2180 break;
2181
2182 /*
2183 * Change the allocation size, leaving the EOF alone unless the file shrinks.
2184 *
2185 * There is no shared folder operation for this, so we only need to care
2186 * about adjusting EOF if the file shrinks.
2187 *
2188 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/d4bc551b-7aaf-4b4f-ba0e-3a75e7c528f0#Appendix_A_83
2189 *
2190 * Note! The RDBSS caller, RxSetAllocationInfo, will always update the
2191 * AllocationSize field of the FCB header before calling us. If
2192 * the change is perceived to be truncating the file (new alloc
2193 * size smaller than cached file size from header), the FileSize
2194 * and (probably also the) ValidateDataLength FCB fields will be
2195 * modified as well _before_ we're called.
2196 *
2197 * Therefore, we cannot use the file size from the FCB to determin
2198 * whether it's okay to skip the EOF setting host call or not, we
2199 * must use our own cached file size value. (Cause of broken test
2200 * of opening w/ truncation.)
2201 *
2202 * P.S. When opening a file with the TRUNCATE_EXISTING disposition,
2203 * kernel32.dll translate it to FILE_OPEN and do the truncating
2204 * separately with a set FileAllocationInformation operation (no
2205 * EOF or VDL setting).
2206 */
2207 case FileAllocationInformation:
2208 {
2209 PFILE_ALLOCATION_INFORMATION pInfo = (PFILE_ALLOCATION_INFORMATION)RxContext->Info.Buffer;
2210 Log(("VBOXSF: MrxSetFileInfo: FileAllocationInformation: new AllocSize = 0x%RX64, FileSize = 0x%RX64\n",
2211 pInfo->AllocationSize.QuadPart, capFcb->Header.FileSize.QuadPart));
2212
2213 if (pInfo->AllocationSize.QuadPart >= pVBoxFobx->Info.cbObject)
2214 Status = STATUS_SUCCESS;
2215 else
2216 {
2217 /** @todo get up to date EOF from host? We may risk accidentally growing the
2218 * file here if the host (or someone else) truncated it. */
2219 Status = vbsfNtSetEndOfFile(RxContext, pInfo->AllocationSize.QuadPart);
2220 }
2221 break;
2222 }
2223
2224 /*
2225 * Prior to calling us, RxSetEndOfFileInfo will have updated the FCB fields space.FileSize,
2226 * Header.AllocationSize and (if old value was larger) Header.ValidDataLength. On success
2227 * it will inform the cache manager, while on failure the old values will be restored.
2228 *
2229 * Note! RxSetEndOfFileInfo assumes that the old Header.FileSize value is up to date and
2230 * will hide calls which does not change the size from us. This is of course not
2231 * the case for non-local file systems, as the server is the only which up-to-date
2232 * information.
2233 *
2234 * We work around this either by modifying FCB.Header.FileSize slightly when it equals
2235 * the new size. This is either done below in the FileEndOfFileInformation + 4096 case,
2236 * or when using older WDK libs in VBoxHookMjSetInformation. The FCB is locked
2237 * exclusivly while we operate with the incorrect Header.FileSize value, which should
2238 * prevent anyone else from making use of it till it has been updated again.
2239 *
2240 */
2241 case FileEndOfFileInformation:
2242 {
2243 PFILE_END_OF_FILE_INFORMATION pInfo = (PFILE_END_OF_FILE_INFORMATION)RxContext->Info.Buffer;
2244 Log(("VBOXSF: MrxSetFileInfo: FileEndOfFileInformation: new EndOfFile 0x%RX64, FileSize = 0x%RX64\n",
2245 pInfo->EndOfFile.QuadPart, capFcb->Header.FileSize.QuadPart));
2246
2247 Status = vbsfNtSetEndOfFile(RxContext, pInfo->EndOfFile.QuadPart);
2248
2249 Log(("VBOXSF: MrxSetFileInfo: FileEndOfFileInformation: Status 0x%08X\n",
2250 Status));
2251 break;
2252 }
2253
2254#if 0 /* This only works for more recent versions of the RDBSS library, not for the one we're using (WDK 7600.16385.1). */
2255 /*
2256 * HACK ALERT! This is FileEndOfFileInformation after it passed thru
2257 * VBoxHookMjSetInformation so we can twiddle the cached file size in
2258 * the FCB to ensure the set EOF request always reaches the host.
2259 *
2260 * Note! We have to call thru RxSetEndOfFileInfo to benefit from its
2261 * update logic and avoid needing to replicate that code.
2262 */
2263 case FileEndOfFileInformation + 4096:
2264 {
2265 PFILE_END_OF_FILE_INFORMATION pInfo = (PFILE_END_OF_FILE_INFORMATION)RxContext->Info.Buffer;
2266 Log(("VBOXSF: MrxSetFileInfo: FileEndOfFileInformation+4096: new EndOfFile 0x%RX64, FileSize = 0x%RX64\n",
2267 pInfo->EndOfFile.QuadPart, capFcb->Header.FileSize.QuadPart));
2268
2269 /* Undo the change from VBoxHookMjSetInformation: */
2270 Assert(RxContext->CurrentIrpSp);
2271 RxContext->CurrentIrpSp->Parameters.SetFile.FileInformationClass = FileEndOfFileInformation;
2272 RxContext->Info.FileInformationClass = FileEndOfFileInformation;
2273
2274 /* Tweak the size if necessary and forward the call. */
2275 int64_t const cbOldSize = capFcb->Header.FileSize.QuadPart;
2276 if ( pInfo->EndOfFile.QuadPart != cbOldSize
2277 || !(capFcb->FcbState & FCB_STATE_PAGING_FILE))
2278 {
2279 Status = RxSetEndOfFileInfo(RxContext, RxContext->CurrentIrp, (PFCB)capFcb, (PFOBX)capFobx);
2280 Log(("VBOXSF: MrxSetFileInfo: FileEndOfFileInformation+4096: Status 0x%08X\n",
2281 Status));
2282 }
2283 else
2284 {
2285 int64_t const cbHackedSize = cbOldSize ? cbOldSize - 1 : 1;
2286 capFcb->Header.FileSize.QuadPart = cbHackedSize;
2287 Status = RxSetEndOfFileInfo(RxContext, RxContext->CurrentIrp, (PFCB)capFcb, (PFOBX)capFobx);
2288 if ( !NT_SUCCESS(Status)
2289 && capFcb->Header.FileSize.QuadPart == cbHackedSize)
2290 capFcb->Header.FileSize.QuadPart = cbOldSize;
2291 else
2292 Assert( capFcb->Header.FileSize.QuadPart != cbHackedSize
2293 || pVBoxFobx->Info.cbObject == cbHackedSize);
2294 Log(("VBOXSF: MrxSetFileInfo: FileEndOfFileInformation+4096: Status 0x%08X (tweaked)\n",
2295 Status));
2296 }
2297 break;
2298 }
2299#endif
2300
2301 /// @todo FileModeInformation ?
2302 /// @todo return access denied or something for FileValidDataLengthInformation?
2303
2304 default:
2305 Log(("VBOXSF: MrxSetFileInfo: Not supported FileInformationClass: %d!\n",
2306 RxContext->Info.FileInformationClass));
2307 Status = STATUS_INVALID_PARAMETER;
2308 break;
2309 }
2310
2311 Log(("VBOXSF: MrxSetFileInfo: Returned 0x%08X\n", Status));
2312 return Status;
2313}
2314
2315/**
2316 * This is a no-op because we already set the file timestamps before closing,
2317 * and generally the host takes care of this.
2318 *
2319 * RDBSS calls this if it things we might need to update file information as the
2320 * file is closed.
2321 */
2322NTSTATUS VBoxMRxSetFileInfoAtCleanup(IN PRX_CONTEXT RxContext)
2323{
2324 RT_NOREF(RxContext);
2325 Log(("VBOXSF: MRxSetFileInfoAtCleanup\n"));
2326 return STATUS_SUCCESS;
2327}
2328
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette