VirtualBox

source: vbox/trunk/src/VBox/Additions/os2/VBoxSF/VBoxSF.cpp@ 75337

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

Add/os2/VBoxSF: Early shared folders for OS/2. Not perfect yet, but was able to build all the disassembler libraries on a shared folder mount.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 75.9 KB
Line 
1/** $Id: VBoxSF.cpp 75337 2018-11-09 01:39:01Z vboxsync $ */
2/** @file
3 * VBoxSF - OS/2 Shared Folders, the FS and FSD level IFS EPs
4 */
5
6/*
7 * Copyright (c) 2007 knut st. osmundsen <[email protected]>
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without
12 * restriction, including without limitation the rights to use,
13 * copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following
16 * conditions:
17 *
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 * OTHER DEALINGS IN THE SOFTWARE.
29 */
30
31
32/*********************************************************************************************************************************
33* Header Files *
34*********************************************************************************************************************************/
35#define LOG_GROUP LOG_GROUP_DEFAULT
36#include "VBoxSFInternal.h"
37
38#include <VBox/log.h>
39#include <iprt/assert.h>
40#include <iprt/ctype.h>
41#include <iprt/mem.h>
42
43#include <iprt/asm.h>
44#include <iprt/asm-amd64-x86.h>
45
46
47/*********************************************************************************************************************************
48* Defined Constants And Macros *
49*********************************************************************************************************************************/
50/** Max folder name length, including terminator.
51 * Easier to deal with stack buffers if we put a reasonable limit on the. */
52#define VBOXSFOS2_MAX_FOLDER_NAME 64
53
54
55/*********************************************************************************************************************************
56* Global Variables *
57*********************************************************************************************************************************/
58/** The shared mutex protecting folders list, drives and the connection. */
59MutexLock_t g_MtxFolders;
60/** The shared folder service client structure. */
61VBGLSFCLIENT g_SfClient;
62/** Set if g_SfClient is valid, clear if not. */
63bool g_fIsConnectedToService = false;
64/** List of active folder (PVBOXSFFOLDER). */
65RTLISTANCHOR g_FolderHead;
66/** This is incremented everytime g_FolderHead is modified. */
67uint32_t volatile g_uFolderRevision;
68/** Folders mapped on drive letters. Pointers include a reference. */
69PVBOXSFFOLDER g_apDriveFolders[26];
70
71
72
73/**
74 * Generic IPRT -> OS/2 status code converter.
75 *
76 * @returns OS/2 status code.
77 * @param vrc IPRT/VBox status code.
78 * @param rcDefault The OS/2 status code to return when there
79 * is no translation.
80 */
81APIRET vboxSfOs2ConvertStatusToOs2(int vrc, APIRET rcDefault)
82{
83 switch (vrc)
84 {
85 default: return rcDefault;
86
87 case VERR_FILE_NOT_FOUND: return ERROR_FILE_NOT_FOUND;
88 case VERR_PATH_NOT_FOUND: return ERROR_PATH_NOT_FOUND;
89 case VERR_SHARING_VIOLATION: return ERROR_SHARING_VIOLATION;
90 case VERR_ACCESS_DENIED: return ERROR_ACCESS_DENIED;
91 case VERR_ALREADY_EXISTS: return ERROR_ACCESS_DENIED;
92 case VERR_WRITE_PROTECT: return ERROR_WRITE_PROTECT;
93 case VERR_IS_A_DIRECTORY: return ERROR_DIRECTORY;
94 case VINF_SUCCESS: return NO_ERROR;
95 }
96}
97
98
99/**
100 * Gets the delta for the local timezone, in minutes.
101 *
102 * We need to do this once for each API call rather than over and over again for
103 * each date/time conversion, so as not to create an update race.
104 *
105 * @returns Delta in minutes. Current thinking is that positive means timezone
106 * is west of UTC, while negative is east of it.
107 */
108int16_t vboxSfOs2GetLocalTimeDelta(void)
109{
110 GINFOSEG volatile *pGis = (GINFOSEG volatile *)&KernSISData;
111 if (pGis)
112 {
113 uint16_t cDelta = pGis->timezone;
114 if (cDelta != 0 && cDelta != 0xffff)
115 return (int16_t)cDelta;
116 }
117 return 0;
118}
119
120
121/**
122 * Helper for converting from IPRT timespec format to OS/2 DATE/TIME.
123 *
124 * @param pDosDate The output DOS date.
125 * @param pDosTime The output DOS time.
126 * @param SrcTimeSpec The IPRT input timestamp.
127 * @param cMinLocalTimeDelta The timezone delta in minutes.
128 */
129void vboxSfOs2DateTimeFromTimeSpec(FDATE *pDosDate, FTIME *pDosTime, RTTIMESPEC SrcTimeSpec, int16_t cMinLocalTimeDelta)
130{
131 if (cMinLocalTimeDelta != 0)
132 RTTimeSpecAddSeconds(&SrcTimeSpec, cMinLocalTimeDelta * 60);
133
134 RTTIME Time;
135 if ( RTTimeSpecGetNano(&SrcTimeSpec) >= RTTIME_OFFSET_DOS_TIME
136 && RTTimeExplode(&Time, &SrcTimeSpec))
137 {
138 pDosDate->year = Time.i32Year - 1980;
139 pDosDate->month = Time.u8Month;
140 pDosDate->day = Time.u8MonthDay;
141 pDosTime->hours = Time.u8Hour;
142 pDosTime->minutes = Time.u8Minute;
143 pDosTime->twosecs = Time.u8Second / 2;
144 }
145 else
146 {
147 pDosDate->year = 0;
148 pDosDate->month = 1;
149 pDosDate->day = 1;
150 pDosTime->hours = 0;
151 pDosTime->minutes = 0;
152 pDosTime->twosecs = 0;
153 }
154}
155
156
157/**
158 * Helper for converting from OS/2 DATE/TIME to IPRT timespec format.
159 *
160 * @returns pDstTimeSpec on success, NULL if invalid input.
161 * @param DosDate The input DOS date.
162 * @param DosTime The input DOS time.
163 * @param cMinLocalTimeDelta The timezone delta in minutes.
164 * @param pDstTimeSpec The IPRT output timestamp.
165 */
166PRTTIMESPEC vboxSfOs2DateTimeToTimeSpec(FDATE DosDate, FTIME DosTime, int16_t cMinLocalTimeDelta, PRTTIMESPEC pDstTimeSpec)
167{
168 RTTIME Time;
169 Time.i32Year = DosDate.year + 1980;
170 Time.u8Month = DosDate.month;
171 Time.u8WeekDay = UINT8_MAX;
172 Time.u16YearDay = 0;
173 Time.u8MonthDay = DosDate.day;
174 Time.u8Hour = DosTime.hours;
175 Time.u8Minute = DosTime.minutes;
176 Time.u8Second = DosTime.twosecs * 2;
177 Time.u32Nanosecond = 0;
178 Time.fFlags = RTTIME_FLAGS_TYPE_LOCAL;
179 Time.offUTC = cMinLocalTimeDelta;
180 if (RTTimeLocalNormalize(&Time))
181 return RTTimeImplode(pDstTimeSpec, &Time);
182 return NULL;
183}
184
185
186/*********************************************************************************************************************************
187* Shared Folder String Buffer Management *
188*********************************************************************************************************************************/
189
190/**
191 * Allocates a SHFLSTRING buffer.
192 *
193 * @returns Pointer to a SHFLSTRING buffer, NULL if out of memory.
194 * @param cchLength The desired string buffer length (excluding terminator).
195 */
196PSHFLSTRING vboxSfOs2StrAlloc(size_t cchLength)
197{
198 AssertReturn(cchLength <= 0x1000, NULL);
199
200 PSHFLSTRING pStr = (PSHFLSTRING)VbglR0PhysHeapAlloc(SHFLSTRING_HEADER_SIZE + cchLength + 1);
201 if (pStr)
202 {
203 pStr->u16Size = (uint16_t)(cchLength + 1);
204 pStr->u16Length = 0;
205 pStr->String.ach[0] = '\0';
206 return pStr;
207 }
208 return NULL;
209}
210
211
212/**
213 * Duplicates a UTF-8 string into a SHFLSTRING buffer.
214 *
215 * @returns Pointer to a SHFLSTRING buffer containing the copy.
216 * NULL if out of memory or the string is too long.
217 * @param pachSrc The string to clone.
218 * @param cchSrc The length of the substring, RTMAX_STR for the whole.
219 */
220PSHFLSTRING vboxSfOs2StrDup(const char *pachSrc, size_t cchSrc)
221{
222 if (cchSrc == RTSTR_MAX)
223 cchSrc = strlen(pachSrc);
224 if (cchSrc < 0x1000)
225 {
226 PSHFLSTRING pStr = (PSHFLSTRING)VbglR0PhysHeapAlloc(SHFLSTRING_HEADER_SIZE + cchSrc + 1);
227 if (pStr)
228 {
229 pStr->u16Size = (uint16_t)(cchSrc + 1);
230 pStr->u16Length = (uint16_t)cchSrc;
231 memcpy(pStr->String.ach, pachSrc, cchSrc);
232 pStr->String.ach[cchSrc] = '\0';
233 return pStr;
234 }
235 }
236 return NULL;
237}
238
239
240/**
241 * Frees a SHLFSTRING buffer.
242 *
243 * @param pStr The buffer to free.
244 */
245void vboxSfOs2StrFree(PSHFLSTRING pStr)
246{
247 if (pStr)
248 VbglR0PhysHeapFree(pStr);
249}
250
251
252
253/*********************************************************************************************************************************
254* Folders, Paths and Service Connection. *
255*********************************************************************************************************************************/
256
257/**
258 * Ensures that we're connected to the host service.
259 *
260 * @returns VBox status code.
261 * @remarks Caller owns g_MtxFolder exclusively!
262 */
263static int vboxSfOs2EnsureConnected(void)
264{
265 if (g_fIsConnectedToService)
266 return VINF_SUCCESS;
267
268 int rc = VbglR0SfConnect(&g_SfClient);
269 if (RT_SUCCESS(rc))
270 {
271 rc = VbglR0SfSetUtf8(&g_SfClient);
272 if (RT_SUCCESS(rc))
273 g_fIsConnectedToService = true;
274 else
275 {
276 LogRel(("VbglR0SfSetUtf8 failed: %Rrc\n", rc));
277 VbglR0SfDisconnect(&g_SfClient);
278 }
279 }
280 else
281 LogRel(("VbglR0SfConnect failed: %Rrc\n", rc));
282 return rc;
283}
284
285
286/**
287 * Destroys a folder when the reference count has reached zero.
288 *
289 * @param pFolder The folder to destroy.
290 */
291static void vboxSfOs2DestroyFolder(PVBOXSFFOLDER pFolder)
292{
293 /* Note! We won't get there while the folder is on the list. */
294 LogRel(("vboxSfOs2ReleaseFolder: Destroying %p [%s]\n", pFolder, pFolder->szName));
295 VbglR0SfUnmapFolder(&g_SfClient, &pFolder->hHostFolder);
296 RT_ZERO(pFolder);
297 RTMemFree(pFolder);
298}
299
300
301/**
302 * Releases a reference to a folder.
303 *
304 * @param pFolder The folder to release.
305 */
306void vboxSfOs2ReleaseFolder(PVBOXSFFOLDER pFolder)
307{
308 if (pFolder)
309 {
310 uint32_t cRefs = ASMAtomicDecU32(&pFolder->cRefs);
311 AssertMsg(cRefs < _64K, ("%#x\n", cRefs));
312 if (!cRefs)
313 vboxSfOs2DestroyFolder(pFolder);
314 }
315}
316
317
318/**
319 * Retain a reference to a folder.
320 *
321 * @param pFolder The folder to release.
322 */
323void vboxSfOs2RetainFolder(PVBOXSFFOLDER pFolder)
324{
325 uint32_t cRefs = ASMAtomicIncU32(&pFolder->cRefs);
326 AssertMsg(cRefs < _64K, ("%#x\n", cRefs));
327}
328
329
330/**
331 * Locates and retains a folder structure.
332 *
333 * @returns Folder matching the name, NULL of not found.
334 * @remarks Caller owns g_MtxFolder.
335 */
336static PVBOXSFFOLDER vboxSfOs2FindAndRetainFolder(const char *pachName, size_t cchName)
337{
338 PVBOXSFFOLDER pCur;
339 RTListForEach(&g_FolderHead, pCur, VBOXSFFOLDER, ListEntry)
340 {
341 if ( pCur->cchName == cchName
342 && RTStrNICmpAscii(pCur->szName, pachName, cchName) == 0)
343 {
344 uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
345 AssertMsg(cRefs < _64K, ("%#x\n", cRefs));
346 return pCur;
347 }
348 }
349 return NULL;
350}
351
352
353/**
354 * Maps a folder, linking it into the list of folders.
355 *
356 * One reference is retained for the caller, which must pass it on or release
357 * it. The list also have a reference to it.
358 *
359 * @returns VBox status code.
360 * @param pName The name of the folder to map.
361 * @param ppFolder Where to return the folder structure on success.
362 *
363 * @remarks Caller owns g_MtxFolder exclusively!
364 */
365static int vboxSfOs2MapFolder(PSHFLSTRING pName, PVBOXSFFOLDER *ppFolder)
366{
367 int rc;
368 PVBOXSFFOLDER pNew = (PVBOXSFFOLDER)RTMemAlloc(RT_UOFFSETOF_DYN(VBOXSFFOLDER, szName[pName->u16Length + 1]));
369 if (pNew != NULL)
370 {
371 pNew->u32Magic = VBOXSFFOLDER_MAGIC;
372 pNew->cRefs = 2; /* (List reference + the returned reference.) */
373 pNew->cOpenFiles = 0;
374 pNew->cDrives = 0;
375 RT_ZERO(pNew->hHostFolder);
376 pNew->hVpb = 0;
377 pNew->cchName = (uint8_t)pName->u16Length;
378 memcpy(pNew->szName, pName->String.utf8, pName->u16Length);
379 pNew->szName[pName->u16Length] = '\0';
380
381 rc = VbglR0SfMapFolder(&g_SfClient, pName, &pNew->hHostFolder);
382 if (RT_SUCCESS(rc))
383 {
384 RTListAppend(&g_FolderHead, &pNew->ListEntry);
385 ASMAtomicIncU32(&g_uFolderRevision);
386 LogRel(("vboxSfOs2MapFolder: %p - %s\n", pNew, pNew->szName));
387
388 *ppFolder = pNew;
389 }
390 else
391 {
392 LogRel(("vboxSfOs2MapFolder: VbglR0SfMapFolder(,%.*s,) -> %Rrc\n", pName->u16Length, pName->String.utf8, rc));
393 RTMemFree(pNew);
394 }
395 }
396 else
397 {
398 LogRel(("vboxSfOs2MapFolder: Out of memory :-(\n"));
399 rc = VERR_NO_MEMORY;
400 }
401 return rc;
402}
403
404
405/**
406 * Worker for vboxSfOs2UncPrefixLength.
407 */
408DECLINLINE(size_t) vboxSfOs2CountLeadingSlashes(const char *pszPath)
409{
410 size_t cchSlashes = 0;
411 char ch;
412 while ((ch = *pszPath) == '\\' || ch == '/')
413 cchSlashes++, pszPath++;
414 return cchSlashes;
415}
416
417
418/**
419 * Checks for a VBox UNC prefix (server + slashes) and determins its length when
420 * found.
421 *
422 * @returns Length of VBoxSF UNC prefix, 0 if not VBoxSF UNC prefix.
423 * @param pszPath The possible UNC path.
424 */
425DECLINLINE(size_t) vboxSfOs2UncPrefixLength(const char *pszPath)
426{
427 char ch;
428 if ( ((ch = pszPath[0]) == '\\' || ch == '/')
429 && ((ch = pszPath[1]) == '\\' || ch == '/')
430 && ((ch = pszPath[2]) == 'V' || ch == 'v')
431 && ((ch = pszPath[3]) == 'B' || ch == 'b')
432 && ((ch = pszPath[4]) == 'O' || ch == 'o')
433 && ((ch = pszPath[5]) == 'X' || ch == 'x')
434 && ((ch = pszPath[6]) == 'S' || ch == 's')
435 )
436 {
437 /* \\VBoxSf\ */
438 if ( ((ch = pszPath[7]) == 'F' || ch == 'f')
439 && ((ch = pszPath[8]) == '\\' || ch == '/') )
440 return vboxSfOs2CountLeadingSlashes(&pszPath[9]) + 9;
441
442 /* \\VBoxSvr\ */
443 if ( ((ch = pszPath[7]) == 'V' || ch == 'v')
444 && ((ch = pszPath[8]) == 'R' || ch == 'r')
445 && ((ch = pszPath[9]) == '\\' || ch == '/') )
446 return vboxSfOs2CountLeadingSlashes(&pszPath[10]) + 10;
447
448 /* \\VBoxSrv\ */
449 if ( ((ch = pszPath[7]) == 'R' || ch == 'r')
450 && ((ch = pszPath[8]) == 'V' || ch == 'v')
451 && ((ch = pszPath[9]) == '\\' || ch == '/') )
452 return vboxSfOs2CountLeadingSlashes(&pszPath[10]) + 10;
453 }
454
455 return 0;
456}
457
458
459/**
460 * Converts a path to UTF-8 and puts it in a VBGL friendly buffer.
461 *
462 * @returns OS/2 status code
463 * @param pszFolderPath The path to convert.
464 * @param ppStr Where to return the pointer to the buffer. Free
465 * using vboxSfOs2FreePath.
466 */
467APIRET vboxSfOs2ConvertPath(const char *pszFolderPath, PSHFLSTRING *ppStr)
468{
469 /* Skip unnecessary leading slashes. */
470 char ch = *pszFolderPath;
471 if (ch == '\\' || ch == '/')
472 while ((ch = pszFolderPath[1]) == '\\' || ch == '/')
473 pszFolderPath++;
474
475 /** @todo do proper codepage -> utf8 conversion and straighten out
476 * everything... */
477 size_t cchSrc = strlen(pszFolderPath);
478 PSHFLSTRING pDst = vboxSfOs2StrAlloc(cchSrc);
479 if (pDst)
480 {
481 pDst->u16Length = (uint16_t)cchSrc;
482 memcpy(pDst->String.utf8, pszFolderPath, cchSrc);
483 pDst->String.utf8[cchSrc] = '\0';
484 *ppStr = pDst;
485 return NO_ERROR;
486 }
487 *ppStr = NULL;
488 return ERROR_NOT_ENOUGH_MEMORY;
489}
490
491
492/**
493 * Counterpart to vboxSfOs2ResolvePath.
494 *
495 * @param pStrPath The path to free.
496 * @param pFolder The folder to release.
497 */
498void vboxSfOs2ReleasePathAndFolder(PSHFLSTRING pStrPath, PVBOXSFFOLDER pFolder)
499{
500 if (pStrPath)
501 VbglR0PhysHeapFree(pStrPath);
502 if (pFolder)
503 {
504 uint32_t cRefs = ASMAtomicDecU32(&pFolder->cRefs);
505 Assert(cRefs < _64K);
506 if (!cRefs)
507 vboxSfOs2DestroyFolder(pFolder);
508 }
509}
510
511
512/**
513 * Worker for vboxSfOs2ResolvePath() for dynamically mapping folders for UNC
514 * paths.
515 *
516 * @returns OS/2 status code.
517 * @param pachFolderName The folder to map. Not necessarily zero terminated
518 * at the end of the folder name!
519 * @param cchFolderName The length of the folder name.
520 * @param uRevBefore The previous folder list revision.
521 * @param ppFolder Where to return the pointer to the retained folder.
522 */
523DECL_NO_INLINE(static, int) vboxSfOs2AttachUncAndRetain(const char *pachFolderName, size_t cchFolderName,
524 uint32_t uRevBefore, PVBOXSFFOLDER *ppFolder)
525{
526 KernRequestExclusiveMutex(&g_MtxFolders);
527
528 /*
529 * Check if someone raced us to it.
530 */
531 if (uRevBefore != g_uFolderRevision)
532 {
533 PVBOXSFFOLDER pFolder = vboxSfOs2FindAndRetainFolder(pachFolderName, cchFolderName);
534 if (pFolder)
535 {
536 KernReleaseExclusiveMutex(&g_MtxFolders);
537 *ppFolder = pFolder;
538 return NO_ERROR;
539 }
540 }
541
542 int rc = vboxSfOs2EnsureConnected();
543 if (RT_SUCCESS(rc))
544 {
545 /*
546 * Copy the name into the buffer format that Vbgl desires.
547 */
548 PSHFLSTRING pStrName = vboxSfOs2StrDup(pachFolderName, cchFolderName);
549 if (pStrName)
550 {
551 /*
552 * Do the attaching.
553 */
554 rc = vboxSfOs2MapFolder(pStrName, ppFolder);
555 vboxSfOs2StrFree(pStrName);
556 if (RT_SUCCESS(rc))
557 {
558 KernReleaseExclusiveMutex(&g_MtxFolders);
559 LogRel(("vboxSfOs2AttachUncAndRetain: Successfully attached '%s' (as UNC).\n", (*ppFolder)->szName));
560 return NO_ERROR;
561 }
562
563 if (rc == VERR_NO_MEMORY)
564 rc = ERROR_NOT_ENOUGH_MEMORY;
565 else
566 rc = ERROR_PATH_NOT_FOUND;
567 }
568 else
569 rc = ERROR_NOT_ENOUGH_MEMORY;
570 }
571 else
572 rc = ERROR_PATH_NOT_FOUND;
573
574 KernReleaseExclusiveMutex(&g_MtxFolders);
575 return rc;
576}
577
578
579/**
580 * Resolves the given path to a folder structure and folder relative string.
581 *
582 * @returns OS/2 status code.
583 * @param pszPath The path to resolve.
584 * @param pCdFsd The IFS dependent CWD structure if present.
585 * @param offCurDirEnd The offset into @a pszPath of the CWD. -1 if not
586 * CWD relative path.
587 * @param ppFolder Where to return the referenced pointer to the folder
588 * structure. Call vboxSfOs2ReleaseFolder() when done.
589 * @param ppStrFolderPath Where to return a buffer holding the folder relative
590 * path component. Free using vboxSfOs2FreePath().
591 */
592APIRET vboxSfOs2ResolvePath(const char *pszPath, PVBOXSFCD pCdFsd, LONG offCurDirEnd,
593 PVBOXSFFOLDER *ppFolder, PSHFLSTRING *ppStrFolderPath)
594{
595 APIRET rc;
596
597 /*
598 * UNC path? Reject the prefix to be on the safe side.
599 */
600 char ch = pszPath[0];
601 if (ch == '\\' || ch == '/')
602 {
603 size_t cchPrefix = vboxSfOs2UncPrefixLength(pszPath);
604 if (cchPrefix > 0)
605 {
606 /* Find the length of the folder name (share). */
607 const char *pszFolderName = &pszPath[cchPrefix];
608 size_t cchFolderName = 0;
609 while ((ch = pszFolderName[cchFolderName]) != '\0' && ch != '\\' && ch != '/')
610 {
611 if ((uint8_t)ch >= 0x20 && (uint8_t)ch <= 0x7f && ch != ':')
612 cchFolderName++;
613 else
614 {
615 LogRel(("vboxSfOs2ResolvePath: Invalid share name (@%u): %.*Rhxs\n",
616 cchPrefix + cchFolderName, strlen(pszPath), pszPath));
617 return ERROR_INVALID_NAME;
618 }
619 }
620 if (cchFolderName >= VBOXSFOS2_MAX_FOLDER_NAME)
621 {
622 LogRel(("vboxSfOs2ResolvePath: Folder name is too long: %u, max %u (%s)\n",
623 cchFolderName, VBOXSFOS2_MAX_FOLDER_NAME, pszPath));
624 return ERROR_FILENAME_EXCED_RANGE;
625 }
626
627 /*
628 * Look for the share.
629 */
630 KernRequestSharedMutex(&g_MtxFolders);
631 PVBOXSFFOLDER pFolder = *ppFolder = vboxSfOs2FindAndRetainFolder(pszFolderName, cchFolderName);
632 if (pFolder)
633 {
634 vboxSfOs2RetainFolder(pFolder);
635 KernReleaseSharedMutex(&g_MtxFolders);
636 }
637 else
638 {
639 uint32_t const uRevBefore = g_uFolderRevision;
640 KernReleaseSharedMutex(&g_MtxFolders);
641 rc = vboxSfOs2AttachUncAndRetain(pszFolderName, cchFolderName, uRevBefore, ppFolder);
642 if (rc == NO_ERROR)
643 pFolder = *ppFolder;
644 else
645 return rc;
646 }
647
648 /*
649 * Convert the path and put it in a Vbgl compatible buffer..
650 */
651 rc = vboxSfOs2ConvertPath(&pszFolderName[cchFolderName], ppStrFolderPath);
652 if (rc == NO_ERROR)
653 return rc;
654
655 vboxSfOs2ReleaseFolder(pFolder);
656 *ppFolder = NULL;
657 return rc;
658 }
659
660 LogRel(("vboxSfOs2ResolvePath: Unexpected path: %s\n", pszPath));
661 return ERROR_PATH_NOT_FOUND;
662 }
663
664 /*
665 * Drive letter?
666 */
667 ch &= ~0x20; /* upper case */
668 if ( ch >= 'A'
669 && ch <= 'Z'
670 && pszPath[1] == ':')
671 {
672 unsigned iDrive = ch - 'A';
673 ch = pszPath[2];
674 if (ch == '\\' || ch == '/')
675 {
676 KernRequestSharedMutex(&g_MtxFolders);
677 PVBOXSFFOLDER pFolder = *ppFolder = g_apDriveFolders[iDrive];
678 if (pFolder)
679 {
680 vboxSfOs2RetainFolder(pFolder);
681 KernReleaseSharedMutex(&g_MtxFolders);
682
683 /*
684 * Convert the path and put it in a Vbgl compatible buffer..
685 */
686 rc = vboxSfOs2ConvertPath(&pszPath[3], ppStrFolderPath);
687 if (rc == NO_ERROR)
688 return rc;
689
690 vboxSfOs2ReleaseFolder(pFolder);
691 *ppFolder = NULL;
692 return rc;
693 }
694 KernReleaseSharedMutex(&g_MtxFolders);
695 LogRel(("vboxSfOs2ResolvePath: No folder mapped on '%s'. Detach race?\n", pszPath));
696 return ERROR_PATH_NOT_FOUND;
697 }
698 LogRel(("vboxSfOs2ResolvePath: No root slash: '%s'\n", pszPath));
699 return ERROR_PATH_NOT_FOUND;
700 }
701 LogRel(("vboxSfOs2ResolvePath: Unexpected path: %s\n", pszPath));
702 RT_NOREF_PV(pCdFsd); RT_NOREF_PV(offCurDirEnd);
703 return ERROR_PATH_NOT_FOUND;
704}
705
706
707DECLASM(void)
708FS32_EXIT(ULONG uid, ULONG pid, ULONG pdb)
709{
710 LogFlow(("FS32_EXIT: uid=%u pid=%u pdb=%#x\n", uid, pid, pdb));
711 NOREF(uid); NOREF(pid); NOREF(pdb);
712}
713
714
715DECLASM(APIRET)
716FS32_SHUTDOWN(ULONG uType, ULONG uReserved)
717{
718 LogFlow(("FS32_SHUTDOWN: type=%u uReserved=%u\n", uType, uReserved));
719 NOREF(uType); NOREF(uReserved);
720 return NO_ERROR;
721}
722
723
724/**
725 * FS32_ATTACH worker: FS_ATTACH
726 */
727static APIRET vboxSfOs2Attach(PCSZ pszDev, PVBOXSFVP pVpFsd, PVBOXSFCD pCdFsd, PBYTE pszParam, PUSHORT pcbParam,
728 PSHFLSTRING *ppCleanup)
729{
730 /*
731 * Check out the parameters, copying the pszParam into a suitable string buffer.
732 */
733 if (pszDev == NULL || !*pszDev || !RT_C_IS_ALPHA(pszDev[0]) || pszDev[1] != ':' || pszDev[2] != '\0')
734 {
735 LogRel(("vboxSfOs2Attach: Invalid pszDev value:%p:{%s}\n", pszDev, pszDev));
736 return ERROR_INVALID_PARAMETER;
737 }
738 unsigned const iDrive = (pszDev[0] & ~0x20) - 'A';
739
740 if (pszParam == NULL || pcbParam == NULL)
741 {
742 LogRel(("vboxSfOs2Attach: NULL parameter buffer or buffer length\n"));
743 return ERROR_INVALID_PARAMETER;
744 }
745
746 PSHFLSTRING pStrName = *ppCleanup = vboxSfOs2StrAlloc(VBOXSFOS2_MAX_FOLDER_NAME - 1);
747 pStrName->u16Length = *pcbParam;
748 if (pStrName->u16Length < 1 || pStrName->u16Length > VBOXSFOS2_MAX_FOLDER_NAME)
749 {
750 LogRel(("vboxSfOs2Attach: Parameter buffer length is out of bounds: %u (min: 1, max " RT_XSTR(VBOXSFOS2_MAX_FOLDER_NAME) ")\n",
751 pStrName->u16Length));
752 return ERROR_INVALID_PARAMETER;
753 }
754
755 int rc = KernCopyIn(pStrName->String.utf8, pszParam, pStrName->u16Length);
756 if (rc != NO_ERROR)
757 return rc;
758
759 pStrName->u16Length -= 1;
760 if (pStrName->String.utf8[pStrName->u16Length] != '\0')
761 {
762 LogRel(("vboxSfOs2Attach: Parameter not null terminated\n"));
763 return ERROR_INVALID_PARAMETER;
764 }
765
766 /* Make sure it's only ascii and contains not weird stuff. */
767 unsigned off = pStrName->u16Length;
768 while (off-- > 0)
769 {
770 char const ch = pStrName->String.utf8[off];
771 if (ch < 0x20 || ch >= 0x7f || ch == ':' || ch == '\\' || ch == '/')
772 {
773 LogRel(("vboxSfOs2Attach: Malformed folder name: %.*Rhxs (off %#x)\n", pStrName->u16Length, pStrName->String.utf8, off));
774 return ERROR_INVALID_PARAMETER;
775 }
776 }
777
778 if (!pVpFsd)
779 {
780 LogRel(("vboxSfOs2Attach: pVpFsd is NULL\n"));
781 return ERROR_INVALID_PARAMETER;
782 }
783
784 /*
785 * Look for the folder to see if we're already using it. Map it if needed.
786 */
787 KernRequestExclusiveMutex(&g_MtxFolders);
788 if (g_apDriveFolders[iDrive] == NULL)
789 {
790
791 PVBOXSFFOLDER pFolder = vboxSfOs2FindAndRetainFolder(pStrName->String.ach, pStrName->u16Length);
792 if (!pFolder)
793 {
794 rc = vboxSfOs2EnsureConnected();
795 if (RT_SUCCESS(rc))
796 rc = vboxSfOs2MapFolder(pStrName, &pFolder);
797 }
798 if (pFolder && RT_SUCCESS(rc))
799 {
800 pFolder->cDrives += 1;
801 g_apDriveFolders[iDrive] = pFolder;
802
803 pVpFsd->u32Magic = VBOXSFVP_MAGIC;
804 pVpFsd->pFolder = pFolder;
805
806 KernReleaseExclusiveMutex(&g_MtxFolders);
807
808 LogRel(("vboxSfOs2Attach: Successfully attached '%s' to '%s'.\n", pFolder->szName, pszDev));
809 return NO_ERROR;
810 }
811
812 KernReleaseExclusiveMutex(&g_MtxFolders);
813 return ERROR_FILE_NOT_FOUND;
814 }
815 KernReleaseExclusiveMutex(&g_MtxFolders);
816
817 LogRel(("vboxSfOs2Attach: Already got a folder on '%s'!\n", pszDev));
818 RT_NOREF(pCdFsd);
819 return ERROR_BUSY_DRIVE;
820}
821
822
823/**
824 * FS32_ATTACH worker: FS_DETACH
825 */
826static APIRET vboxSfOs2Detach(PCSZ pszDev, PVBOXSFVP pVpFsd, PVBOXSFCD pCdFsd, PBYTE pszParam, PUSHORT pcbParam)
827{
828 /*
829 * Validate the volume data and assocated folder.
830 */
831 AssertPtrReturn(pVpFsd, ERROR_SYS_INTERNAL);
832 AssertReturn(pVpFsd->u32Magic == VBOXSFVP_MAGIC, ERROR_SYS_INTERNAL);
833 PVBOXSFFOLDER pFolder = pVpFsd->pFolder;
834 AssertPtrReturn(pFolder, ERROR_SYS_INTERNAL);
835 AssertReturn(pFolder->u32Magic == VBOXSFFOLDER_MAGIC, ERROR_SYS_INTERNAL);
836
837 uint8_t idxDrive = UINT8_MAX;
838 if ( pszDev
839 && RT_C_IS_ALPHA(*pszDev))
840 idxDrive = (*pszDev & ~0x20) - 'A';
841
842 /*
843 * Can we detach it?
844 */
845 APIRET rc;
846 KernRequestExclusiveMutex(&g_MtxFolders);
847 if ( pFolder->cOpenFiles == 0
848 && pFolder->cOpenSearches == 0)
849 {
850 /*
851 * Check that we've got the right folder/drive combo.
852 */
853 if ( idxDrive < RT_ELEMENTS(g_apDriveFolders)
854 && g_apDriveFolders[idxDrive] == pFolder)
855 {
856 g_apDriveFolders[idxDrive] = NULL;
857 uint8_t cDrives = --pFolder->cDrives;
858 AssertMsg(cDrives < 30, ("%#x\n", cDrives));
859
860 uint32_t cRefs = ASMAtomicDecU32(&pFolder->cRefs);
861 AssertMsg(cRefs < _32K, ("%#x\n", cRefs));
862 if (cRefs)
863 {
864 /* If there are no zero drives, unlink it from the list and release
865 the list reference. This should almost always drop end up with us
866 destroying the folder.*/
867 if (cDrives == 0)
868 {
869 RTListNodeRemove(&pFolder->ListEntry);
870 cRefs = ASMAtomicDecU32(&pFolder->cRefs);
871 AssertMsg(cRefs < _32K, ("%#x\n", cRefs));
872 if (!cRefs)
873 vboxSfOs2DestroyFolder(pFolder);
874 }
875 }
876 else
877 {
878 LogRel(("vboxSfOs2Detach: cRefs=0?!?\n"));
879 vboxSfOs2DestroyFolder(pFolder);
880 }
881 }
882 else
883 {
884 LogRel(("vboxSfOs2Detach: g_apDriveFolders[%#x]=%p pFolder=%p\n",
885 idxDrive, idxDrive < RT_ELEMENTS(g_apDriveFolders) ? g_apDriveFolders[idxDrive] : NULL, pFolder));
886 rc = ERROR_NOT_SUPPORTED;
887 }
888 }
889 else
890 rc = ERROR_BUSY_DRIVE;
891 KernReleaseExclusiveMutex(&g_MtxFolders);
892
893 RT_NOREF(pszDev, pVpFsd, pCdFsd, pszParam, pcbParam);
894 return rc;
895}
896
897
898/**
899 * FS32_ATTACH worker: FSA_ATTACH_INFO
900 */
901static APIRET vboxSfOs2QueryAttachInfo(PCSZ pszDev, PVBOXSFVP pVpFsd, PVBOXSFCD pCdFsd, PBYTE pbData, PUSHORT pcbParam)
902{
903 /*
904 * Userland calls the kernel with a FSQBUFFER buffer, the kernel
905 * fills in the first part of us and hands us &FSQBUFFER::cbFSAData
906 * to do the rest. We could return the share name here, for instance.
907 */
908 APIRET rc;
909 USHORT cbParam = *pcbParam;
910 if ( pszDev == NULL
911 || (pszDev[0] != '\\' && pszDev[0] != '/'))
912 {
913 /* Validate the volume data and assocated folder. */
914 AssertPtrReturn(pVpFsd, ERROR_SYS_INTERNAL);
915 AssertReturn(pVpFsd->u32Magic == VBOXSFVP_MAGIC, ERROR_SYS_INTERNAL);
916 PVBOXSFFOLDER pFolder = pVpFsd->pFolder;
917 AssertPtrReturn(pFolder, ERROR_SYS_INTERNAL);
918 AssertReturn(pFolder->u32Magic == VBOXSFFOLDER_MAGIC, ERROR_SYS_INTERNAL);
919
920 /* Try copy out the data. */
921 if (cbParam >= sizeof(USHORT) + pFolder->cchName + 1)
922 {
923 *pcbParam = (uint16_t)sizeof(USHORT) + pFolder->cchName + 1;
924 cbParam = pFolder->cchName + 1;
925 rc = KernCopyOut(pbData, &cbParam, sizeof(cbParam));
926 if (rc != NO_ERROR)
927 rc = KernCopyOut(pbData + sizeof(USHORT), pFolder->szName, pFolder->cchName + 1);
928 }
929 else
930 rc = ERROR_BUFFER_OVERFLOW;
931 }
932 else
933 {
934 /* Looks like a device query, so return zero bytes. */
935 if (cbParam >= sizeof(USHORT))
936 {
937 *pcbParam = sizeof(USHORT);
938 cbParam = 0;
939 rc = KernCopyOut(pbData, &cbParam, sizeof(cbParam));
940 }
941 else
942 rc = ERROR_BUFFER_OVERFLOW;
943 }
944
945 RT_NOREF(pCdFsd);
946 return rc;
947}
948
949
950DECLASM(APIRET)
951FS32_ATTACH(ULONG fFlags, PCSZ pszDev, PVBOXSFVP pVpFsd, PVBOXSFCD pCdFsd, PBYTE pszParam, PUSHORT pcbParam)
952{
953 LogFlow(("FS32_ATTACH: fFlags=%#x pszDev=%p:{%s} pVpFsd=%p pCdFsd=%p pszParam=%p pcbParam=%p\n",
954 fFlags, pszDev, pszDev, pVpFsd, pCdFsd, pszParam, pcbParam));
955 APIRET rc;
956 if (pVpFsd)
957 {
958 PSHFLSTRING pCleanup = NULL;
959
960 if (fFlags == FS_ATTACH)
961 rc = vboxSfOs2Attach(pszDev, pVpFsd, pCdFsd, pszParam, pcbParam, &pCleanup);
962 else if (fFlags == FSA_DETACH)
963 rc = vboxSfOs2Detach(pszDev, pVpFsd, pCdFsd, pszParam, pcbParam);
964 else if (fFlags == FSA_ATTACH_INFO)
965 rc = vboxSfOs2QueryAttachInfo(pszDev, pVpFsd, pCdFsd, pszParam, pcbParam);
966 else
967 {
968 LogRel(("FS32_ATTACH: Unsupported fFlags value: %#x\n", fFlags));
969 rc = ERROR_NOT_SUPPORTED;
970 }
971
972 if (pCleanup)
973 vboxSfOs2StrFree(pCleanup);
974 }
975 else
976 rc = ERROR_NOT_SUPPORTED; /* We don't support device attaching. */
977 LogFlow(("FS32_ATTACH: returns %u\n", rc));
978 return rc;
979}
980
981
982DECLASM(APIRET)
983FS32_VERIFYUNCNAME(ULONG uType, PCSZ pszName)
984{
985 LogFlow(("FS32_VERIFYUNCNAME: uType=%#x pszName=%p:{%s}\n", uType, pszName, pszName));
986 RT_NOREF(uType); /* pass 1 or pass 2 doesn't matter to us, we've only got one 'server'. */
987
988 if (vboxSfOs2UncPrefixLength(pszName) > 0 )
989 return NO_ERROR;
990 return ERROR_NOT_SUPPORTED;
991}
992
993
994DECLASM(APIRET)
995FS32_FLUSHBUF(USHORT hVPB, ULONG fFlags)
996{
997 NOREF(hVPB); NOREF(fFlags);
998 return NO_ERROR;
999}
1000
1001
1002DECLASM(APIRET)
1003FS32_FSINFO(ULONG fFlags, USHORT hVpb, PBYTE pbData, ULONG cbData, ULONG uLevel)
1004{
1005 LogFlow(("FS32_FSINFO: fFlags=%#x hVpb=%#x pbData=%p cbData=%#x uLevel=%p\n", fFlags, hVpb, pbData, cbData, uLevel));
1006
1007 /*
1008 * Resolve hVpb and do parameter validation.
1009 */
1010 PVPFSI pVpFsi = NULL;
1011 PVBOXSFVP pVpFsd = Fsh32GetVolParams(hVpb, &pVpFsi);
1012 Log(("FS32_FSINFO: hVpb=%#x -> pVpFsd=%p pVpFsi=%p\n", hVpb, pVpFsd, pVpFsi));
1013
1014 AssertPtrReturn(pVpFsd, ERROR_SYS_INTERNAL);
1015 AssertReturn(pVpFsd->u32Magic == VBOXSFVP_MAGIC, ERROR_SYS_INTERNAL);
1016 PVBOXSFFOLDER pFolder = pVpFsd->pFolder; /** @todo need to retain it behind locks. */
1017 AssertPtrReturn(pFolder, ERROR_SYS_INTERNAL);
1018 AssertReturn(pFolder->u32Magic == VBOXSFFOLDER_MAGIC, ERROR_SYS_INTERNAL);
1019
1020 APIRET rc;
1021
1022 /*
1023 * Queries.
1024 */
1025 if (fFlags == INFO_RETREIVE)
1026 {
1027 /* Check that buffer/level matches up. */
1028 switch (uLevel)
1029 {
1030 case FSIL_ALLOC:
1031 if (cbData >= sizeof(FSALLOCATE))
1032 break;
1033 LogFlow(("FS32_FSINOF: cbData=%u < sizeof(FSALLOCATE) -> ERROR_BUFFER_OVERFLOW\n", cbData));
1034 return ERROR_BUFFER_OVERFLOW;
1035
1036 case FSIL_VOLSER:
1037 if (cbData >= sizeof(FSINFO))
1038 break;
1039 LogFlow(("FS32_FSINOF: cbData=%u < sizeof(FSINFO) -> ERROR_BUFFER_OVERFLOW\n", cbData));
1040 return ERROR_BUFFER_OVERFLOW;
1041
1042 default:
1043 LogRel(("FS32_FSINFO: Unsupported info level %u!\n", uLevel));
1044 return ERROR_INVALID_LEVEL;
1045 }
1046
1047 /* Work buffer union to keep it to a single allocation and no stack. */
1048 union FsInfoBufs
1049 {
1050 struct
1051 {
1052 SHFLCREATEPARMS Params;
1053 union
1054 {
1055 SHFLSTRING Path;
1056 uint8_t abPadding[SHFLSTRING_HEADER_SIZE + 4];
1057 };
1058 } Open;
1059 struct
1060 {
1061 SHFLVOLINFO VolInfo;
1062 union
1063 {
1064 FSALLOCATE Alloc;
1065 FSINFO FsInfo;
1066 };
1067 } Info;
1068 } *pu = (union FsInfoBufs *)VbglR0PhysHeapAlloc(sizeof(*pu));
1069 if (!pu)
1070 return ERROR_NOT_ENOUGH_MEMORY;
1071
1072 /*
1073 * To get the info we need to open the root of the folder.
1074 */
1075 RT_ZERO(pu->Open.Params);
1076 pu->Open.Params.CreateFlags = SHFL_CF_DIRECTORY | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACT_OPEN_IF_EXISTS
1077 | SHFL_CF_ACCESS_READ | SHFL_CF_ACCESS_ATTR_READ | SHFL_CF_ACCESS_DENYNONE;
1078 pu->Open.Path.u16Size = 3;
1079 pu->Open.Path.u16Length = 2;
1080 pu->Open.Path.String.utf8[0] = '\\';
1081 pu->Open.Path.String.utf8[1] = '.';
1082 pu->Open.Path.String.utf8[2] = '\0';
1083
1084 int vrc = VbglR0SfCreate(&g_SfClient, &pFolder->hHostFolder, &pu->Open.Path, &pu->Open.Params);
1085 LogFlow(("FS32_FSINFO: VbglR0SfCreate -> %Rrc Result=%d Handle=%#RX64\n", vrc, pu->Open.Params.Result, pu->Open.Params.Handle));
1086 if ( RT_SUCCESS(vrc)
1087 && pu->Open.Params.Handle != SHFL_HANDLE_NIL)
1088 {
1089 SHFLHANDLE hHandle = pu->Open.Params.Handle;
1090
1091 RT_ZERO(pu->Info.VolInfo);
1092 uint32_t cbBuf = sizeof(pu->Info.VolInfo);
1093 vrc = VbglR0SfFsInfo(&g_SfClient, &pFolder->hHostFolder, hHandle, SHFL_INFO_VOLUME | SHFL_INFO_GET,
1094 &cbBuf, (PSHFLDIRINFO)&pu->Info.VolInfo);
1095 if (RT_SUCCESS(vrc))
1096 {
1097 /*
1098 * Construct and copy out the requested info.
1099 */
1100 if (uLevel == FSIL_ALLOC)
1101 {
1102 pu->Info.Alloc.idFileSystem = 0; /* unknown */
1103 pu->Info.Alloc.cSectorUnit = pu->Info.VolInfo.ulBytesPerAllocationUnit / RT_MAX(pu->Info.VolInfo.ulBytesPerSector, 1);
1104 pu->Info.Alloc.cUnit = (uint32_t)(pu->Info.VolInfo.ullTotalAllocationBytes / RT_MAX(pu->Info.VolInfo.ulBytesPerAllocationUnit, 1));
1105 pu->Info.Alloc.cUnitAvail = (uint32_t)(pu->Info.VolInfo.ullAvailableAllocationBytes / RT_MAX(pu->Info.VolInfo.ulBytesPerAllocationUnit, 1));
1106 pu->Info.Alloc.cbSector = (uint16_t)(pu->Info.VolInfo.ulBytesPerSector);
1107 rc = KernCopyOut(pbData, &pu->Info.Alloc, sizeof(pu->Info.Alloc));
1108 }
1109 else
1110 {
1111 RT_ZERO(pu->Info.FsInfo);
1112 pu->Info.FsInfo.vol.cch = (uint8_t)RT_MIN(pFolder->cchName, sizeof(pu->Info.FsInfo.vol.szVolLabel) - 1);
1113 memcpy(pu->Info.FsInfo.vol.szVolLabel, pFolder->szName, pu->Info.FsInfo.vol.cch);
1114 *(uint32_t *)&pu->Info.FsInfo.fdateCreation = pu->Info.VolInfo.ulSerial;
1115 rc = KernCopyOut(pbData, &pu->Info.FsInfo, sizeof(pu->Info.FsInfo));
1116 }
1117 }
1118 else
1119 {
1120 LogRel(("FS32_FSINFO: VbglR0SfFsInfo/SHFL_INFO_VOLUME failed: %Rrc\n", rc));
1121 rc = ERROR_GEN_FAILURE;
1122 }
1123
1124 vrc = VbglR0SfClose(&g_SfClient, &pFolder->hHostFolder, hHandle);
1125 AssertRC(vrc);
1126 }
1127 else
1128 rc = ERROR_GEN_FAILURE;
1129
1130 VbglR0PhysHeapFree(pu);
1131 }
1132 /*
1133 * We don't allow setting anything.
1134 */
1135 else if (fFlags == INFO_SET)
1136 {
1137 LogRel(("FS32_FSINFO: Attempting to set volume info (uLevel=%u, cbData=%#x) -> ERROR_ACCESS_DENIED\n", uLevel, cbData));
1138 rc = ERROR_ACCESS_DENIED;
1139 }
1140 else
1141 {
1142 LogRel(("FS32_FSINFO: Unknown flags: %#x\n", fFlags));
1143 rc = ERROR_SYS_INTERNAL;
1144 }
1145
1146 LogFlow(("FS32_FSINFO: returns %#x\n", rc));
1147 return rc;
1148}
1149
1150
1151DECLASM(APIRET)
1152FS32_FSCTL(union argdat *pArgData, ULONG iArgType, ULONG uFunction,
1153 PVOID pvParm, USHORT cbParm, PUSHORT pcbParmIO,
1154 PVOID pvData, USHORT cbData, PUSHORT pcbDataIO)
1155{
1156 LogFlow(("FS32_FSCTL: pArgData=%p iArgType=%#x uFunction=%#x pvParam=%p cbParam=%#x pcbParmIO=%p pvData=%p cbData=%#x pcbDataIO=%p\n",
1157 pArgData, iArgType, uFunction, pvParm, cbParm, pcbParmIO, pvData, cbData, pcbDataIO));
1158 NOREF(pArgData); NOREF(iArgType); NOREF(uFunction); NOREF(pvParm); NOREF(cbParm); NOREF(pcbParmIO);
1159 NOREF(pvData); NOREF(cbData); NOREF(pcbDataIO);
1160 return ERROR_NOT_SUPPORTED;
1161}
1162
1163
1164DECLASM(APIRET)
1165FS32_PROCESSNAME(PSZ pszName)
1166{
1167 LogFlow(("FS32_PROCESSNAME: '%s'\n", pszName));
1168 NOREF(pszName);
1169 return NO_ERROR;
1170}
1171
1172
1173DECLASM(APIRET)
1174FS32_CHDIR(ULONG fFlags, PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszDir, LONG offCurDirEnd)
1175{
1176 LogFlow(("FS32_CHDIR: fFlags=%#x pCdFsi=%p:{%#x,%s} pCdFsd=%p pszDir=%p:{%s} offCurDirEnd=%d\n",
1177 fFlags, pCdFsi, pCdFsi ? pCdFsi->cdi_hVPB : 0xffff, pCdFsi ? pCdFsi->cdi_curdir : "", pCdFsd, pszDir, pszDir, offCurDirEnd));
1178
1179 /*
1180 * We do not keep any information about open directory, just verify
1181 * them before they are CD'ed into and when asked to revalidate them.
1182 * If there were any path walking benefits, we could consider opening the
1183 * directory and keeping it open, but there isn't, so we don't do that.
1184 */
1185 APIRET rc = NO_ERROR;
1186 if ( fFlags == CD_EXPLICIT
1187 || fFlags == CD_VERIFY)
1188 {
1189 if (fFlags == CD_VERIFY)
1190 pszDir = pCdFsi->cdi_curdir;
1191
1192 PVBOXSFFOLDER pFolder;
1193 PSHFLSTRING pStrFolderPath;
1194 rc = vboxSfOs2ResolvePath(pszDir, pCdFsd, offCurDirEnd, &pFolder, &pStrFolderPath);
1195 if (rc == NO_ERROR)
1196 {
1197 SHFLCREATEPARMS *pParams = (SHFLCREATEPARMS *)VbglR0PhysHeapAlloc(sizeof(*pParams));
1198 if (pParams)
1199 {
1200 RT_ZERO(*pParams);
1201 pParams->CreateFlags = SHFL_CF_LOOKUP;
1202
1203 int vrc = VbglR0SfCreate(&g_SfClient, &pFolder->hHostFolder, pStrFolderPath, pParams);
1204 LogFlow(("FS32_CHDIR: VbglR0SfCreate -> %Rrc Result=%d fMode=%#x\n", vrc, pParams->Result, pParams->Info.Attr.fMode));
1205 if (RT_SUCCESS(vrc))
1206 {
1207 switch (pParams->Result)
1208 {
1209 case SHFL_FILE_EXISTS:
1210 if (RTFS_IS_DIRECTORY(pParams->Info.Attr.fMode))
1211 rc = NO_ERROR;
1212 else
1213 rc = ERROR_ACCESS_DENIED;
1214 break;
1215
1216 case SHFL_PATH_NOT_FOUND:
1217 rc = ERROR_PATH_NOT_FOUND;
1218 break;
1219
1220 default:
1221 case SHFL_FILE_NOT_FOUND:
1222 rc = ERROR_FILE_NOT_FOUND;
1223 break;
1224 }
1225 }
1226 else
1227 rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_PATH_NOT_FOUND);
1228 VbglR0PhysHeapFree(pParams);
1229 }
1230 else
1231 rc = ERROR_NOT_ENOUGH_MEMORY;
1232 vboxSfOs2ReleasePathAndFolder(pStrFolderPath, pFolder);
1233 }
1234 }
1235 else if (fFlags == CD_FREE)
1236 {
1237 /* nothing to do here. */
1238 }
1239 else
1240 {
1241 LogRel(("FS32_CHDIR: Unexpected fFlags value: %#x\n", fFlags));
1242 rc = ERROR_NOT_SUPPORTED;
1243 }
1244
1245 LogFlow(("FS32_CHDIR: returns %u\n", rc));
1246 return rc;
1247}
1248
1249
1250DECLASM(APIRET)
1251FS32_MKDIR(PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszDir, LONG offCurDirEnd, PEAOP pEaOp, ULONG fFlags)
1252{
1253 LogFlow(("FS32_MKDIR: pCdFsi=%p pCdFsd=%p pszDir=%p:{%s} pEAOp=%p fFlags=%#x\n", pCdFsi, pCdFsd, pszDir, pszDir, offCurDirEnd, pEaOp, fFlags));
1254
1255 /*
1256 * We don't do EAs.
1257 */
1258 APIRET rc;
1259 if (pEaOp == NULL)
1260 {
1261 /*
1262 * Resolve the path.
1263 */
1264 PVBOXSFFOLDER pFolder;
1265 PSHFLSTRING pStrFolderPath;
1266 rc = vboxSfOs2ResolvePath(pszDir, pCdFsd, offCurDirEnd, &pFolder, &pStrFolderPath);
1267 if (rc == NO_ERROR)
1268 {
1269 /*
1270 * The silly interface for creating directories amounts an open call that
1271 * fails if it exists and we get a file handle back that needs closing. Sigh.
1272 */
1273 SHFLCREATEPARMS *pParams = (SHFLCREATEPARMS *)VbglR0PhysHeapAlloc(sizeof(*pParams));
1274 if (pParams != NULL)
1275 {
1276 RT_ZERO(*pParams);
1277 pParams->CreateFlags = SHFL_CF_DIRECTORY | SHFL_CF_ACT_CREATE_IF_NEW | SHFL_CF_ACT_FAIL_IF_EXISTS
1278 | SHFL_CF_ACCESS_READ | SHFL_CF_ACCESS_DENYNONE;
1279
1280 int vrc = VbglR0SfCreate(&g_SfClient, &pFolder->hHostFolder, pStrFolderPath, pParams);
1281 LogFlow(("FS32_MKDIR: VbglR0SfCreate -> %Rrc Result=%d fMode=%#x\n", vrc, pParams->Result, pParams->Info.Attr.fMode));
1282 if (RT_SUCCESS(vrc))
1283 {
1284 switch (pParams->Result)
1285 {
1286 case SHFL_FILE_CREATED:
1287 if (pParams->Handle != SHFL_HANDLE_NIL)
1288 {
1289 vrc = VbglR0SfClose(&g_SfClient, &pFolder->hHostFolder, pParams->Handle);
1290 AssertRC(vrc);
1291 }
1292 rc = NO_ERROR;
1293 break;
1294
1295 case SHFL_FILE_EXISTS:
1296 rc = ERROR_ACCESS_DENIED;
1297 break;
1298
1299 case SHFL_PATH_NOT_FOUND:
1300 rc = ERROR_PATH_NOT_FOUND;
1301 break;
1302
1303 default:
1304 case SHFL_FILE_NOT_FOUND:
1305 rc = ERROR_FILE_NOT_FOUND;
1306 break;
1307 }
1308 }
1309 else if (vrc == VERR_ALREADY_EXISTS)
1310 rc = ERROR_ACCESS_DENIED;
1311 else
1312 rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_FILE_NOT_FOUND);
1313 VbglR0PhysHeapFree(pParams);
1314 }
1315 else
1316 rc = ERROR_NOT_ENOUGH_MEMORY;
1317 vboxSfOs2ReleasePathAndFolder(pStrFolderPath, pFolder);
1318 }
1319 }
1320 else
1321 {
1322 Log(("FS32_MKDIR: EAs not supported\n"));
1323 rc = ERROR_EAS_NOT_SUPPORTED;
1324 }
1325
1326 RT_NOREF_PV(pCdFsi);
1327 LogFlow(("FS32_RMDIR: returns %u\n", rc));
1328 return rc;
1329}
1330
1331
1332DECLASM(APIRET)
1333FS32_RMDIR(PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszDir, LONG offCurDirEnd)
1334{
1335 LogFlow(("FS32_RMDIR: pCdFsi=%p pCdFsd=%p pszDir=%p:{%s} offCurDirEnd=%d\n", pCdFsi, pCdFsd, pszDir, pszDir, offCurDirEnd));
1336
1337 /*
1338 * Resolve the path.
1339 */
1340 PVBOXSFFOLDER pFolder;
1341 PSHFLSTRING pStrFolderPath;
1342 APIRET rc = vboxSfOs2ResolvePath(pszDir, pCdFsd, offCurDirEnd, &pFolder, &pStrFolderPath);
1343 if (rc == NO_ERROR)
1344 {
1345 int vrc = VbglR0SfRemove(&g_SfClient, &pFolder->hHostFolder, pStrFolderPath, SHFL_REMOVE_DIR);
1346 LogFlow(("FS32_RMDIR: VbglR0SfRemove -> %Rrc\n", rc));
1347 if (RT_SUCCESS(vrc))
1348 rc = NO_ERROR;
1349 else
1350 rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_ACCESS_DENIED);
1351
1352 vboxSfOs2ReleasePathAndFolder(pStrFolderPath, pFolder);
1353 }
1354
1355 RT_NOREF_PV(pCdFsi);
1356 LogFlow(("FS32_RMDIR: returns %u\n", rc));
1357 return rc;
1358}
1359
1360
1361DECLASM(APIRET)
1362FS32_COPY(ULONG fFlags, PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszSrc, LONG offSrcCurDirEnd,
1363 PCSZ pszDst, LONG offDstCurDirEnd, ULONG uNameType)
1364{
1365 LogFlow(("FS32_COPY: fFlags=%#x pCdFsi=%p pCdFsd=%p pszSrc=%p:{%s} offSrcCurDirEnd=%d pszDst=%p:{%s} offDstCurDirEnd=%d uNameType=%#x\n",
1366 fFlags, pCdFsi, pCdFsd, pszSrc, pszSrc, offSrcCurDirEnd, pszDst, pszDst, offDstCurDirEnd, uNameType));
1367 NOREF(fFlags); NOREF(pCdFsi); NOREF(pCdFsd); NOREF(pszSrc); NOREF(offSrcCurDirEnd);
1368 NOREF(pszDst); NOREF(offDstCurDirEnd); NOREF(uNameType);
1369
1370 /* Let DOSCALL1.DLL do the work for us till we get a host side function for doing this. */
1371 return ERROR_CANNOT_COPY;
1372}
1373
1374
1375DECLASM(APIRET)
1376FS32_MOVE(PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszSrc, LONG offSrcCurDirEnd, PCSZ pszDst, LONG offDstCurDirEnd, ULONG uNameType)
1377{
1378 LogFlow(("FS32_MOVE: pCdFsi=%p pCdFsd=%p pszSrc=%p:{%s} offSrcCurDirEnd=%d pszDst=%p:{%s} offDstcurDirEnd=%d uNameType=%#x\n",
1379 pCdFsi, pCdFsd, pszSrc, pszSrc, offSrcCurDirEnd, pszDst, pszDst, offDstCurDirEnd, uNameType));
1380
1381 /*
1382 * Resolve the source and destination paths and check that they
1383 * refer to the same folder.
1384 */
1385 PVBOXSFFOLDER pSrcFolder;
1386 PSHFLSTRING pSrcFolderPath;
1387 APIRET rc = vboxSfOs2ResolvePath(pszSrc, pCdFsd, offSrcCurDirEnd, &pSrcFolder, &pSrcFolderPath);
1388 if (rc == NO_ERROR)
1389 {
1390 PVBOXSFFOLDER pDstFolder;
1391 PSHFLSTRING pDstFolderPath;
1392 rc = vboxSfOs2ResolvePath(pszDst, pCdFsd, offDstCurDirEnd, &pDstFolder, &pDstFolderPath);
1393 if (rc == NO_ERROR)
1394 {
1395 if (pSrcFolder == pDstFolder)
1396 {
1397 /*
1398 * Do the renaming.
1399 * Note! Requires 6.0.0beta2+ or 5.2.24+ host for renaming files.
1400 */
1401 int vrc = VbglR0SfRename(&g_SfClient, &pSrcFolder->hHostFolder, pSrcFolderPath, pDstFolderPath,
1402 SHFL_RENAME_FILE | SHFL_RENAME_DIR);
1403 if (RT_SUCCESS(vrc))
1404 rc = NO_ERROR;
1405 else
1406 {
1407 Log(("FS32_MOVE: VbglR0SfRename failed: %Rrc\n", rc));
1408 rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_ACCESS_DENIED);
1409 }
1410 }
1411 else
1412 {
1413 Log(("FS32_MOVE: source folder '%s' != destiation folder '%s'\n", pSrcFolder->szName, pDstFolder->szName));
1414 rc = ERROR_NOT_SAME_DEVICE;
1415 }
1416 vboxSfOs2ReleasePathAndFolder(pDstFolderPath, pDstFolder);
1417 }
1418 vboxSfOs2ReleasePathAndFolder(pSrcFolderPath, pSrcFolder);
1419 }
1420
1421 RT_NOREF_PV(pCdFsi); RT_NOREF_PV(uNameType);
1422 return rc;
1423}
1424
1425
1426DECLASM(APIRET)
1427FS32_DELETE(PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszFile, LONG offCurDirEnd)
1428{
1429 LogFlow(("FS32_DELETE: pCdFsi=%p pCdFsd=%p pszFile=%p:{%s} offCurDirEnd=%d\n", pCdFsi, pCdFsd, pszFile, pszFile, offCurDirEnd));
1430
1431 /*
1432 * Resolve the path.
1433 */
1434 PVBOXSFFOLDER pFolder;
1435 PSHFLSTRING pStrFolderPath;
1436 APIRET rc = vboxSfOs2ResolvePath(pszFile, pCdFsd, offCurDirEnd, &pFolder, &pStrFolderPath);
1437 if (rc == NO_ERROR)
1438 {
1439 int vrc = VbglR0SfRemove(&g_SfClient, &pFolder->hHostFolder, pStrFolderPath, SHFL_REMOVE_FILE);
1440 LogFlow(("FS32_DELETE: VbglR0SfRemove -> %Rrc\n", rc));
1441 if (RT_SUCCESS(vrc))
1442 rc = NO_ERROR;
1443 else if (rc == VERR_FILE_NOT_FOUND)
1444 rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_ACCESS_DENIED);
1445
1446 vboxSfOs2ReleasePathAndFolder(pStrFolderPath, pFolder);
1447 }
1448
1449 RT_NOREF_PV(pCdFsi);
1450 LogFlow(("FS32_DELETE: returns %u\n", rc));
1451 return rc;
1452}
1453
1454
1455
1456/**
1457 * Worker for FS32_PATHINFO that handles file stat setting.
1458 *
1459 * @returns OS/2 status code
1460 * @param pFolder The folder.
1461 * @param hHostFile The host file handle.
1462 * @param fAttribs The attributes to set.
1463 * @param pTimestamps Pointer to the timestamps. NULL if none should be
1464 * modified.
1465 * @param pObjInfoBuf Buffer to use when setting the attributes (host will
1466 * return current info upon successful return).
1467 */
1468APIRET vboxSfOs2SetInfoCommonWorker(PVBOXSFFOLDER pFolder, SHFLHANDLE hHostFile, ULONG fAttribs,
1469 PFILESTATUS pTimestamps, PSHFLFSOBJINFO pObjInfoBuf)
1470{
1471 /*
1472 * Validate the data a little and convert it to host speak.
1473 * When the date part is zero, the timestamp should not be updated.
1474 */
1475 RT_ZERO(*pObjInfoBuf);
1476 uint16_t cDelta = vboxSfOs2GetLocalTimeDelta();
1477
1478 /** @todo should we validate attributes? */
1479 pObjInfoBuf->Attr.fMode = (fAttribs << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_OS2;
1480
1481 if (pTimestamps)
1482 {
1483 if ( *(uint16_t *)&pTimestamps->fdateCreation != 0
1484 && !vboxSfOs2DateTimeToTimeSpec(pTimestamps->fdateCreation, pTimestamps->ftimeCreation, cDelta, &pObjInfoBuf->BirthTime))
1485 {
1486 LogRel(("vboxSfOs2SetInfoCommonWorker: Bad creation timestamp: %u-%u-%u %u:%u:%u\n",
1487 pTimestamps->fdateCreation.year + 1980, pTimestamps->fdateCreation.month, pTimestamps->fdateCreation.day,
1488 pTimestamps->ftimeCreation.hours, pTimestamps->ftimeCreation.minutes, pTimestamps->ftimeCreation.twosecs * 2));
1489 return ERROR_INVALID_PARAMETER;
1490 }
1491 if ( *(uint16_t *)&pTimestamps->fdateLastAccess != 0
1492 && !vboxSfOs2DateTimeToTimeSpec(pTimestamps->fdateLastAccess, pTimestamps->ftimeLastAccess, cDelta, &pObjInfoBuf->AccessTime))
1493 {
1494 LogRel(("vboxSfOs2SetInfoCommonWorker: Bad last access timestamp: %u-%u-%u %u:%u:%u\n",
1495 pTimestamps->fdateLastAccess.year + 1980, pTimestamps->fdateLastAccess.month, pTimestamps->fdateLastAccess.day,
1496 pTimestamps->ftimeLastAccess.hours, pTimestamps->ftimeLastAccess.minutes, pTimestamps->ftimeLastAccess.twosecs * 2));
1497 return ERROR_INVALID_PARAMETER;
1498 }
1499 if ( *(uint16_t *)&pTimestamps->fdateLastWrite != 0
1500 && !vboxSfOs2DateTimeToTimeSpec(pTimestamps->fdateLastWrite, pTimestamps->ftimeLastWrite, cDelta, &pObjInfoBuf->ModificationTime))
1501 {
1502 LogRel(("vboxSfOs2SetInfoCommonWorker: Bad last access timestamp: %u-%u-%u %u:%u:%u\n",
1503 pTimestamps->fdateLastWrite.year + 1980, pTimestamps->fdateLastWrite.month, pTimestamps->fdateLastWrite.day,
1504 pTimestamps->ftimeLastWrite.hours, pTimestamps->ftimeLastWrite.minutes, pTimestamps->ftimeLastWrite.twosecs * 2));
1505 return ERROR_INVALID_PARAMETER;
1506 }
1507 }
1508
1509 /*
1510 * Call the host to do the updating.
1511 */
1512 uint32_t cbBuf = sizeof(*pObjInfoBuf);
1513 int vrc = VbglR0SfFsInfo(&g_SfClient, &pFolder->hHostFolder, hHostFile, SHFL_INFO_SET | SHFL_INFO_FILE,
1514 &cbBuf, (SHFLDIRINFO *)pObjInfoBuf);
1515 LogFlow(("vboxSfOs2SetFileInfo: VbglR0SfFsInfo -> %Rrc\n", vrc));
1516
1517 if (RT_SUCCESS(vrc))
1518 return NO_ERROR;
1519 return vboxSfOs2ConvertStatusToOs2(vrc, ERROR_ACCESS_DENIED);
1520}
1521
1522
1523/**
1524 * Worker for FS32_FILEATTRIBUTE and FS32_PATHINFO that handles setting stuff.
1525 *
1526 * @returns OS/2 status code.
1527 * @param pFolder The folder.
1528 * @param pFolderPath The path within the folder.
1529 * @param fAttribs New file attributes.
1530 * @param pTimestamps New timestamps. May be NULL.
1531 */
1532static APIRET vboxSfOs2SetPathInfoWorker(PVBOXSFFOLDER pFolder, PSHFLSTRING pFolderPath, ULONG fAttribs, PFILESTATUS pTimestamps)
1533
1534{
1535 /*
1536 * In order to do anything we need to open the object.
1537 */
1538 APIRET rc;
1539 SHFLCREATEPARMS *pParams = (SHFLCREATEPARMS *)VbglR0PhysHeapAlloc(sizeof(*pParams));
1540 if (pParams)
1541 {
1542 RT_ZERO(*pParams);
1543 pParams->CreateFlags = SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW
1544 | SHFL_CF_ACCESS_ATTR_READWRITE | SHFL_CF_ACCESS_DENYNONE | SHFL_CF_ACCESS_NONE;
1545
1546 int vrc = VbglR0SfCreate(&g_SfClient, &pFolder->hHostFolder, pFolderPath, pParams);
1547 LogFlow(("vboxSfOs2SetPathInfoWorker: VbglR0SfCreate -> %Rrc Result=%d Handle=%#RX64 fMode=%#x\n",
1548 vrc, pParams->Result, pParams->Handle, pParams->Info.Attr.fMode));
1549 if ( vrc == VERR_IS_A_DIRECTORY
1550 || ( RT_SUCCESS(vrc)
1551 && pParams->Handle == SHFL_HANDLE_NIL
1552 && RTFS_IS_DIRECTORY(pParams->Info.Attr.fMode)))
1553 {
1554 RT_ZERO(*pParams);
1555 pParams->CreateFlags = SHFL_CF_DIRECTORY | SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW
1556 | SHFL_CF_ACCESS_ATTR_READWRITE | SHFL_CF_ACCESS_DENYNONE | SHFL_CF_ACCESS_NONE;
1557 vrc = VbglR0SfCreate(&g_SfClient, &pFolder->hHostFolder, pFolderPath, pParams);
1558 LogFlow(("vboxSfOs2SetPathInfoWorker: VbglR0SfCreate#2 -> %Rrc Result=%d Handle=%#RX64 fMode=%#x\n",
1559 vrc, pParams->Result, pParams->Handle, pParams->Info.Attr.fMode));
1560 }
1561 if (RT_SUCCESS(vrc))
1562 {
1563 switch (pParams->Result)
1564 {
1565 case SHFL_FILE_EXISTS:
1566 if (pParams->Handle != SHFL_HANDLE_NIL)
1567 {
1568 /*
1569 * Join up with FS32_FILEINFO to do the actual setting.
1570 */
1571 rc = vboxSfOs2SetInfoCommonWorker(pFolder, pParams->Handle, fAttribs, pTimestamps, &pParams->Info);
1572
1573 vrc = VbglR0SfClose(&g_SfClient, &pFolder->hHostFolder, pParams->Handle);
1574 AssertRC(vrc);
1575 }
1576 else
1577 {
1578 LogRel(("vboxSfOs2SetPathInfoWorker: No handle! fMode=%#x\n", pParams->Info.Attr.fMode));
1579 rc = ERROR_SYS_INTERNAL;
1580 }
1581 break;
1582
1583 case SHFL_PATH_NOT_FOUND:
1584 rc = ERROR_PATH_NOT_FOUND;
1585 break;
1586
1587 default:
1588 case SHFL_FILE_NOT_FOUND:
1589 rc = ERROR_FILE_NOT_FOUND;
1590 break;
1591 }
1592 }
1593 else
1594 rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_FILE_NOT_FOUND);
1595 VbglR0PhysHeapFree(pParams);
1596 }
1597 else
1598 rc = ERROR_NOT_ENOUGH_MEMORY;
1599 return rc;
1600}
1601
1602
1603DECLASM(APIRET)
1604FS32_FILEATTRIBUTE(ULONG fFlags, PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszName, LONG offCurDirEnd, PUSHORT pfAttr)
1605{
1606 LogFlow(("FS32_FILEATTRIBUTE: fFlags=%#x pCdFsi=%p:{%#x,%s} pCdFsd=%p pszName=%p:{%s} offCurDirEnd=%d pfAttr=%p\n",
1607 fFlags, pCdFsi, pCdFsi->cdi_hVPB, pCdFsi->cdi_curdir, pCdFsd, pszName, pszName, offCurDirEnd, pfAttr));
1608 RT_NOREF(offCurDirEnd);
1609
1610 APIRET rc;
1611 if ( fFlags == FA_RETRIEVE
1612 || fFlags == FA_SET)
1613 {
1614 PVBOXSFFOLDER pFolder;
1615 PSHFLSTRING pStrFolderPath;
1616 rc = vboxSfOs2ResolvePath(pszName, pCdFsd, offCurDirEnd, &pFolder, &pStrFolderPath);
1617 LogRel(("FS32_FILEATTRIBUTE: vboxSfOs2ResolvePath: -> %u pFolder=%p\n", rc, pFolder));
1618 if (rc == NO_ERROR)
1619 {
1620 if (fFlags == FA_RETRIEVE)
1621 {
1622 /*
1623 * Query it.
1624 */
1625 SHFLCREATEPARMS *pParams = (SHFLCREATEPARMS *)VbglR0PhysHeapAlloc(sizeof(*pParams));
1626 if (pParams)
1627 {
1628 RT_ZERO(*pParams);
1629 pParams->CreateFlags = SHFL_CF_LOOKUP;
1630
1631 int vrc = VbglR0SfCreate(&g_SfClient, &pFolder->hHostFolder, pStrFolderPath, pParams);
1632 LogFlow(("FS32_FILEATTRIBUTE: VbglR0SfCreate -> %Rrc Result=%d fMode=%#x\n", vrc, pParams->Result, pParams->Info.Attr.fMode));
1633 if (RT_SUCCESS(vrc))
1634 {
1635 switch (pParams->Result)
1636 {
1637 case SHFL_FILE_EXISTS:
1638 *pfAttr = (uint16_t)((pParams->Info.Attr.fMode & RTFS_DOS_MASK_OS2) >> RTFS_DOS_SHIFT);
1639 rc = NO_ERROR;
1640 break;
1641
1642 case SHFL_PATH_NOT_FOUND:
1643 rc = ERROR_PATH_NOT_FOUND;
1644 break;
1645
1646 default:
1647 case SHFL_FILE_NOT_FOUND:
1648 rc = ERROR_FILE_NOT_FOUND;
1649 break;
1650 }
1651 }
1652 else
1653 rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_FILE_NOT_FOUND);
1654 VbglR0PhysHeapFree(pParams);
1655 }
1656 else
1657 rc = ERROR_NOT_ENOUGH_MEMORY;
1658 }
1659 else
1660 {
1661 /*
1662 * Set the info. Join paths with FS32_PATHINFO.
1663 */
1664 rc = vboxSfOs2SetPathInfoWorker(pFolder, pStrFolderPath, *pfAttr, NULL);
1665 }
1666 vboxSfOs2ReleasePathAndFolder(pStrFolderPath, pFolder);
1667 }
1668 }
1669 else
1670 {
1671 LogRel(("FS32_FILEATTRIBUTE: Unknwon flag value: %#x\n", fFlags));
1672 rc = ERROR_NOT_SUPPORTED;
1673 }
1674 LogFlow(("FS32_FILEATTRIBUTE: returns %u\n", rc));
1675 return rc;
1676}
1677
1678
1679/**
1680 * Creates an empty full EA list given a GEALIST and info level.
1681 *
1682 * @returns OS/2 status code.
1683 * @param pEaOp Kernel copy of the EA request with flattened pointers.
1684 * @param uLevel The info level being queried.
1685 * @param pcbWritten Where to return the length of the resulting list. Optional.
1686 * @param poffError User buffer address of EAOP.oError for reporting GEALIST issues.
1687 */
1688APIRET vboxSfOs2MakeEmptyEaListEx(PEAOP pEaOp, ULONG uLevel, uint32_t *pcbWritten, ULONG *poffError)
1689{
1690 ULONG cbDstList;
1691 APIRET rc;
1692
1693 /*
1694 * Levels 8 and 5 are simple.
1695 */
1696 if ( pEaOp->fpGEAList == NULL
1697 || uLevel == FI_LVL_EAS_FULL_8
1698 || uLevel == FI_LVL_EAS_FULL_5)
1699 {
1700 Log2(("vboxSfOs2MakeEmptyEaList: #1\n"));
1701 cbDstList = RT_UOFFSET_AFTER(FEALIST, cbList);
1702 rc = NO_ERROR;
1703 }
1704 /*
1705 * For levels 3 and 4 we have to do work when a request list is present.
1706 */
1707 else
1708 {
1709 ULONG cbGetEasLeft = 0;
1710 rc = KernCopyIn(&cbGetEasLeft, &pEaOp->fpGEAList->cbList, sizeof(pEaOp->fpGEAList->cbList));
1711 ULONG cbFullEasLeft = 0;
1712 if (rc == NO_ERROR)
1713 rc = KernCopyIn(&cbFullEasLeft, &pEaOp->fpFEAList->cbList, sizeof(cbFullEasLeft));
1714 if ( rc == NO_ERROR
1715 && cbGetEasLeft >= sizeof(pEaOp->fpGEAList->cbList)
1716 && cbFullEasLeft >= sizeof(pEaOp->fpFEAList->cbList))
1717 {
1718 cbGetEasLeft -= sizeof(pEaOp->fpGEAList->cbList);
1719 cbFullEasLeft -= sizeof(pEaOp->fpFEAList->cbList);
1720
1721 char *pszNameBuf = (char *)RTMemAlloc(256 + 1);
1722 if (!pszNameBuf)
1723 return ERROR_NOT_ENOUGH_MEMORY;
1724 /* Start of no-return zone. */
1725
1726 uint8_t const *pbSrc = (uint8_t const *)&pEaOp->fpGEAList->list[0]; /* user buffer! */
1727 uint8_t *pbDst = (uint8_t *)&pEaOp->fpFEAList->list[0]; /* user buffer! */
1728 Log2(("vboxSfOs2MakeEmptyEaList: %p LB %#x -> %p LB %#x...\n", pbSrc, cbGetEasLeft, pbDst, cbFullEasLeft));
1729 while (cbGetEasLeft > 0)
1730 {
1731 /*
1732 * pbSrc: GEA: BYTE cbName; char szName[];
1733 */
1734 /* Get name length. */
1735 uint8_t cbName = 0;
1736 rc = KernCopyIn(&cbName, pbSrc, sizeof(cbName));
1737 Log3(("vboxSfOs2MakeEmptyEaList: cbName=%#x rc=%u\n", cbName, rc));
1738 if (rc != NO_ERROR)
1739 break;
1740 pbSrc++;
1741 cbGetEasLeft--;
1742 if (cbName + 1 > cbGetEasLeft)
1743 {
1744 cbDstList = pbSrc - 1 - (uint8_t *)pEaOp->fpGEAList;
1745 rc = KernCopyOut(poffError, &cbDstList, sizeof(pEaOp->oError));
1746 if (rc == NO_ERROR)
1747 rc = ERROR_EA_LIST_INCONSISTENT;
1748 Log(("vboxSfOs2MakeEmptyEaList: ERROR_EA_LIST_INCONSISTENT\n"));
1749 break;
1750 }
1751
1752 /* Copy in name. */
1753 rc = KernCopyIn(pszNameBuf, pbSrc, cbName + 1);
1754 if (rc != NO_ERROR)
1755 break;
1756 Log3(("vboxSfOs2MakeEmptyEaList: szName: %.*Rhxs\n", cbName + 1, pszNameBuf));
1757 if ((char *)memchr(pszNameBuf, '\0', cbName) != &pszNameBuf[cbName])
1758 {
1759 cbDstList = pbSrc - 1 - (uint8_t *)pEaOp->fpGEAList;
1760 rc = KernCopyOut(poffError, &cbDstList, sizeof(pEaOp->oError));
1761 if (rc == NO_ERROR)
1762 rc = ERROR_INVALID_EA_NAME;
1763 Log(("vboxSfOs2MakeEmptyEaList: ERROR_INVALID_EA_NAME\n"));
1764 break;
1765 }
1766
1767 /* Skip input. */
1768 cbGetEasLeft -= cbName + 1;
1769 pbSrc += cbName + 1;
1770
1771 /*
1772 * Construct and emit output.
1773 * Note! We should technically skip duplicates here, but who cares...
1774 */
1775 if (cbName > 0)
1776 {
1777 FEA Result;
1778 if (sizeof(Result) + cbName + 1 > cbFullEasLeft)
1779 {
1780 Log(("vboxSfOs2MakeEmptyEaList: ERROR_BUFFER_OVERFLOW (%#x vs %#x)\n", sizeof(Result) + cbName + 1, cbFullEasLeft));
1781 rc = ERROR_BUFFER_OVERFLOW;
1782 break;
1783 }
1784 cbFullEasLeft -= sizeof(Result) + cbName + 1;
1785
1786 Result.fEA = 0;
1787 Result.cbName = cbName;
1788 Result.cbValue = 0;
1789 rc = KernCopyOut(pbDst, &Result, sizeof(Result));
1790 if (rc != NO_ERROR)
1791 break;
1792 pbDst += sizeof(Result);
1793
1794 rc = KernCopyOut(pbDst, pszNameBuf, cbName + 1);
1795 if (rc != NO_ERROR)
1796 break;
1797 pbDst += cbName + 1;
1798 }
1799 } /* (while more GEAs) */
1800
1801 /* End of no-return zone. */
1802 RTMemFree(pszNameBuf);
1803
1804 cbDstList = (uintptr_t)pbDst - (uintptr_t)pEaOp->fpFEAList;
1805 }
1806 else
1807 {
1808 if (rc == NO_ERROR)
1809 rc = ERROR_BUFFER_OVERFLOW;
1810 cbDstList = 0; /* oh, shut up. */
1811 }
1812
1813 }
1814
1815 /* Set the list length. */
1816 if (rc == NO_ERROR)
1817 rc = KernCopyOut(&pEaOp->fpFEAList->cbList, &cbDstList, sizeof(pEaOp->fpFEAList->cbList));
1818
1819 if (pcbWritten)
1820 *pcbWritten = cbDstList;
1821
1822 Log(("vboxSfOs2MakeEmptyEaList: return %u (cbDstList=%#x)\n", rc, cbDstList));
1823 return rc;
1824}
1825
1826
1827
1828/**
1829 * Creates an empty full EA list given a GEALIST and info level.
1830 *
1831 * @returns OS/2 status code.
1832 * @param pEaOp The EA request. User buffer.
1833 * @param uLevel The info level being queried.
1834 */
1835DECL_NO_INLINE(RT_NOTHING, APIRET)
1836vboxSfOs2MakeEmptyEaList(PEAOP pEaOp, ULONG uLevel)
1837{
1838 /*
1839 * Copy the user request into memory, do pointer conversion, and
1840 * join extended function version.
1841 */
1842 EAOP EaOp = { NULL, NULL, 0 };
1843 APIRET rc = KernCopyIn(&EaOp, pEaOp, sizeof(EaOp));
1844 if (rc == NO_ERROR)
1845 {
1846 Log2(("vboxSfOs2MakeEmptyEaList: #0: %p %p %#x\n", EaOp.fpGEAList, EaOp.fpFEAList, EaOp.oError));
1847 EaOp.fpFEAList = (PFEALIST)KernSelToFlat((uintptr_t)EaOp.fpFEAList);
1848 EaOp.fpGEAList = (PGEALIST)KernSelToFlat((uintptr_t)EaOp.fpGEAList);
1849 Log2(("vboxSfOs2MakeEmptyEaList: #0b: %p %p\n", EaOp.fpGEAList, EaOp.fpFEAList));
1850
1851 rc = vboxSfOs2MakeEmptyEaListEx(&EaOp, uLevel, NULL, &pEaOp->oError);
1852 }
1853 return rc;
1854}
1855
1856
1857/**
1858 * Corrects the case of the given path.
1859 *
1860 * @returns OS/2 status code
1861 * @param pFolder The folder.
1862 * @param pStrFolderPath The path within the folder.
1863 * @param pszPath The original path for figuring the drive letter or
1864 * UNC part of the path.
1865 * @param pbData Where to return the data (user address).
1866 * @param cbData The maximum amount of data we can return.
1867 */
1868static int vboxSfOs2QueryCorrectCase(PVBOXSFFOLDER pFolder, PSHFLSTRING pStrFolderPath, const char *pszPath,
1869 PBYTE pbData, ULONG cbData)
1870{
1871/** @todo do case correction. Can do step-by-step dir info... but slow */
1872 RT_NOREF(pFolder, pStrFolderPath, pszPath, pbData, cbData);
1873 return ERROR_NOT_SUPPORTED;
1874}
1875
1876
1877/**
1878 * Copy out file status info.
1879 *
1880 * @returns OS/2 status code.
1881 * @param pbDst User address to put the status info at.
1882 * @param cbDst The size of the structure to produce.
1883 * @param uLevel The info level of the structure to produce.
1884 * @param pSrc The shared folder FS object info source structure.
1885 * @note Careful with stack, thus no-inlining.
1886 */
1887DECL_NO_INLINE(RT_NOTHING, APIRET)
1888vboxSfOs2FileStatusFromObjInfo(PBYTE pbDst, ULONG cbDst, ULONG uLevel, SHFLFSOBJINFO const *pSrc)
1889{
1890 union
1891 {
1892 FILESTATUS Fst;
1893 FILESTATUS2 Fst2;
1894 FILESTATUS3L Fst3L;
1895 FILESTATUS4L Fst4L;
1896 } uTmp;
1897
1898 int16_t cMinLocalTimeDelta = vboxSfOs2GetLocalTimeDelta();
1899 vboxSfOs2DateTimeFromTimeSpec(&uTmp.Fst.fdateCreation, &uTmp.Fst.ftimeCreation, pSrc->BirthTime, cMinLocalTimeDelta);
1900 vboxSfOs2DateTimeFromTimeSpec(&uTmp.Fst.fdateLastAccess, &uTmp.Fst.ftimeLastAccess, pSrc->AccessTime, cMinLocalTimeDelta);
1901 vboxSfOs2DateTimeFromTimeSpec(&uTmp.Fst.fdateLastWrite, &uTmp.Fst.ftimeLastWrite, pSrc->ModificationTime, cMinLocalTimeDelta);
1902 if (uLevel < FI_LVL_STANDARD_64)
1903 {
1904 uTmp.Fst.cbFile = (uint32_t)RT_MIN(pSrc->cbObject, UINT32_MAX);
1905 uTmp.Fst.cbFileAlloc = (uint32_t)RT_MIN(pSrc->cbAllocated, UINT32_MAX);
1906 uTmp.Fst.attrFile = (uint16_t)((pSrc->Attr.fMode & RTFS_DOS_MASK_OS2) >> RTFS_DOS_SHIFT);
1907 if (uLevel == FI_LVL_STANDARD_EASIZE)
1908 uTmp.Fst2.cbList = 0;
1909 }
1910 else
1911 {
1912 uTmp.Fst3L.cbFile = pSrc->cbObject;
1913 uTmp.Fst3L.cbFileAlloc = pSrc->cbAllocated;
1914 uTmp.Fst3L.attrFile = (pSrc->Attr.fMode & RTFS_DOS_MASK_OS2) >> RTFS_DOS_SHIFT;
1915 uTmp.Fst4L.cbList = 0;
1916 }
1917
1918 return KernCopyOut(pbDst, &uTmp, cbDst);
1919}
1920
1921
1922
1923/**
1924 * Worker for FS32_PATHINFO that handles file stat queries.
1925 *
1926 * @returns OS/2 status code
1927 * @param pFolder The folder.
1928 * @param pStrFolderPath The path within the folder.
1929 * @param uLevel The information level.
1930 * @param pbData Where to return the data (user address).
1931 * @param cbData The amount of data to produce.
1932 */
1933static APIRET vboxSfOs2QueryPathInfo(PVBOXSFFOLDER pFolder, PSHFLSTRING pStrFolderPath, ULONG uLevel, PBYTE pbData, ULONG cbData)
1934{
1935 APIRET rc;
1936 SHFLCREATEPARMS *pParams = (SHFLCREATEPARMS *)VbglR0PhysHeapAlloc(sizeof(*pParams));
1937 if (pParams)
1938 {
1939 RT_ZERO(*pParams);
1940 pParams->CreateFlags = SHFL_CF_LOOKUP;
1941
1942 int vrc = VbglR0SfCreate(&g_SfClient, &pFolder->hHostFolder, pStrFolderPath, pParams);
1943 LogFlow(("FS32_PATHINFO: VbglR0SfCreate -> %Rrc Result=%d fMode=%#x\n", vrc, pParams->Result, pParams->Info.Attr.fMode));
1944 if (RT_SUCCESS(vrc))
1945 {
1946 switch (pParams->Result)
1947 {
1948 case SHFL_FILE_EXISTS:
1949 switch (uLevel)
1950 {
1951 /*
1952 * Produce the desired file stat data.
1953 */
1954 case FI_LVL_STANDARD:
1955 case FI_LVL_STANDARD_EASIZE:
1956 case FI_LVL_STANDARD_64:
1957 case FI_LVL_STANDARD_EASIZE_64:
1958 rc = vboxSfOs2FileStatusFromObjInfo(pbData, cbData, uLevel, &pParams->Info);
1959 break;
1960
1961 /*
1962 * We don't do EAs and we "just" need to return no-EAs.
1963 * However, that's not as easy as you might think.
1964 */
1965 case FI_LVL_EAS_FROM_LIST:
1966 case FI_LVL_EAS_FULL:
1967 case FI_LVL_EAS_FULL_5:
1968 case FI_LVL_EAS_FULL_8:
1969 rc = vboxSfOs2MakeEmptyEaList((PEAOP)pbData, uLevel);
1970 break;
1971
1972 default:
1973 AssertFailed();
1974 rc = ERROR_GEN_FAILURE;
1975 break;
1976 }
1977 break;
1978
1979 case SHFL_PATH_NOT_FOUND:
1980 rc = ERROR_PATH_NOT_FOUND;
1981 break;
1982
1983 default:
1984 case SHFL_FILE_NOT_FOUND:
1985 rc = ERROR_FILE_NOT_FOUND;
1986 break;
1987 }
1988 }
1989 else
1990 rc = vboxSfOs2ConvertStatusToOs2(rc, ERROR_FILE_NOT_FOUND);
1991 VbglR0PhysHeapFree(pParams);
1992 }
1993 else
1994 rc = ERROR_NOT_ENOUGH_MEMORY;
1995 return rc;
1996}
1997
1998
1999DECLASM(APIRET)
2000FS32_PATHINFO(USHORT fFlags, PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszPath, LONG offCurDirEnd,
2001 ULONG uLevel, PBYTE pbData, ULONG cbData)
2002{
2003 LogFlow(("FS32_PATHINFO: fFlags=%#x pCdFsi=%p:{%#x,%s} pCdFsd=%p pszPath=%p:{%s} offCurDirEnd=%d uLevel=%u pbData=%p cbData=%#x\n",
2004 fFlags, pCdFsi, pCdFsi->cdi_hVPB, pCdFsi->cdi_curdir, pCdFsd, pszPath, pszPath, offCurDirEnd, uLevel, pbData, cbData));
2005
2006 /*
2007 * Check the level.
2008 *
2009 * Note! You would think this is FIL_STANDARD, FIL_QUERYEASIZE,
2010 * FIL_QUERYEASFROMLISTL and such. However, there are several levels
2011 * (4/14, 6/16, 7/17, 8/18) that are not defined in os2.h and then
2012 * there and FIL_QUERYFULLNAME that is used very between the kernel
2013 * and the FSD so the kernel can implement DosEnumAttributes.
2014 *
2015 * Note! DOSCALL1.DLL has code for converting FILESTATUS to FILESTATUS3
2016 * and FILESTATUS2 to FILESTATUS4 as needed. We don't need to do this.
2017 * It also has weird code for doubling the FILESTATUS2.cbList value
2018 * for no apparent reason.
2019 */
2020 ULONG cbMinData;
2021 switch (uLevel)
2022 {
2023 case FI_LVL_STANDARD:
2024 cbMinData = sizeof(FILESTATUS);
2025 AssertCompileSize(FILESTATUS, 0x16);
2026 break;
2027 case FI_LVL_STANDARD_64:
2028 cbMinData = sizeof(FILESTATUS3L);
2029 AssertCompileSize(FILESTATUS3L, 0x20); /* cbFile and cbFileAlloc are misaligned. */
2030 break;
2031 case FI_LVL_STANDARD_EASIZE:
2032 cbMinData = sizeof(FILESTATUS2);
2033 AssertCompileSize(FILESTATUS2, 0x1a);
2034 break;
2035 case FI_LVL_STANDARD_EASIZE_64:
2036 cbMinData = sizeof(FILESTATUS4L);
2037 AssertCompileSize(FILESTATUS4L, 0x24); /* cbFile and cbFileAlloc are misaligned. */
2038 break;
2039 case FI_LVL_EAS_FROM_LIST:
2040 case FI_LVL_EAS_FULL:
2041 case FI_LVL_EAS_FULL_5:
2042 case FI_LVL_EAS_FULL_8:
2043 cbMinData = sizeof(EAOP);
2044 break;
2045 case FI_LVL_VERIFY_PATH:
2046 case FI_LVL_CASE_CORRECT_PATH:
2047 cbMinData = 1;
2048 break;
2049 default:
2050 LogRel(("FS32_PATHINFO: Unsupported info level %u!\n", uLevel));
2051 return ERROR_INVALID_LEVEL;
2052 }
2053 if (cbData < cbMinData || pbData == NULL)
2054 {
2055 Log(("FS32_PATHINFO: ERROR_BUFFER_OVERFLOW (cbMinData=%#x, cbData=%#x, pszPath=%s)\n", cbMinData, cbData, pszPath));
2056 return ERROR_BUFFER_OVERFLOW;
2057 }
2058
2059 /*
2060 * Resolve the path to a folder and folder path.
2061 */
2062 PVBOXSFFOLDER pFolder;
2063 PSHFLSTRING pStrFolderPath;
2064 APIRET rc = vboxSfOs2ResolvePath(pszPath, pCdFsd, offCurDirEnd, &pFolder, &pStrFolderPath);
2065 LogFlow(("FS32_PATHINFO: vboxSfOs2ResolvePath: -> %u pFolder=%p\n", rc, pFolder));
2066 if (rc == NO_ERROR)
2067 {
2068 /*
2069 * Query information.
2070 */
2071 if (fFlags == PI_RETRIEVE)
2072 {
2073 if ( uLevel != FI_LVL_VERIFY_PATH
2074 && uLevel != FI_LVL_CASE_CORRECT_PATH)
2075 rc = vboxSfOs2QueryPathInfo(pFolder, pStrFolderPath, uLevel, pbData, cbMinData);
2076 else if (uLevel == FI_LVL_VERIFY_PATH)
2077 rc = NO_ERROR; /* vboxSfOs2ResolvePath should've taken care of this already */
2078 else
2079 rc = vboxSfOs2QueryCorrectCase(pFolder, pStrFolderPath, pszPath, pbData, cbData);
2080 }
2081 /*
2082 * Update information.
2083 */
2084 else if ( fFlags == PI_SET
2085 || fFlags == (PI_SET | PI_WRITE_THRU))
2086 {
2087 if ( uLevel == FI_LVL_STANDARD
2088 || uLevel == FI_LVL_STANDARD_64)
2089 {
2090 /* Read in the data and join paths with FS32_FILEATTRIBUTE: */
2091 PFILESTATUS pDataCopy = (PFILESTATUS)VbglR0PhysHeapAlloc(cbMinData);
2092 if (pDataCopy)
2093 {
2094 rc = KernCopyIn(pDataCopy, pbData, cbMinData);
2095 if (rc == NO_ERROR)
2096 rc = vboxSfOs2SetPathInfoWorker(pFolder, pStrFolderPath,
2097 uLevel == FI_LVL_STANDARD
2098 ? (ULONG)pDataCopy->attrFile
2099 : ((PFILESTATUS3L)pDataCopy)->attrFile,
2100 (PFILESTATUS)pDataCopy);
2101 VbglR0PhysHeapFree(pDataCopy);
2102 }
2103 else
2104 rc = ERROR_NOT_ENOUGH_MEMORY;
2105 }
2106 else if (uLevel == FI_LVL_STANDARD_EASIZE)
2107 rc = ERROR_EAS_NOT_SUPPORTED;
2108 else
2109 rc = ERROR_INVALID_LEVEL;
2110 }
2111 else
2112 {
2113 LogRel(("FS32_PATHINFO: Unknown flags value: %#x (path: %s)\n", fFlags, pszPath));
2114 rc = ERROR_INVALID_PARAMETER;
2115 }
2116 vboxSfOs2ReleasePathAndFolder(pStrFolderPath, pFolder);
2117 }
2118 RT_NOREF_PV(pCdFsi);
2119 return rc;
2120}
2121
2122
2123DECLASM(APIRET)
2124FS32_MOUNT(USHORT fFlags, PVPFSI pvpfsi, PVBOXSFVP pVpFsd, USHORT hVPB, PCSZ pszBoot)
2125{
2126 NOREF(fFlags); NOREF(pvpfsi); NOREF(pVpFsd); NOREF(hVPB); NOREF(pszBoot);
2127 return ERROR_NOT_SUPPORTED;
2128}
2129
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