VirtualBox

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

Last change on this file was 93073, checked in by vboxsync, 2 years ago

os2/VBoxSF: Implemented ERROR_EAS_DIDNT_FIT. ticketref:19453

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 37.9 KB
RevLine 
[3655]1/** $Id: VBoxSFFind.cpp 93073 2021-12-24 01:44:58Z vboxsync $ */
2/** @file
3 * VBoxSF - OS/2 Shared Folders, Find File IFS EPs.
4 */
5
6/*
[75337]7 * Copyright (c) 2007-2018 knut st. osmundsen <bird-src-spam@anduin.net>
[3655]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
[57358]32/*********************************************************************************************************************************
33* Header Files *
34*********************************************************************************************************************************/
[3655]35#define LOG_GROUP LOG_GROUP_DEFAULT
36#include "VBoxSFInternal.h"
37
38#include <VBox/log.h>
[75337]39#include <iprt/asm.h>
[3655]40#include <iprt/assert.h>
[75337]41#include <iprt/mem.h>
42#include <iprt/path.h>
43#include <iprt/err.h>
[3655]44
45
[75337]46
47/**
48 * Checks if the given name is 8-dot-3 compatible.
49 *
50 * @returns true if compatible, false if not.
[76108]51 * @param pwszName The name to inspect (UTF-16).
52 * @param cwcName The length of the name.
53 * @param pszTmp Buffer for test conversions.
54 * @param cbTmp The size of the buffer.
[75337]55 */
[76108]56static bool vboxSfOs2IsUtf16Name8dot3(PRTUTF16 pwszName, size_t cwcName, char *pszTmp, size_t cbTmp)
[3655]57{
[76108]58 /* Reject names that must be too long. */
59 if (cwcName > 8 + 1 + 3)
[75337]60 return false;
61
[76108]62 /* First char cannot be a dot, nor can it be an empty string. */
63 if (*pwszName == '.' || !*pwszName)
[75337]64 return false;
65
66 /*
67 * To basic checks on code point level before doing full conversion.
68 */
[76108]69 for (unsigned off = 0; ; off++)
[75337]70 {
[76108]71 RTUTF16 wc = pwszName[off];
72 if (wc == '.')
[75337]73 {
[76108]74 unsigned const offMax = off + 3;
75 for (++off;; off++)
[75337]76 {
[76108]77 wc = pwszName[off];
78 if (!wc)
[75337]79 break;
[76108]80 if (wc == '.')
[75337]81 return false;
[84484]82 if (off > offMax)
[75337]83 return false;
84 }
85 break;
86 }
[76108]87 if (!wc)
[75337]88 break;
[76108]89 if (off >= 8)
[75337]90 return false;
91 }
92
93 /*
[84484]94 * Convert to the native code page.
[75337]95 */
[77640]96 APIRET rc = SafeKernStrFromUcs(NULL, pszTmp, pwszName, cbTmp, cwcName);
[76108]97 if (rc != NO_ERROR)
[75337]98 {
[77640]99 LogRel(("vboxSfOs2IsUtf8Name8dot3: SafeKernStrFromUcs failed: %d\n", rc));
[76108]100 return false;
[75337]101 }
[3655]102
[76108]103 /*
104 * Redo the check.
105 * Note! This could be bogus if a DBCS leadin sequence collides with '.'.
106 */
107 for (unsigned cch = 0; ; cch++)
[75337]108 {
[76108]109 char ch = *pszTmp++;
110 if (ch == '.')
111 break;
112 if (ch == '\0')
113 return true;
114 if (cch >= 8)
115 return false;
[75337]116 }
[76108]117 for (unsigned cch = 0; ; cch++)
118 {
119 char ch = *pszTmp++;
120 if (ch == '\0')
121 return true;
[84484]122 if (ch == '.')
[76108]123 return false;
124 if (cch >= 3)
125 return false;
126 }
[3655]127}
128
129
[75337]130/**
131 * @returns Updated pbDst on success, NULL on failure.
132 */
[76108]133static uint8_t *vboxSfOs2CopyUtf16Name(uint8_t *pbDst, PRTUTF16 pwszSrc, size_t cwcSrc)
[3655]134{
[76108]135 char *pszDst = (char *)pbDst + 1;
[77640]136 APIRET rc = SafeKernStrFromUcs(NULL, pszDst, pwszSrc, CCHMAXPATHCOMP, cwcSrc);
[76108]137 if (rc == NO_ERROR)
[75337]138 {
[76108]139 size_t cchDst = strlen(pszDst);
140 *pbDst++ = (uint8_t)cchDst;
141 pbDst += cchDst;
142 *pbDst++ = '\0';
143 return pbDst;
[75337]144 }
[77640]145 LogRel(("vboxSfOs2CopyUtf8Name: SafeKernStrFromUcs failed: %d\n", rc));
[75337]146 return NULL;
[3655]147}
148
149
[75337]150/**
151 * @returns Updated pbDst on success, NULL on failure.
152 */
153static uint8_t *vboxSfOs2CopyUtf16NameAndUpperCase(uint8_t *pbDst, PRTUTF16 pwszSrc, size_t cwcSrc)
[3655]154{
[75337]155 char *pszDst = (char *)(pbDst + 1);
[77640]156 APIRET rc = SafeKernStrFromUcs(NULL, pszDst, RTUtf16ToUpper(pwszSrc), CCHMAXPATHCOMP, cwcSrc);
[75337]157 if (rc == NO_ERROR)
158 {
159 size_t cchDst = strlen(pszDst);
160 *pbDst++ = (uint8_t)cchDst;
161 pbDst += cchDst;
162 *pbDst++ = '\0';
163 return pbDst;
164 }
[77640]165 LogRel(("vboxSfOs2CopyUtf16NameAndUpperCase: SafeKernStrFromUcs failed: %#x\n", rc));
[75337]166 return NULL;
[3655]167}
168
169
170
[75337]171/**
172 * Worker for FS32_FINDFIRST, FS32_FINDNEXT and FS32_FINDFROMNAME.
173 *
174 * @returns OS/2 status code.
175 * @param pFolder The folder we're working on.
176 * @param pFsFsd The search handle data.
177 * @param pDataBuf The search data buffer (some handle data there too).
178 * @param uLevel The info level to return.
179 * @param fFlags Position flag.
180 * @param pbData The output buffer.
181 * @param cbData The size of the output buffer.
182 * @param cMaxMatches The maximum number of matches to return.
183 * @param pcMatches Where to set the number of returned matches.
184 */
185static APIRET vboxSfOs2ReadDirEntries(PVBOXSFFOLDER pFolder, PVBOXSFFS pFsFsd, PVBOXSFFSBUF pDataBuf, ULONG uLevel, ULONG fFlags,
186 PBYTE pbData, ULONG cbData, USHORT cMaxMatches, PUSHORT pcMatches)
187{
188 APIRET rc = NO_ERROR;
[3655]189
[75337]190 /*
191 * If we're doing EAs, the buffer starts with an EAOP structure.
192 */
193 EAOP EaOp;
[75461]194 PEAOP pEaOpUser = NULL; /* Shut up gcc */
[75337]195 switch (uLevel)
196 {
197 case FI_LVL_EAS_FROM_LIST:
198 case FI_LVL_EAS_FROM_LIST_64:
199 case FI_LVL_EAS_FULL:
200 case FI_LVL_EAS_FULL_5:
201 case FI_LVL_EAS_FULL_8:
202 if (cbData >= sizeof(EaOp))
203 {
204 rc = KernCopyIn(&EaOp, pbData, sizeof(EaOp));
205 if (rc == NO_ERROR)
206 {
207 EaOp.fpGEAList = (PGEALIST)KernSelToFlat((uintptr_t)EaOp.fpGEAList);
208 EaOp.fpFEAList = NULL;
[3655]209
[75337]210 pEaOpUser = (PEAOP)pbData;
211 pbData += sizeof(*pEaOpUser);
212 cbData -= sizeof(*pEaOpUser);
213 break;
214 }
215 }
216 else
217 rc = ERROR_BUFFER_OVERFLOW;
218 Log(("vboxSfOs2ReadDirEntries: Failed to read EAOP: %u\n", rc));
219 return rc;
220 }
221
222 /*
223 * Do the reading.
224 */
225 USHORT cMatches;
226 for (cMatches = 0; cMatches < cMaxMatches;)
227 {
228 /*
229 * Do we need to fetch more directory entries?
230 */
231 PSHFLDIRINFO pEntry = pDataBuf->pEntry;
232 if ( pDataBuf->cEntriesLeft == 0
233 || pEntry == NULL /* paranoia */)
234 {
[76665]235 pDataBuf->pEntry = pEntry = pDataBuf->pBuf;
[76716]236 int vrc = VbglR0SfHostReqListDir(pFolder->idHostRoot, &pDataBuf->Req, pFsFsd->hHostDir, pDataBuf->pFilter,
237 /*cMaxMatches == 1 ? SHFL_LIST_RETURN_ONE :*/ 0, pDataBuf->pBuf, pDataBuf->cbBuf);
[75337]238 if (RT_SUCCESS(vrc))
239 {
[76665]240 pDataBuf->cEntriesLeft = pDataBuf->Req.Parms.c32Entries.u.value32;
241 pDataBuf->cbValid = pDataBuf->Req.Parms.cb32Buffer.u.value32;
242 //Log(("%.*Rhxd\n", pDataBuf->cbValid, pEntry));
[75337]243 AssertReturn(pDataBuf->cbValid >= RT_UOFFSETOF(SHFLDIRINFO, name.String), ERROR_SYS_INTERNAL);
244 AssertReturn(pDataBuf->cbValid >= RT_UOFFSETOF(SHFLDIRINFO, name.String) + pEntry->name.u16Size, ERROR_SYS_INTERNAL);
[76716]245 Log4(("vboxSfOs2ReadDirEntries: VbglR0SfHostReqListDir returned %#x matches in %#x bytes\n", pDataBuf->cEntriesLeft, pDataBuf->cbValid));
[75337]246 }
247 else
248 {
249 if (vrc == VERR_NO_MORE_FILES)
[76716]250 Log4(("vboxSfOs2ReadDirEntries: VbglR0SfHostReqListDir returned VERR_NO_MORE_FILES (%d,%d)\n",
[76665]251 pDataBuf->Req.Parms.c32Entries.u.value32, pDataBuf->Req.Parms.cb32Buffer.u.value32));
[75337]252 else
[76716]253 Log(("vboxSfOs2ReadDirEntries: VbglR0SfHostReqListDir failed %Rrc (%d,%d)\n", vrc,
[76665]254 pDataBuf->Req.Parms.c32Entries.u.value32, pDataBuf->Req.Parms.cb32Buffer.u.value32));
[75337]255 pDataBuf->pEntry = NULL;
256 pDataBuf->cEntriesLeft = 0;
[76665]257 pDataBuf->cbValid = 0;
[75337]258 if (cMatches == 0)
259 {
260 if (vrc == VERR_NO_MORE_FILES)
261 rc = ERROR_NO_MORE_FILES;
262 else
263 rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_GEN_FAILURE);
264 }
265 break;
266 }
267 }
268
269 /*
270 * Do matching and stuff the return buffer.
271 */
272 if ( !((pEntry->Info.Attr.fMode >> RTFS_DOS_SHIFT) & pDataBuf->fExcludedAttribs)
273 && ((pEntry->Info.Attr.fMode >> RTFS_DOS_SHIFT) & pDataBuf->fMustHaveAttribs) == pDataBuf->fMustHaveAttribs
274 && ( pDataBuf->fLongFilenames
275 || pEntry->cucShortName
[76108]276 || vboxSfOs2IsUtf16Name8dot3(pEntry->name.String.utf16, pEntry->name.u16Length / sizeof(RTUTF16),
277 (char *)pDataBuf->abStaging, sizeof(pDataBuf->abStaging))))
[75337]278 {
279 /*
280 * We stages all but FEAs (level 3, 4, 13 and 14).
281 */
282 PBYTE const pbUserBufStart = pbData; /* In case we need to skip a bad name. */
283 uint8_t *pbToCopy = pDataBuf->abStaging;
284 uint8_t *pbDst = pbToCopy;
285
286 /* Position (originally used for FS32_FINDFROMNAME 'position', but since reused
287 for FILEFINDBUF3::oNextEntryOffset and FILEFINDBUF4::oNextEntryOffset): */
288 if (fFlags & FF_GETPOS)
289 {
290 *(uint32_t *)pbDst = pFsFsd->offLastFile + 1;
291 pbDst += sizeof(uint32_t);
292 }
293
294 /* Dates: Creation, Access, Write */
295 vboxSfOs2DateTimeFromTimeSpec((FDATE *)pbDst, (FTIME *)(pbDst + 2), pEntry->Info.BirthTime, pDataBuf->cMinLocalTimeDelta);
296 pbDst += sizeof(FDATE) + sizeof(FTIME);
297 vboxSfOs2DateTimeFromTimeSpec((FDATE *)pbDst, (FTIME *)(pbDst + 2), pEntry->Info.AccessTime, pDataBuf->cMinLocalTimeDelta);
298 pbDst += sizeof(FDATE) + sizeof(FTIME);
299 vboxSfOs2DateTimeFromTimeSpec((FDATE *)pbDst, (FTIME *)(pbDst + 2), pEntry->Info.ModificationTime, pDataBuf->cMinLocalTimeDelta);
300 pbDst += sizeof(FDATE) + sizeof(FTIME);
301
302 /* File size, allocation size, attributes: */
303 if (uLevel >= FI_LVL_STANDARD_64)
304 {
305 *(uint64_t *)pbDst = pEntry->Info.cbObject;
306 pbDst += sizeof(uint64_t);
307 *(uint64_t *)pbDst = pEntry->Info.cbAllocated;
308 pbDst += sizeof(uint64_t);
309 *(uint32_t *)pbDst = (pEntry->Info.Attr.fMode & RTFS_DOS_MASK_OS2) >> RTFS_DOS_SHIFT;
310 pbDst += sizeof(uint32_t);
311 }
312 else
313 {
314 *(uint32_t *)pbDst = (uint32_t)RT_MIN(pEntry->Info.cbObject, _2G - 1);
315 pbDst += sizeof(uint32_t);
316 *(uint32_t *)pbDst = (uint32_t)RT_MIN(pEntry->Info.cbAllocated, _2G - 1);
317 pbDst += sizeof(uint32_t);
318 *(uint16_t *)pbDst = (uint16_t)((pEntry->Info.Attr.fMode & RTFS_DOS_MASK_OS2) >> RTFS_DOS_SHIFT);
319 pbDst += sizeof(uint16_t); /* (Curious: Who is expanding this to 32-bits for 32-bit callers? */
320 }
321
322 /* Extra EA related fields: */
323 if ( uLevel == FI_LVL_STANDARD
324 || uLevel == FI_LVL_STANDARD_64)
325 { /* nothing */ }
326 else if ( uLevel == FI_LVL_STANDARD_EASIZE
327 || uLevel == FI_LVL_STANDARD_EASIZE_64)
328 {
329 /* EA size: */
330 *(uint32_t *)pbDst = 0;
331 pbDst += sizeof(uint32_t);
332 }
333 else
334 {
335 /* Empty FEALIST - flush pending data first: */
336 uint32_t cbToCopy = pbDst - pbToCopy;
337 if (cbToCopy < cbData)
338 {
339 rc = KernCopyOut(pbData, pbToCopy, cbToCopy);
340 if (rc == NO_ERROR)
341 {
342 pbData += cbToCopy;
343 cbData -= cbToCopy;
344 pbDst = pbToCopy;
345
[93071]346 /* Output empty EA list. We don't try anticipate filename output length here,
347 instead we'll just handle that when we come to it below. */
348 /** @todo If this overflows, JFS will return ERROR_EAS_DIDNT_FIT and just the
349 * EA size here (i.e. as if FI_LVL_STANDARD_EASIZE or _64 was requested).
350 * I think, however, that ERROR_EAS_DIDNT_FIT should only be considered if
351 * this is the first entry we're returning and we'll have to stop after it. */
[75337]352 uint32_t cbWritten = 0;
353 EaOp.fpFEAList = (PFEALIST)pbData;
[93071]354 rc = vboxSfOs2MakeEmptyEaListEx(&EaOp, uLevel, cbData, &cbWritten, &pEaOpUser->oError);
[75337]355 if (rc == NO_ERROR)
356 {
357 cbData -= cbWritten;
358 pbData += cbWritten;
359 }
360 }
361 }
362 else
363 rc = ERROR_BUFFER_OVERFLOW;
364 if (rc != NO_ERROR)
365 break;
366 }
367
368 /* The length prefixed filename. */
369 if (pDataBuf->fLongFilenames)
[76108]370 pbDst = vboxSfOs2CopyUtf16Name(pbDst, pEntry->name.String.utf16, pEntry->name.u16Length / sizeof(RTUTF16));
[75337]371 else if (pEntry->cucShortName == 0)
[76108]372 pbDst = vboxSfOs2CopyUtf16NameAndUpperCase(pbDst, pEntry->name.String.utf16, pEntry->name.u16Length / sizeof(RTUTF16));
[75337]373 else
374 pbDst = vboxSfOs2CopyUtf16NameAndUpperCase(pbDst, pEntry->uszShortName, pEntry->cucShortName);
375 if (pbDst)
376 {
377 /*
378 * Copy out the staged data.
379 */
380 uint32_t cbToCopy = pbDst - pbToCopy;
381 if (cbToCopy <= cbData)
382 {
383 rc = KernCopyOut(pbData, pbToCopy, cbToCopy);
384 if (rc == NO_ERROR)
385 {
386 Log4(("vboxSfOs2ReadDirEntries: match #%u LB %#x: '%s'\n", cMatches, cbToCopy, pEntry->name.String.utf8));
387 Log4(("%.*Rhxd\n", cbToCopy, pbToCopy));
388
389 pbData += cbToCopy;
390 cbData -= cbToCopy;
391 pbDst = pbToCopy;
392
393 cMatches++;
394 pFsFsd->offLastFile++;
395 }
396 else
397 break;
398 }
399 else
400 {
401 rc = ERROR_BUFFER_OVERFLOW;
402 break;
403 }
404 }
405 else
406 {
407 /* Name conversion issue, just skip the entry. */
408 Log3(("vboxSfOs2ReadDirEntries: Skipping '%s' due to name conversion issue.\n", pEntry->name.String.utf8));
409 cbData -= pbUserBufStart - pbData;
410 pbData = pbUserBufStart;
411 }
412 }
413 else
414 Log3(("vboxSfOs2ReadDirEntries: fMode=%#x filter out by %#x/%#x; '%s'\n",
415 pEntry->Info.Attr.fMode, pDataBuf->fMustHaveAttribs, pDataBuf->fExcludedAttribs, pEntry->name.String.utf8));
416
417 /*
418 * Advance to the next directory entry from the host.
419 */
420 if (pDataBuf->cEntriesLeft-- > 1)
421 {
422 pDataBuf->pEntry = pEntry = (PSHFLDIRINFO)&pEntry->name.String.utf8[pEntry->name.u16Size];
[76665]423 uintptr_t offEntry = (uintptr_t)pEntry - (uintptr_t)pDataBuf->pBuf;
[75337]424 AssertMsgReturn(offEntry + RT_UOFFSETOF(SHFLDIRINFO, name.String) <= pDataBuf->cbValid,
425 ("offEntry=%#x cbValid=%#x\n", offEntry, pDataBuf->cbValid), ERROR_SYS_INTERNAL);
[76665]426 //Log(("next entry: %p / %#x: u16Size=%#x => size: %#x\n", pEntry, offEntry, RT_UOFFSETOF(SHFLDIRINFO, name.String) + pEntry->name.u16Size));
[75337]427 AssertMsgReturn(offEntry + RT_UOFFSETOF(SHFLDIRINFO, name.String) + pEntry->name.u16Size <= pDataBuf->cbValid,
[76665]428 ("offEntry=%#x + offName=%#x + cbName=%#x => %#x; cbValid=%#x\n",
429 offEntry, RT_UOFFSETOF(SHFLDIRINFO, name.String), pEntry->name.u16Size,
430 offEntry + RT_UOFFSETOF(SHFLDIRINFO, name.String) + pEntry->name.u16Size, pDataBuf->cbValid),
431 ERROR_SYS_INTERNAL);
432 //Log(("%.*Rhxd\n", RT_UOFFSETOF(SHFLDIRINFO, name.String) + pEntry->name.u16Size, pEntry));
[75337]433 }
434 else
[76665]435 pDataBuf->pEntry = pEntry = NULL;
[75337]436 }
437
438 *pcMatches = cMatches;
439
440 /* Ignore buffer overflows if we've got matches to return. */
441 if (rc == ERROR_BUFFER_OVERFLOW && cMatches > 0)
442 rc = NO_ERROR;
443 return rc;
444}
445
446
447DECLASM(APIRET)
448FS32_FINDFIRST(PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszPath, LONG offCurDirEnd, ULONG fAttribs,
449 PFSFSI pFsFsi, PVBOXSFFS pFsFsd, PBYTE pbData, ULONG cbData, PUSHORT pcMatches, ULONG uLevel, ULONG fFlags)
[3655]450{
[77640]451 LogFlow(("FS32_FINDFIRST: pCdFsi=%p pCdFsd=%p pszPath=%p:{%s} offCurDirEnd=%d fAttribs=%#x pFsFsi=%p pFsFsd=%p pbData=%p cbData=%#x pcMatches=%p:{%#x} uLevel=%#x fFlags=%#x\n",
[75337]452 pCdFsi, pCdFsd, pszPath, pszPath, offCurDirEnd, fAttribs, pFsFsi, pFsFsd, pbData, cbData, pcMatches, *pcMatches, uLevel, fFlags));
453 USHORT const cMaxMatches = *pcMatches;
454 *pcMatches = 0;
455
456 /*
457 * Input validation.
458 */
459 switch (uLevel)
460 {
461 case FI_LVL_STANDARD:
462 case FI_LVL_STANDARD_64:
463 case FI_LVL_STANDARD_EASIZE:
464 case FI_LVL_STANDARD_EASIZE_64:
465 break;
466
467 case FI_LVL_EAS_FROM_LIST:
468 case FI_LVL_EAS_FROM_LIST_64:
469 if (cbData < sizeof(EAOP))
470 {
471 Log(("FS32_FINDFIRST: Buffer smaller than EAOP: %#x\n", cbData));
472 return ERROR_BUFFER_OVERFLOW;
473 }
474 break;
475
476 default:
477 LogRel(("FS32_FINDFIRST: Unsupported info level %u!\n", uLevel));
478 return ERROR_INVALID_LEVEL;
479 }
480
481 /*
482 * Resolve path to a folder and folder relative path.
483 */
484 RT_NOREF(pCdFsi);
[76143]485 PVBOXSFFOLDER pFolder;
486 VBOXSFCREATEREQ *pReq;
487 APIRET rc = vboxSfOs2ResolvePathEx(pszPath, pCdFsd, offCurDirEnd, RT_UOFFSETOF(VBOXSFCREATEREQ, StrPath),
488 &pFolder, (void **)&pReq);
489 LogFlow(("FS32_FINDFIRST: vboxSfOs2ResolvePathEx: -> %u pReq=%p\n", rc, pReq));
[75337]490 if (rc == NO_ERROR)
491 {
[76143]492 PSHFLSTRING pStrFolderPath = &pReq->StrPath;
493
[75337]494 /*
495 * Look for a wildcard filter at the end of the path, saving it all for
496 * later in NT filter speak if present.
497 */
498 PSHFLSTRING pFilter = NULL;
[76108]499 PRTUTF16 pwszFilter = RTPathFilenameUtf16(pStrFolderPath->String.utf16);
500 if ( pwszFilter
501 && ( RTUtf16Chr(pwszFilter, '*') != NULL
502 || RTUtf16Chr(pwszFilter, '?') != NULL))
[75337]503 {
[76108]504 if (RTUtf16CmpAscii(pwszFilter, "*.*") == 0)
[75337]505 {
506 /* All files, no filtering needed. Just drop the filter expression from the directory path. */
[76108]507 *pwszFilter = '\0';
508 pStrFolderPath->u16Length = (uint16_t)((uint8_t *)pwszFilter - &pStrFolderPath->String.utf8[0]);
[75337]509 }
510 else
511 {
512 /* Duplicate the whole path. */
[76108]513 pFilter = vboxSfOs2StrDup(pStrFolderPath);
[75337]514 if (pFilter)
515 {
516 /* Drop filter from directory path. */
[76108]517 *pwszFilter = '\0';
518 pStrFolderPath->u16Length = (uint16_t)((uint8_t *)pwszFilter - &pStrFolderPath->String.utf8[0]);
[75337]519
520 /* Convert filter part of the copy to NT speak. */
[76108]521 pwszFilter = (PRTUTF16)&pFilter->String.utf8[(uint8_t *)pwszFilter - &pStrFolderPath->String.utf8[0]];
[75337]522 for (;;)
523 {
[76108]524 RTUTF16 wc = *pwszFilter;
525 if (wc == '?')
526 *pwszFilter = '>'; /* The DOS question mark: Matches one char, but dots and end-of-name eats them. */
527 else if (wc == '.')
[75337]528 {
[76108]529 RTUTF16 wc2 = pwszFilter[1];
530 if (wc2 == '*' || wc2 == '?')
531 *pwszFilter = '"'; /* The DOS dot: Matches a dot or end-of-name. */
[75337]532 }
[76108]533 else if (wc == '*')
[75337]534 {
[76108]535 if (pwszFilter[1] == '.')
536 *pwszFilter = '<'; /* The DOS star: Matches zero or more chars except the DOS dot.*/
[75337]537 }
[76108]538 else if (wc == '\0')
[75337]539 break;
[76108]540 pwszFilter++;
[75337]541 }
542 }
543 else
544 rc = ERROR_NOT_ENOUGH_MEMORY;
545 }
546 }
547 /*
548 * When no wildcard is specified, we're supposed to return a single entry
549 * with the name in the final component. Exception is the root, where we
550 * always list the whole thing.
551 *
552 * Not sure if we'll ever see a trailing slash here (pszFilter == NULL),
553 * but if we do we should accept it only for the root.
554 */
[76108]555 else if (pwszFilter)
[75337]556 {
[76143]557 /* Copy the whole path for filtering. */
558 pFilter = vboxSfOs2StrDup(pStrFolderPath);
559 if (pFilter)
[76108]560 {
[76143]561 /* Strip the filename off the one we're opening. */
562 pStrFolderPath->u16Length = (uint16_t)((uintptr_t)pwszFilter - (uintptr_t)pStrFolderPath->String.utf16);
563 pStrFolderPath->u16Size = pStrFolderPath->u16Length + (uint16_t)sizeof(RTUTF16);
[76108]564 pStrFolderPath->String.utf16[pStrFolderPath->u16Length / sizeof(RTUTF16)] = '\0';
565 }
566 else
[75337]567 rc = ERROR_NOT_ENOUGH_MEMORY;
568 }
[76108]569 else if (!pwszFilter && pStrFolderPath->u16Length > 1)
[75337]570 {
[76108]571 LogFlow(("FS32_FINDFIRST: Trailing slash (%ls)\n", pStrFolderPath->String.utf16));
[75337]572 rc = ERROR_PATH_NOT_FOUND;
573 }
574 else
[76108]575 LogFlow(("FS32_FINDFIRST: Root dir (%ls)\n", pStrFolderPath->String.utf16));
[75337]576
577 /*
[76665]578 * Allocate data/request buffer and another buffer for receiving entries in.
[75337]579 */
580 if (rc == NO_ERROR)
581 {
[76665]582 PVBOXSFFSBUF pDataBuf = (PVBOXSFFSBUF)VbglR0PhysHeapAlloc(sizeof(*pDataBuf));
[75337]583 if (pDataBuf)
584 {
[76665]585#define MIN_BUF_SIZE ( RT_ALIGN_32(sizeof(SHFLDIRINFO) + CCHMAXPATHCOMP * sizeof(RTUTF16) + 64 /*fudge*/ + ALLOC_HDR_SIZE, 64) \
586 - ALLOC_HDR_SIZE)
587 RT_ZERO(*pDataBuf);
588 pDataBuf->cbBuf = cMaxMatches == 1 ? MIN_BUF_SIZE : _16K - ALLOC_HDR_SIZE;
589 pDataBuf->pBuf = (PSHFLDIRINFO)VbglR0PhysHeapAlloc(pDataBuf->cbBuf);
590 if (pDataBuf->pBuf)
591 { /* likely */ }
[75337]592 else
[76665]593 {
594 pDataBuf->pBuf = (PSHFLDIRINFO)VbglR0PhysHeapAlloc(MIN_BUF_SIZE);
595 if (pDataBuf->pBuf)
596 pDataBuf->cbBuf = MIN_BUF_SIZE;
597 else
598 rc = ERROR_NOT_ENOUGH_MEMORY;
599 }
[75337]600 }
[76665]601 if (rc == NO_ERROR)
602 {
603 /*
604 * Now, try open the directory for reading.
605 */
606 pReq->CreateParms.CreateFlags = SHFL_CF_DIRECTORY | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACT_OPEN_IF_EXISTS
607 | SHFL_CF_ACCESS_READ | SHFL_CF_ACCESS_ATTR_READ | SHFL_CF_ACCESS_DENYNONE;
[76143]608
[76716]609 int vrc = VbglR0SfHostReqCreate(pFolder->idHostRoot, pReq);
610 LogFlow(("FS32_FINDFIRST: VbglR0SfHostReqCreate(%ls) -> %Rrc Result=%d fMode=%#x hHandle=%#RX64\n",
[76665]611 pStrFolderPath->String.utf16, vrc, pReq->CreateParms.Result, pReq->CreateParms.Info.Attr.fMode,
612 pReq->CreateParms.Handle));
613 if (RT_SUCCESS(vrc))
[75337]614 {
[76665]615 switch (pReq->CreateParms.Result)
616 {
617 case SHFL_FILE_EXISTS:
618 if (pReq->CreateParms.Handle != SHFL_HANDLE_NIL)
[75337]619 {
[76665]620 /*
621 * Initialize the structures.
622 */
[93073]623 pFsFsd->hHostDir = pReq->CreateParms.Handle;
624 pFsFsd->u32Magic = VBOXSFFS_MAGIC;
625 pFsFsd->pFolder = pFolder;
626 pFsFsd->pBuf = pDataBuf;
627 pFsFsd->offLastFile = 0;
628 pDataBuf->u32Magic = VBOXSFFSBUF_MAGIC;
629 pDataBuf->cbValid = 0;
630 pDataBuf->cEntriesLeft = 0;
631 pDataBuf->pEntry = NULL;
632 pDataBuf->pFilter = pFilter;
633 pDataBuf->fMustHaveAttribs = (uint8_t)( (fAttribs >> 8)
634 & (FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_DIRECTORY | FILE_ARCHIVED));
635 pDataBuf->fExcludedAttribs = (uint8_t)(~fAttribs & (FILE_HIDDEN | FILE_SYSTEM | FILE_DIRECTORY));
636 pDataBuf->fLongFilenames = RT_BOOL(fAttribs & FF_ATTR_LONG_FILENAME);
[79710]637 LogFlow(("FS32_FINDFIRST: fMustHaveAttribs=%#x fExcludedAttribs=%#x fLongFilenames=%d (fAttribs=%#x)\n",
638 pDataBuf->fMustHaveAttribs, pDataBuf->fExcludedAttribs, pDataBuf->fLongFilenames, fAttribs));
[76665]639 pDataBuf->cMinLocalTimeDelta = vboxSfOs2GetLocalTimeDelta();
[75337]640
[76665]641 rc = vboxSfOs2ReadDirEntries(pFolder, pFsFsd, pDataBuf, uLevel, fFlags,
642 pbData, cbData, cMaxMatches ? cMaxMatches : UINT16_MAX, pcMatches);
[93073]643 if (rc != ERROR_BUFFER_OVERFLOW)
644 { /* likely */ }
645 else if ( uLevel == FI_LVL_EAS_FROM_LIST
646 || uLevel == FI_LVL_EAS_FROM_LIST_64)
[76665]647 {
[93073]648 /* If we've got a buffer overflow asking for EAs from a LIST, we are allowed (indeed
649 expected) to fall back to level 2 (EA size) and return ERROR_EAS_DIDNT_FIT.
650 See http://www.edm2.com/index.php/DosFindFirst for somewhat dated details. */
651 rc = vboxSfOs2ReadDirEntries(pFolder, pFsFsd, pDataBuf,
652 uLevel == FI_LVL_EAS_FROM_LIST_64
653 ? FI_LVL_STANDARD_EASIZE_64 : FI_LVL_STANDARD_EASIZE,
654 fFlags, pbData, cbData, 1 /* no more than one! */, pcMatches);
655 if (rc == NO_ERROR)
656 rc = ERROR_EAS_DIDNT_FIT;
657 }
658 if (rc == NO_ERROR || rc == ERROR_EAS_DIDNT_FIT)
659 {
[76665]660 uint32_t cRefs = ASMAtomicIncU32(&pFolder->cOpenSearches);
661 Assert(cRefs < _4K); RT_NOREF(cRefs);
662
663 /* We keep these on success: */
664 if (pFilter == pStrFolderPath)
665 pStrFolderPath = NULL;
666 pFilter = NULL;
667 pDataBuf = NULL;
668 pFolder = NULL;
669 }
670 else
671 {
672 AssertCompile(sizeof(VBOXSFCLOSEREQ) < sizeof(*pReq));
[76716]673 vrc = VbglR0SfHostReqClose(pFolder->idHostRoot, (VBOXSFCLOSEREQ *)pReq, pFsFsd->hHostDir);
[76665]674 AssertRC(vrc);
675 pFsFsd->u32Magic = ~VBOXSFFS_MAGIC;
676 pDataBuf->u32Magic = ~VBOXSFFSBUF_MAGIC;
677 pFsFsd->pFolder = NULL;
678 pFsFsd->hHostDir = NULL;
679 }
[75337]680 }
681 else
682 {
[76716]683 LogFlow(("FS32_FINDFIRST: VbglR0SfHostReqCreate returns NIL handle for '%ls'\n",
[76665]684 pStrFolderPath->String.utf16));
685 rc = ERROR_PATH_NOT_FOUND;
[75337]686 }
[76665]687 break;
688
689 case SHFL_PATH_NOT_FOUND:
[75337]690 rc = ERROR_PATH_NOT_FOUND;
[76665]691 break;
[75337]692
[76665]693 default:
694 case SHFL_FILE_NOT_FOUND:
695 rc = ERROR_FILE_NOT_FOUND;
696 break;
697 }
[75337]698 }
[76665]699 else
700 rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_GEN_FAILURE);
[75337]701 }
[76665]702
703 if (pDataBuf)
704 {
705 VbglR0PhysHeapFree(pDataBuf->pBuf);
706 pDataBuf->pBuf = NULL;
707 VbglR0PhysHeapFree(pDataBuf);
708 }
[75337]709 }
[76143]710 vboxSfOs2StrFree(pFilter);
711 VbglR0PhysHeapFree(pReq);
712 vboxSfOs2ReleaseFolder(pFolder);
[75337]713 }
[75338]714
715 RT_NOREF_PV(pFsFsi);
[75337]716 LogFlow(("FS32_FINDFIRST: returns %u\n", rc));
717 return rc;
718}
719
720
721DECLASM(APIRET)
722FS32_FINDFROMNAME(PFSFSI pFsFsi, PVBOXSFFS pFsFsd, PBYTE pbData, ULONG cbData, PUSHORT pcMatches,
723 ULONG uLevel, ULONG uPosition, PCSZ pszName, ULONG fFlags)
724{
725 LogFlow(("FS32_FINDFROMNAME: pFsFsi=%p pFsFsd=%p pbData=%p cbData=%#x pcMatches=%p:{%#x} uLevel=%#x uPosition=%#x pszName=%p:{%s} fFlags=%#x\n",
726 pFsFsi, pFsFsd, pbData, cbData, pcMatches, *pcMatches, uLevel, uPosition, pszName, pszName, fFlags));
727
728 /*
729 * Input validation.
730 */
731 USHORT const cMaxMatches = *pcMatches;
732 *pcMatches = 0;
733 AssertReturn(pFsFsd->u32Magic == VBOXSFFS_MAGIC, ERROR_SYS_INTERNAL);
734 PVBOXSFFOLDER pFolder = pFsFsd->pFolder;
735 AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
736 Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
737 Assert(pFolder->cOpenSearches > 0);
738 PVBOXSFFSBUF pDataBuf = pFsFsd->pBuf;
739 AssertReturn(pDataBuf, ERROR_SYS_INTERNAL);
740 Assert(pDataBuf->u32Magic == VBOXSFFSBUF_MAGIC);
741
742 switch (uLevel)
743 {
744 case FI_LVL_STANDARD:
745 case FI_LVL_STANDARD_64:
746 case FI_LVL_STANDARD_EASIZE:
747 case FI_LVL_STANDARD_EASIZE_64:
748 case FI_LVL_EAS_FROM_LIST:
749 case FI_LVL_EAS_FROM_LIST_64:
[79710]750 break;
[75337]751
752 default:
[79710]753 LogRel(("FS32_FINDFROMNAME: Unsupported info level %u!\n", uLevel));
[75337]754 return ERROR_INVALID_LEVEL;
755 }
756
757 /*
758 * Check if we're just continuing. This is usually the case.
759 */
760 APIRET rc;
761 if (uPosition == pFsFsd->offLastFile)
762 rc = vboxSfOs2ReadDirEntries(pFolder, pFsFsd, pDataBuf, uLevel, fFlags, pbData, cbData,
763 cMaxMatches ? cMaxMatches : UINT16_MAX, pcMatches);
764 else
765 {
766 Log(("TODO: uPosition differs: %#x, expected %#x (%s)\n", uPosition, pFsFsd->offLastFile, pszName));
767 rc = vboxSfOs2ReadDirEntries(pFolder, pFsFsd, pDataBuf, uLevel, fFlags, pbData, cbData,
768 cMaxMatches ? cMaxMatches : UINT16_MAX, pcMatches);
769 }
[93073]770 if (rc != ERROR_BUFFER_OVERFLOW)
771 { /* likely */ }
772 else if ( uLevel == FI_LVL_EAS_FROM_LIST
773 || uLevel == FI_LVL_EAS_FROM_LIST_64)
774 {
775 /* If we've got a buffer overflow asking for EAs from a LIST, we are allowed (indeed
776 expected) to fall back to level 2 (EA size) and return ERROR_EAS_DIDNT_FIT. */
777 rc = vboxSfOs2ReadDirEntries(pFolder, pFsFsd, pDataBuf,
778 uLevel == FI_LVL_EAS_FROM_LIST_64 ? FI_LVL_STANDARD_EASIZE_64 : FI_LVL_STANDARD_EASIZE,
779 fFlags, pbData, cbData, 1 /* no more than one! */, pcMatches);
780 if (rc == NO_ERROR)
781 rc = ERROR_EAS_DIDNT_FIT;
782 }
[75337]783
784 RT_NOREF(pFsFsi, pszName);
785 LogFlow(("FS32_FINDFROMNAME: returns %u (*pcMatches=%#x)\n", rc, *pcMatches));
786 return rc;
787}
788
789
790DECLASM(APIRET)
791FS32_FINDNEXT(PFSFSI pFsFsi, PVBOXSFFS pFsFsd, PBYTE pbData, ULONG cbData, PUSHORT pcMatches, ULONG uLevel, ULONG fFlags)
792{
793 LogFlow(("FS32_FINDNEXT: pFsFsi=%p pFsFsd=%p pbData=%p cbData=%#x pcMatches=%p:{%#x} uLevel=%#x fFlags=%#x\n",
794 pFsFsi, pFsFsd, pbData, cbData, pcMatches, *pcMatches, uLevel, fFlags));
795
796 /*
797 * Input validation.
798 */
799 USHORT const cMaxMatches = *pcMatches;
800 *pcMatches = 0;
801 AssertReturn(pFsFsd->u32Magic == VBOXSFFS_MAGIC, ERROR_SYS_INTERNAL);
802 PVBOXSFFOLDER pFolder = pFsFsd->pFolder;
803 AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
804 Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
805 Assert(pFolder->cOpenSearches > 0);
806 PVBOXSFFSBUF pDataBuf = pFsFsd->pBuf;
807 AssertReturn(pDataBuf, ERROR_SYS_INTERNAL);
808 Assert(pDataBuf->u32Magic == VBOXSFFSBUF_MAGIC);
809
810 switch (uLevel)
811 {
812 case FI_LVL_STANDARD:
813 case FI_LVL_STANDARD_64:
814 case FI_LVL_STANDARD_EASIZE:
815 case FI_LVL_STANDARD_EASIZE_64:
816 case FI_LVL_EAS_FROM_LIST:
817 case FI_LVL_EAS_FROM_LIST_64:
[79710]818 break;
[75337]819
820 default:
[79710]821 LogRel(("FS32_FINDNEXT: Unsupported info level %u!\n", uLevel));
[75337]822 return ERROR_INVALID_LEVEL;
823 }
824
825 /*
826 * Read more.
827 */
828 APIRET rc = vboxSfOs2ReadDirEntries(pFolder, pFsFsd, pDataBuf, uLevel, fFlags, pbData, cbData,
829 cMaxMatches ? cMaxMatches : UINT16_MAX, pcMatches);
[93073]830 if (rc != ERROR_BUFFER_OVERFLOW)
831 { /* likely */ }
832 else if ( uLevel == FI_LVL_EAS_FROM_LIST
833 || uLevel == FI_LVL_EAS_FROM_LIST_64)
834 {
835 /* If we've got a buffer overflow asking for EAs from a LIST, we are allowed (indeed
836 expected) to fall back to level 2 (EA size) and return ERROR_EAS_DIDNT_FIT.
837 See http://www.edm2.com/index.php/DosFindNext for somewhat dated details. */
838 rc = vboxSfOs2ReadDirEntries(pFolder, pFsFsd, pDataBuf,
839 uLevel == FI_LVL_EAS_FROM_LIST_64 ? FI_LVL_STANDARD_EASIZE_64 : FI_LVL_STANDARD_EASIZE,
840 fFlags, pbData, cbData, 1 /* no more than one! */, pcMatches);
841 if (rc == NO_ERROR)
842 rc = ERROR_EAS_DIDNT_FIT;
843 }
[75337]844
845 NOREF(pFsFsi);
846 LogFlow(("FS32_FINDNEXT: returns %u (*pcMatches=%#x)\n", rc, *pcMatches));
847 return rc;
848}
849
850
851DECLASM(APIRET)
852FS32_FINDCLOSE(PFSFSI pFsFsi, PVBOXSFFS pFsFsd)
853{
854 /*
855 * Input validation.
856 */
857 AssertReturn(pFsFsd->u32Magic == VBOXSFFS_MAGIC, ERROR_SYS_INTERNAL);
858 PVBOXSFFOLDER pFolder = pFsFsd->pFolder;
859 AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
860 Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
861 Assert(pFolder->cOpenSearches > 0);
862 PVBOXSFFSBUF pDataBuf = pFsFsd->pBuf;
863 AssertReturn(pDataBuf, ERROR_SYS_INTERNAL);
864 Assert(pDataBuf->u32Magic == VBOXSFFSBUF_MAGIC);
865
866 /*
867 * Close it.
868 */
869 if (pFsFsd->hHostDir != SHFL_HANDLE_NIL)
870 {
[76716]871 int vrc = VbglR0SfHostReqCloseSimple(pFolder->idHostRoot, pFsFsd->hHostDir);
[75337]872 AssertRC(vrc);
873 }
874
875 pFsFsd->u32Magic = ~VBOXSFFS_MAGIC;
876 pFsFsd->hHostDir = SHFL_HANDLE_NIL;
877 pFsFsd->pFolder = NULL;
878 pFsFsd->pBuf = NULL;
879 vboxSfOs2StrFree(pDataBuf->pFilter);
880 pDataBuf->pFilter = NULL;
881 pDataBuf->u32Magic = ~VBOXSFFSBUF_MAGIC;
882 pDataBuf->cbBuf = 0;
883
[76665]884 VbglR0PhysHeapFree(pDataBuf->pBuf);
885 pDataBuf->pBuf = NULL;
886 VbglR0PhysHeapFree(pDataBuf);
887
[75337]888 uint32_t cRefs = ASMAtomicDecU32(&pFolder->cOpenSearches);
889 Assert(cRefs < _4K); RT_NOREF(cRefs);
890 vboxSfOs2ReleaseFolder(pFolder);
891
892 RT_NOREF(pFsFsi);
893 LogFlow(("FS32_FINDCLOSE: returns NO_ERROR\n"));
894 return NO_ERROR;
895}
896
897
898
899
900
901DECLASM(APIRET)
902FS32_FINDNOTIFYFIRST(PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszPath, LONG offCurDirEnd, ULONG fAttribs,
903 PUSHORT phHandle, PBYTE pbData, ULONG cbData, PUSHORT pcMatches,
904 ULONG uLevel, ULONG fFlags)
905{
906 RT_NOREF(pCdFsi, pCdFsd, pszPath, offCurDirEnd, fAttribs, phHandle, pbData, cbData, pcMatches, uLevel, fFlags);
[3655]907 return ERROR_NOT_SUPPORTED;
908}
909
910
[75337]911DECLASM(APIRET)
912FS32_FINDNOTIFYNEXT(ULONG hHandle, PBYTE pbData, ULONG cbData, PUSHORT pcMatchs, ULONG uLevel, ULONG cMsTimeout)
[3655]913{
[75337]914 RT_NOREF(hHandle, pbData, cbData, pcMatchs, uLevel, cMsTimeout);
[3655]915 return ERROR_NOT_SUPPORTED;
916}
917
918
[75337]919DECLASM(APIRET)
920FS32_FINDNOTIFYCLOSE(ULONG hHandle)
[3655]921{
[68612]922 NOREF(hHandle);
[3655]923 return ERROR_NOT_SUPPORTED;
924}
925
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use