VirtualBox

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

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

winnt/vboxsf: Workaround for SetEndOfFile() to the cached file size (i.e. the file size percieved by the guest) being incorrectly nooped out by the RDBSS code. Since the host may have appended (or truncated) the file without the guest noticing, a SetEndOfFile call must restore the file size to what the guest expected. A possible scenario would be resaving a document to overwrite host edits that extended the file (a bit unusual, though, since the editor would typically re-open the file, but it could happen). bugref:9172

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 86.7 KB
Line 
1/* $Id: info.cpp 78368 2019-05-03 03:02:54Z 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,
1806 * db 18h ; 10 FileRenameInformation,
1807 * db 18h ; 11 FileLinkInformation,
1808 * db 1 ; 13 FileDispositionInformation,
1809 * db 8 ; 14 FilePositionInformation,
1810 * db 4 ; 16 FileModeInformation,
1811 * db 8 ; 19 FileAllocationInformation,
1812 * db 8 ; 20 FileEndOfFileInformation,
1813 * db 8 ; 23 FilePipeInformation,
1814 * db 10h ; 25 FilePipeRemoteInformation,
1815 * db 8 ; 27 FileMailslotSetInformation,
1816 * db 48h ; 29 FileObjectIdInformation,
1817 * db 10h ; 30 FileCompletionInformation, - "reserved for system use"
1818 * db 18h ; 31 FileMoveClusterInformation, - "reserved for system use"
1819 * db 38h ; 32 FileQuotaInformation,
1820 * db 10h ; 36 FileTrackingInformation, - "reserved for system use"
1821 * db 8 ; 39 FileValidDataLengthInformation,
1822 * db 8 ; 40 FileShortNameInformation,
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 switch ((int)RxContext->Info.FileInformationClass)
1838 {
1839 /*
1840 * This is used to modify timestamps and attributes.
1841 *
1842 * Upon successful return, RDBSS will ensure that FILE_ATTRIBUTE_DIRECTORY is set
1843 * according to the FCB object type (see RxFinishFcbInitialization in path.cpp),
1844 * and that the FILE_ATTRIBUTE_TEMPORARY attribute is reflected in FcbState
1845 * (FCB_STATE_TEMPORARY) and the file object flags (FO_TEMPORARY_FILE). It will
1846 * also copy each non-zero timestamp into the FCB and set the corresponding
1847 * FOBX_FLAG_USER_SET_xxxx flag in the FOBX.
1848 *
1849 * RDBSS behaviour is idential between 16299.0/w10 and 7600.16385.1/wnet.
1850 */
1851 case FileBasicInformation:
1852 {
1853 Assert(RxContext->Info.Length >= sizeof(FILE_BASIC_INFORMATION));
1854 Status = vbsfNtSetBasicInfo(pNetRootExtension, RxContext->pFobx->AssociatedFileObject, pVBoxFobx, capFcb,
1855 VBoxMRxGetFcbExtension(capFcb), (PFILE_BASIC_INFORMATION)RxContext->Info.Buffer);
1856 break;
1857 }
1858
1859 /*
1860 * This is used to rename a file.
1861 */
1862 case FileRenameInformation:
1863 {
1864#ifdef LOG_ENABLED
1865 PFILE_RENAME_INFORMATION pInfo = (PFILE_RENAME_INFORMATION)RxContext->Info.Buffer;
1866 Log(("VBOXSF: MrxSetFileInfo: FileRenameInformation: ReplaceIfExists = %d, RootDirectory = 0x%x = [%.*ls]\n",
1867 pInfo->ReplaceIfExists, pInfo->RootDirectory, pInfo->FileNameLength / sizeof(WCHAR), pInfo->FileName));
1868#endif
1869
1870 Status = vbsfNtRename(RxContext, FileRenameInformation, RxContext->Info.Buffer, RxContext->Info.Length);
1871 break;
1872 }
1873
1874 /*
1875 * This is presumably used for hardlinking purposes. We don't support that.
1876 */
1877 case FileLinkInformation:
1878 {
1879#ifdef LOG_ENABLED
1880 PFILE_LINK_INFORMATION pInfo = (PFILE_LINK_INFORMATION )RxContext->Info.Buffer;
1881 Log(("VBOXSF: MrxSetFileInfo: FileLinkInformation: ReplaceIfExists = %d, RootDirectory = 0x%x = [%.*ls]. Not implemented!\n",
1882 pInfo->ReplaceIfExists, pInfo->RootDirectory, pInfo->FileNameLength / sizeof(WCHAR), pInfo->FileName));
1883#endif
1884
1885 Status = STATUS_NOT_IMPLEMENTED;
1886 break;
1887 }
1888
1889 /*
1890 * This is used to delete file.
1891 */
1892 case FileDispositionInformation:
1893 {
1894 PFILE_DISPOSITION_INFORMATION pInfo = (PFILE_DISPOSITION_INFORMATION)RxContext->Info.Buffer;
1895 Log(("VBOXSF: MrxSetFileInfo: FileDispositionInformation: Delete = %d\n",
1896 pInfo->DeleteFile));
1897
1898 if (pInfo->DeleteFile && capFcb->OpenCount == 1)
1899 Status = vbsfNtRemove(RxContext);
1900 else
1901 Status = STATUS_SUCCESS;
1902 break;
1903 }
1904
1905 /*
1906 * The file position is handled by the RDBSS library (RxSetPositionInfo)
1907 * and we should never see this request.
1908 */
1909 case FilePositionInformation:
1910 AssertMsgFailed(("VBOXSF: MrxSetFileInfo: FilePositionInformation: CurrentByteOffset = 0x%RX64. Unsupported!\n",
1911 ((PFILE_POSITION_INFORMATION)RxContext->Info.Buffer)->CurrentByteOffset.QuadPart));
1912 Status = STATUS_INTERNAL_ERROR;
1913 break;
1914
1915 /*
1916 * Change the allocation size, leaving the EOF alone unless the file shrinks.
1917 *
1918 * There is no shared folder operation for this, so we only need to care
1919 * about adjusting EOF if the file shrinks.
1920 *
1921 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/d4bc551b-7aaf-4b4f-ba0e-3a75e7c528f0#Appendix_A_83
1922 */
1923 case FileAllocationInformation:
1924 {
1925 PFILE_ALLOCATION_INFORMATION pInfo = (PFILE_ALLOCATION_INFORMATION)RxContext->Info.Buffer;
1926 Log(("VBOXSF: MrxSetFileInfo: FileAllocationInformation: new AllocSize = 0x%RX64, FileSize = 0x%RX64\n",
1927 pInfo->AllocationSize.QuadPart, capFcb->Header.FileSize.QuadPart));
1928
1929 if (pInfo->AllocationSize.QuadPart >= capFcb->Header.FileSize.QuadPart)
1930 Status = STATUS_SUCCESS;
1931 else
1932 {
1933 /** @todo get up to date EOF from host? We may risk accidentally growing the
1934 * file here if the host (or someone else) truncated it. */
1935 Status = vbsfNtSetEndOfFile(RxContext, pInfo->AllocationSize.QuadPart);
1936 }
1937 break;
1938 }
1939
1940 /*
1941 * Prior to calling us, RxSetEndOfFileInfo will have updated the FCB fields space.FileSize,
1942 * Header.AllocationSize and (if old value was larger) Header.ValidDataLength. On success
1943 * it will inform the cache manager, while on failure the old values will be restored.
1944 *
1945 * Note! RxSetEndOfFileInfo assumes that the old Header.FileSize value is up to date and
1946 * will hide calls which does not change the size from us. This is of course not
1947 * the case for non-local file systems, as the server is the only which up-to-date
1948 * information.
1949 *
1950 * We work around this either by modifying FCB.Header.FileSize slightly when it equals
1951 * the new size. This is either done below in the FileEndOfFileInformation + 4096 case,
1952 * or when using older WDK libs in VBoxHookMjSetInformation. The FCB is locked
1953 * exclusivly while we operate with the incorrect Header.FileSize value, which should
1954 * prevent anyone else from making use of it till it has been updated again.
1955 *
1956 */
1957 case FileEndOfFileInformation:
1958 {
1959 PFILE_END_OF_FILE_INFORMATION pInfo = (PFILE_END_OF_FILE_INFORMATION)RxContext->Info.Buffer;
1960 Log(("VBOXSF: MrxSetFileInfo: FileEndOfFileInformation: new EndOfFile 0x%RX64, FileSize = 0x%RX64\n",
1961 pInfo->EndOfFile.QuadPart, capFcb->Header.FileSize.QuadPart));
1962
1963 Status = vbsfNtSetEndOfFile(RxContext, pInfo->EndOfFile.QuadPart);
1964
1965 Log(("VBOXSF: MrxSetFileInfo: FileEndOfFileInformation: Status 0x%08X\n",
1966 Status));
1967 break;
1968 }
1969
1970#if 0 /* This only works for more recent versions of the RDBSS library, not for the one we're using (WDK 7600.16385.1). */
1971 /*
1972 * HACK ALERT! This is FileEndOfFileInformation after it passed thru
1973 * VBoxHookMjSetInformation so we can twiddle the cached file size in
1974 * the FCB to ensure the set EOF request always reaches the host.
1975 *
1976 * Note! We have to call thru RxSetEndOfFileInfo to benefit from its
1977 * update logic and avoid needing to replicate that code.
1978 */
1979 case FileEndOfFileInformation + 4096:
1980 {
1981 PFILE_END_OF_FILE_INFORMATION pInfo = (PFILE_END_OF_FILE_INFORMATION)RxContext->Info.Buffer;
1982 Log(("VBOXSF: MrxSetFileInfo: FileEndOfFileInformation+4096: new EndOfFile 0x%RX64, FileSize = 0x%RX64\n",
1983 pInfo->EndOfFile.QuadPart, capFcb->Header.FileSize.QuadPart));
1984
1985 /* Undo the change from VBoxHookMjSetInformation: */
1986 Assert(RxContext->CurrentIrpSp);
1987 RxContext->CurrentIrpSp->Parameters.SetFile.FileInformationClass = FileEndOfFileInformation;
1988 RxContext->Info.FileInformationClass = FileEndOfFileInformation;
1989
1990 /* Tweak the size if necessary and forward the call. */
1991 int64_t const cbOldSize = capFcb->Header.FileSize.QuadPart;
1992 if ( pInfo->EndOfFile.QuadPart != cbOldSize
1993 || !(capFcb->FcbState & FCB_STATE_PAGING_FILE))
1994 {
1995 Status = RxSetEndOfFileInfo(RxContext, RxContext->CurrentIrp, (PFCB)capFcb, (PFOBX)capFobx);
1996 Log(("VBOXSF: MrxSetFileInfo: FileEndOfFileInformation+4096: Status 0x%08X\n",
1997 Status));
1998 }
1999 else
2000 {
2001 int64_t const cbHackedSize = cbOldSize ? cbOldSize - 1 : 1;
2002 capFcb->Header.FileSize.QuadPart = cbHackedSize;
2003 Status = RxSetEndOfFileInfo(RxContext, RxContext->CurrentIrp, (PFCB)capFcb, (PFOBX)capFobx);
2004 if ( !NT_SUCCESS(Status)
2005 && capFcb->Header.FileSize.QuadPart == cbHackedSize)
2006 capFcb->Header.FileSize.QuadPart = cbOldSize;
2007 else
2008 Assert( capFcb->Header.FileSize.QuadPart != cbHackedSize
2009 || pVBoxFobx->Info.cbObject == cbHackedSize);
2010 Log(("VBOXSF: MrxSetFileInfo: FileEndOfFileInformation+4096: Status 0x%08X (tweaked)\n",
2011 Status));
2012 }
2013 break;
2014 }
2015#endif
2016
2017 /// @todo FileModeInformation ?
2018 /// @todo return access denied or something for FileValidDataLengthInformation?
2019
2020 default:
2021 Log(("VBOXSF: MrxSetFileInfo: Not supported FileInformationClass: %d!\n",
2022 RxContext->Info.FileInformationClass));
2023 Status = STATUS_INVALID_PARAMETER;
2024 break;
2025 }
2026
2027 Log(("VBOXSF: MrxSetFileInfo: Returned 0x%08X\n", Status));
2028 return Status;
2029}
2030
2031NTSTATUS VBoxMRxSetFileInfoAtCleanup(IN PRX_CONTEXT RxContext)
2032{
2033 RT_NOREF(RxContext);
2034 Log(("VBOXSF: MRxSetFileInfoAtCleanup\n"));
2035 return STATUS_SUCCESS;
2036}
2037
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