VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use