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
Line 
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/*
7 * Copyright (c) 2007-2018 knut st. osmundsen <bird-src-spam@anduin.net>
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/asm.h>
40#include <iprt/assert.h>
41#include <iprt/mem.h>
42#include <iprt/path.h>
43#include <iprt/err.h>
44
45
46
47/**
48 * Checks if the given name is 8-dot-3 compatible.
49 *
50 * @returns true if compatible, false if not.
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.
55 */
56static bool vboxSfOs2IsUtf16Name8dot3(PRTUTF16 pwszName, size_t cwcName, char *pszTmp, size_t cbTmp)
57{
58 /* Reject names that must be too long. */
59 if (cwcName > 8 + 1 + 3)
60 return false;
61
62 /* First char cannot be a dot, nor can it be an empty string. */
63 if (*pwszName == '.' || !*pwszName)
64 return false;
65
66 /*
67 * To basic checks on code point level before doing full conversion.
68 */
69 for (unsigned off = 0; ; off++)
70 {
71 RTUTF16 wc = pwszName[off];
72 if (wc == '.')
73 {
74 unsigned const offMax = off + 3;
75 for (++off;; off++)
76 {
77 wc = pwszName[off];
78 if (!wc)
79 break;
80 if (wc == '.')
81 return false;
82 if (off > offMax)
83 return false;
84 }
85 break;
86 }
87 if (!wc)
88 break;
89 if (off >= 8)
90 return false;
91 }
92
93 /*
94 * Convert to the native code page.
95 */
96 APIRET rc = SafeKernStrFromUcs(NULL, pszTmp, pwszName, cbTmp, cwcName);
97 if (rc != NO_ERROR)
98 {
99 LogRel(("vboxSfOs2IsUtf8Name8dot3: SafeKernStrFromUcs failed: %d\n", rc));
100 return false;
101 }
102
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++)
108 {
109 char ch = *pszTmp++;
110 if (ch == '.')
111 break;
112 if (ch == '\0')
113 return true;
114 if (cch >= 8)
115 return false;
116 }
117 for (unsigned cch = 0; ; cch++)
118 {
119 char ch = *pszTmp++;
120 if (ch == '\0')
121 return true;
122 if (ch == '.')
123 return false;
124 if (cch >= 3)
125 return false;
126 }
127}
128
129
130/**
131 * @returns Updated pbDst on success, NULL on failure.
132 */
133static uint8_t *vboxSfOs2CopyUtf16Name(uint8_t *pbDst, PRTUTF16 pwszSrc, size_t cwcSrc)
134{
135 char *pszDst = (char *)pbDst + 1;
136 APIRET rc = SafeKernStrFromUcs(NULL, pszDst, pwszSrc, CCHMAXPATHCOMP, cwcSrc);
137 if (rc == NO_ERROR)
138 {
139 size_t cchDst = strlen(pszDst);
140 *pbDst++ = (uint8_t)cchDst;
141 pbDst += cchDst;
142 *pbDst++ = '\0';
143 return pbDst;
144 }
145 LogRel(("vboxSfOs2CopyUtf8Name: SafeKernStrFromUcs failed: %d\n", rc));
146 return NULL;
147}
148
149
150/**
151 * @returns Updated pbDst on success, NULL on failure.
152 */
153static uint8_t *vboxSfOs2CopyUtf16NameAndUpperCase(uint8_t *pbDst, PRTUTF16 pwszSrc, size_t cwcSrc)
154{
155 char *pszDst = (char *)(pbDst + 1);
156 APIRET rc = SafeKernStrFromUcs(NULL, pszDst, RTUtf16ToUpper(pwszSrc), CCHMAXPATHCOMP, cwcSrc);
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 }
165 LogRel(("vboxSfOs2CopyUtf16NameAndUpperCase: SafeKernStrFromUcs failed: %#x\n", rc));
166 return NULL;
167}
168
169
170
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;
189
190 /*
191 * If we're doing EAs, the buffer starts with an EAOP structure.
192 */
193 EAOP EaOp;
194 PEAOP pEaOpUser = NULL; /* Shut up gcc */
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;
209
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 {
235 pDataBuf->pEntry = pEntry = pDataBuf->pBuf;
236 int vrc = VbglR0SfHostReqListDir(pFolder->idHostRoot, &pDataBuf->Req, pFsFsd->hHostDir, pDataBuf->pFilter,
237 /*cMaxMatches == 1 ? SHFL_LIST_RETURN_ONE :*/ 0, pDataBuf->pBuf, pDataBuf->cbBuf);
238 if (RT_SUCCESS(vrc))
239 {
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));
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);
245 Log4(("vboxSfOs2ReadDirEntries: VbglR0SfHostReqListDir returned %#x matches in %#x bytes\n", pDataBuf->cEntriesLeft, pDataBuf->cbValid));
246 }
247 else
248 {
249 if (vrc == VERR_NO_MORE_FILES)
250 Log4(("vboxSfOs2ReadDirEntries: VbglR0SfHostReqListDir returned VERR_NO_MORE_FILES (%d,%d)\n",
251 pDataBuf->Req.Parms.c32Entries.u.value32, pDataBuf->Req.Parms.cb32Buffer.u.value32));
252 else
253 Log(("vboxSfOs2ReadDirEntries: VbglR0SfHostReqListDir failed %Rrc (%d,%d)\n", vrc,
254 pDataBuf->Req.Parms.c32Entries.u.value32, pDataBuf->Req.Parms.cb32Buffer.u.value32));
255 pDataBuf->pEntry = NULL;
256 pDataBuf->cEntriesLeft = 0;
257 pDataBuf->cbValid = 0;
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
276 || vboxSfOs2IsUtf16Name8dot3(pEntry->name.String.utf16, pEntry->name.u16Length / sizeof(RTUTF16),
277 (char *)pDataBuf->abStaging, sizeof(pDataBuf->abStaging))))
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
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. */
352 uint32_t cbWritten = 0;
353 EaOp.fpFEAList = (PFEALIST)pbData;
354 rc = vboxSfOs2MakeEmptyEaListEx(&EaOp, uLevel, cbData, &cbWritten, &pEaOpUser->oError);
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)
370 pbDst = vboxSfOs2CopyUtf16Name(pbDst, pEntry->name.String.utf16, pEntry->name.u16Length / sizeof(RTUTF16));
371 else if (pEntry->cucShortName == 0)
372 pbDst = vboxSfOs2CopyUtf16NameAndUpperCase(pbDst, pEntry->name.String.utf16, pEntry->name.u16Length / sizeof(RTUTF16));
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];
423 uintptr_t offEntry = (uintptr_t)pEntry - (uintptr_t)pDataBuf->pBuf;
424 AssertMsgReturn(offEntry + RT_UOFFSETOF(SHFLDIRINFO, name.String) <= pDataBuf->cbValid,
425 ("offEntry=%#x cbValid=%#x\n", offEntry, pDataBuf->cbValid), ERROR_SYS_INTERNAL);
426 //Log(("next entry: %p / %#x: u16Size=%#x => size: %#x\n", pEntry, offEntry, RT_UOFFSETOF(SHFLDIRINFO, name.String) + pEntry->name.u16Size));
427 AssertMsgReturn(offEntry + RT_UOFFSETOF(SHFLDIRINFO, name.String) + pEntry->name.u16Size <= pDataBuf->cbValid,
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));
433 }
434 else
435 pDataBuf->pEntry = pEntry = NULL;
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)
450{
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",
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);
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));
490 if (rc == NO_ERROR)
491 {
492 PSHFLSTRING pStrFolderPath = &pReq->StrPath;
493
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;
499 PRTUTF16 pwszFilter = RTPathFilenameUtf16(pStrFolderPath->String.utf16);
500 if ( pwszFilter
501 && ( RTUtf16Chr(pwszFilter, '*') != NULL
502 || RTUtf16Chr(pwszFilter, '?') != NULL))
503 {
504 if (RTUtf16CmpAscii(pwszFilter, "*.*") == 0)
505 {
506 /* All files, no filtering needed. Just drop the filter expression from the directory path. */
507 *pwszFilter = '\0';
508 pStrFolderPath->u16Length = (uint16_t)((uint8_t *)pwszFilter - &pStrFolderPath->String.utf8[0]);
509 }
510 else
511 {
512 /* Duplicate the whole path. */
513 pFilter = vboxSfOs2StrDup(pStrFolderPath);
514 if (pFilter)
515 {
516 /* Drop filter from directory path. */
517 *pwszFilter = '\0';
518 pStrFolderPath->u16Length = (uint16_t)((uint8_t *)pwszFilter - &pStrFolderPath->String.utf8[0]);
519
520 /* Convert filter part of the copy to NT speak. */
521 pwszFilter = (PRTUTF16)&pFilter->String.utf8[(uint8_t *)pwszFilter - &pStrFolderPath->String.utf8[0]];
522 for (;;)
523 {
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 == '.')
528 {
529 RTUTF16 wc2 = pwszFilter[1];
530 if (wc2 == '*' || wc2 == '?')
531 *pwszFilter = '"'; /* The DOS dot: Matches a dot or end-of-name. */
532 }
533 else if (wc == '*')
534 {
535 if (pwszFilter[1] == '.')
536 *pwszFilter = '<'; /* The DOS star: Matches zero or more chars except the DOS dot.*/
537 }
538 else if (wc == '\0')
539 break;
540 pwszFilter++;
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 */
555 else if (pwszFilter)
556 {
557 /* Copy the whole path for filtering. */
558 pFilter = vboxSfOs2StrDup(pStrFolderPath);
559 if (pFilter)
560 {
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);
564 pStrFolderPath->String.utf16[pStrFolderPath->u16Length / sizeof(RTUTF16)] = '\0';
565 }
566 else
567 rc = ERROR_NOT_ENOUGH_MEMORY;
568 }
569 else if (!pwszFilter && pStrFolderPath->u16Length > 1)
570 {
571 LogFlow(("FS32_FINDFIRST: Trailing slash (%ls)\n", pStrFolderPath->String.utf16));
572 rc = ERROR_PATH_NOT_FOUND;
573 }
574 else
575 LogFlow(("FS32_FINDFIRST: Root dir (%ls)\n", pStrFolderPath->String.utf16));
576
577 /*
578 * Allocate data/request buffer and another buffer for receiving entries in.
579 */
580 if (rc == NO_ERROR)
581 {
582 PVBOXSFFSBUF pDataBuf = (PVBOXSFFSBUF)VbglR0PhysHeapAlloc(sizeof(*pDataBuf));
583 if (pDataBuf)
584 {
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 */ }
592 else
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 }
600 }
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;
608
609 int vrc = VbglR0SfHostReqCreate(pFolder->idHostRoot, pReq);
610 LogFlow(("FS32_FINDFIRST: VbglR0SfHostReqCreate(%ls) -> %Rrc Result=%d fMode=%#x hHandle=%#RX64\n",
611 pStrFolderPath->String.utf16, vrc, pReq->CreateParms.Result, pReq->CreateParms.Info.Attr.fMode,
612 pReq->CreateParms.Handle));
613 if (RT_SUCCESS(vrc))
614 {
615 switch (pReq->CreateParms.Result)
616 {
617 case SHFL_FILE_EXISTS:
618 if (pReq->CreateParms.Handle != SHFL_HANDLE_NIL)
619 {
620 /*
621 * Initialize the structures.
622 */
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);
637 LogFlow(("FS32_FINDFIRST: fMustHaveAttribs=%#x fExcludedAttribs=%#x fLongFilenames=%d (fAttribs=%#x)\n",
638 pDataBuf->fMustHaveAttribs, pDataBuf->fExcludedAttribs, pDataBuf->fLongFilenames, fAttribs));
639 pDataBuf->cMinLocalTimeDelta = vboxSfOs2GetLocalTimeDelta();
640
641 rc = vboxSfOs2ReadDirEntries(pFolder, pFsFsd, pDataBuf, uLevel, fFlags,
642 pbData, cbData, cMaxMatches ? cMaxMatches : UINT16_MAX, pcMatches);
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)
647 {
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 {
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));
673 vrc = VbglR0SfHostReqClose(pFolder->idHostRoot, (VBOXSFCLOSEREQ *)pReq, pFsFsd->hHostDir);
674 AssertRC(vrc);
675 pFsFsd->u32Magic = ~VBOXSFFS_MAGIC;
676 pDataBuf->u32Magic = ~VBOXSFFSBUF_MAGIC;
677 pFsFsd->pFolder = NULL;
678 pFsFsd->hHostDir = NULL;
679 }
680 }
681 else
682 {
683 LogFlow(("FS32_FINDFIRST: VbglR0SfHostReqCreate returns NIL handle for '%ls'\n",
684 pStrFolderPath->String.utf16));
685 rc = ERROR_PATH_NOT_FOUND;
686 }
687 break;
688
689 case SHFL_PATH_NOT_FOUND:
690 rc = ERROR_PATH_NOT_FOUND;
691 break;
692
693 default:
694 case SHFL_FILE_NOT_FOUND:
695 rc = ERROR_FILE_NOT_FOUND;
696 break;
697 }
698 }
699 else
700 rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_GEN_FAILURE);
701 }
702
703 if (pDataBuf)
704 {
705 VbglR0PhysHeapFree(pDataBuf->pBuf);
706 pDataBuf->pBuf = NULL;
707 VbglR0PhysHeapFree(pDataBuf);
708 }
709 }
710 vboxSfOs2StrFree(pFilter);
711 VbglR0PhysHeapFree(pReq);
712 vboxSfOs2ReleaseFolder(pFolder);
713 }
714
715 RT_NOREF_PV(pFsFsi);
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:
750 break;
751
752 default:
753 LogRel(("FS32_FINDFROMNAME: Unsupported info level %u!\n", uLevel));
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 }
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 }
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:
818 break;
819
820 default:
821 LogRel(("FS32_FINDNEXT: Unsupported info level %u!\n", uLevel));
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);
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 }
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 {
871 int vrc = VbglR0SfHostReqCloseSimple(pFolder->idHostRoot, pFsFsd->hHostDir);
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
884 VbglR0PhysHeapFree(pDataBuf->pBuf);
885 pDataBuf->pBuf = NULL;
886 VbglR0PhysHeapFree(pDataBuf);
887
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);
907 return ERROR_NOT_SUPPORTED;
908}
909
910
911DECLASM(APIRET)
912FS32_FINDNOTIFYNEXT(ULONG hHandle, PBYTE pbData, ULONG cbData, PUSHORT pcMatchs, ULONG uLevel, ULONG cMsTimeout)
913{
914 RT_NOREF(hHandle, pbData, cbData, pcMatchs, uLevel, cMsTimeout);
915 return ERROR_NOT_SUPPORTED;
916}
917
918
919DECLASM(APIRET)
920FS32_FINDNOTIFYCLOSE(ULONG hHandle)
921{
922 NOREF(hHandle);
923 return ERROR_NOT_SUPPORTED;
924}
925
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use