VirtualBox

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

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

winnt/vboxsf: Some more set information docs regarding the library we're linking against (AMD64). bugref:9172

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