VirtualBox

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

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

winnt/vboxsf: More words describing the FileAllocationInformation setting scenario. bugref:9172

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

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