VirtualBox

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

Last change on this file since 79112 was 77640, checked in by vboxsync, 5 years ago

os2/vboxsf: KernStrToUcs and (probably also) KernStrFromUcs trashes ES in some cases (probably when they load a translation table, as it doesn't happen when JFS is actively used), so they need ES preserving wrappers to be safe to use. ticketref:18376 ticketref:18379

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 34.7 KB
Line 
1/** $Id: VBoxSFFind.cpp 77640 2019-03-10 21:13:03Z 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 * Conver 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 uint32_t cbWritten = 0;
347 EaOp.fpFEAList = (PFEALIST)pbData;
348 rc = vboxSfOs2MakeEmptyEaListEx(&EaOp, uLevel, &cbWritten, &pEaOpUser->oError);
349 if (rc == NO_ERROR)
350 {
351 cbData -= cbWritten;
352 pbData += cbWritten;
353 }
354 }
355 }
356 else
357 rc = ERROR_BUFFER_OVERFLOW;
358 if (rc != NO_ERROR)
359 break;
360 }
361
362 /* The length prefixed filename. */
363 if (pDataBuf->fLongFilenames)
364 pbDst = vboxSfOs2CopyUtf16Name(pbDst, pEntry->name.String.utf16, pEntry->name.u16Length / sizeof(RTUTF16));
365 else if (pEntry->cucShortName == 0)
366 pbDst = vboxSfOs2CopyUtf16NameAndUpperCase(pbDst, pEntry->name.String.utf16, pEntry->name.u16Length / sizeof(RTUTF16));
367 else
368 pbDst = vboxSfOs2CopyUtf16NameAndUpperCase(pbDst, pEntry->uszShortName, pEntry->cucShortName);
369 if (pbDst)
370 {
371 /*
372 * Copy out the staged data.
373 */
374 uint32_t cbToCopy = pbDst - pbToCopy;
375 if (cbToCopy <= cbData)
376 {
377 rc = KernCopyOut(pbData, pbToCopy, cbToCopy);
378 if (rc == NO_ERROR)
379 {
380 Log4(("vboxSfOs2ReadDirEntries: match #%u LB %#x: '%s'\n", cMatches, cbToCopy, pEntry->name.String.utf8));
381 Log4(("%.*Rhxd\n", cbToCopy, pbToCopy));
382
383 pbData += cbToCopy;
384 cbData -= cbToCopy;
385 pbDst = pbToCopy;
386
387 cMatches++;
388 pFsFsd->offLastFile++;
389 }
390 else
391 break;
392 }
393 else
394 {
395 rc = ERROR_BUFFER_OVERFLOW;
396 break;
397 }
398 }
399 else
400 {
401 /* Name conversion issue, just skip the entry. */
402 Log3(("vboxSfOs2ReadDirEntries: Skipping '%s' due to name conversion issue.\n", pEntry->name.String.utf8));
403 cbData -= pbUserBufStart - pbData;
404 pbData = pbUserBufStart;
405 }
406 }
407 else
408 Log3(("vboxSfOs2ReadDirEntries: fMode=%#x filter out by %#x/%#x; '%s'\n",
409 pEntry->Info.Attr.fMode, pDataBuf->fMustHaveAttribs, pDataBuf->fExcludedAttribs, pEntry->name.String.utf8));
410
411 /*
412 * Advance to the next directory entry from the host.
413 */
414 if (pDataBuf->cEntriesLeft-- > 1)
415 {
416 pDataBuf->pEntry = pEntry = (PSHFLDIRINFO)&pEntry->name.String.utf8[pEntry->name.u16Size];
417 uintptr_t offEntry = (uintptr_t)pEntry - (uintptr_t)pDataBuf->pBuf;
418 AssertMsgReturn(offEntry + RT_UOFFSETOF(SHFLDIRINFO, name.String) <= pDataBuf->cbValid,
419 ("offEntry=%#x cbValid=%#x\n", offEntry, pDataBuf->cbValid), ERROR_SYS_INTERNAL);
420 //Log(("next entry: %p / %#x: u16Size=%#x => size: %#x\n", pEntry, offEntry, RT_UOFFSETOF(SHFLDIRINFO, name.String) + pEntry->name.u16Size));
421 AssertMsgReturn(offEntry + RT_UOFFSETOF(SHFLDIRINFO, name.String) + pEntry->name.u16Size <= pDataBuf->cbValid,
422 ("offEntry=%#x + offName=%#x + cbName=%#x => %#x; cbValid=%#x\n",
423 offEntry, RT_UOFFSETOF(SHFLDIRINFO, name.String), pEntry->name.u16Size,
424 offEntry + RT_UOFFSETOF(SHFLDIRINFO, name.String) + pEntry->name.u16Size, pDataBuf->cbValid),
425 ERROR_SYS_INTERNAL);
426 //Log(("%.*Rhxd\n", RT_UOFFSETOF(SHFLDIRINFO, name.String) + pEntry->name.u16Size, pEntry));
427 }
428 else
429 pDataBuf->pEntry = pEntry = NULL;
430 }
431
432 *pcMatches = cMatches;
433
434 /* Ignore buffer overflows if we've got matches to return. */
435 if (rc == ERROR_BUFFER_OVERFLOW && cMatches > 0)
436 rc = NO_ERROR;
437 return rc;
438}
439
440
441DECLASM(APIRET)
442FS32_FINDFIRST(PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszPath, LONG offCurDirEnd, ULONG fAttribs,
443 PFSFSI pFsFsi, PVBOXSFFS pFsFsd, PBYTE pbData, ULONG cbData, PUSHORT pcMatches, ULONG uLevel, ULONG fFlags)
444{
445 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",
446 pCdFsi, pCdFsd, pszPath, pszPath, offCurDirEnd, fAttribs, pFsFsi, pFsFsd, pbData, cbData, pcMatches, *pcMatches, uLevel, fFlags));
447 USHORT const cMaxMatches = *pcMatches;
448 *pcMatches = 0;
449
450 /*
451 * Input validation.
452 */
453 switch (uLevel)
454 {
455 case FI_LVL_STANDARD:
456 case FI_LVL_STANDARD_64:
457 case FI_LVL_STANDARD_EASIZE:
458 case FI_LVL_STANDARD_EASIZE_64:
459 break;
460
461 case FI_LVL_EAS_FROM_LIST:
462 case FI_LVL_EAS_FROM_LIST_64:
463 if (cbData < sizeof(EAOP))
464 {
465 Log(("FS32_FINDFIRST: Buffer smaller than EAOP: %#x\n", cbData));
466 return ERROR_BUFFER_OVERFLOW;
467 }
468 break;
469
470 default:
471 LogRel(("FS32_FINDFIRST: Unsupported info level %u!\n", uLevel));
472 return ERROR_INVALID_LEVEL;
473 }
474
475 /*
476 * Resolve path to a folder and folder relative path.
477 */
478 RT_NOREF(pCdFsi);
479 PVBOXSFFOLDER pFolder;
480 VBOXSFCREATEREQ *pReq;
481 APIRET rc = vboxSfOs2ResolvePathEx(pszPath, pCdFsd, offCurDirEnd, RT_UOFFSETOF(VBOXSFCREATEREQ, StrPath),
482 &pFolder, (void **)&pReq);
483 LogFlow(("FS32_FINDFIRST: vboxSfOs2ResolvePathEx: -> %u pReq=%p\n", rc, pReq));
484 if (rc == NO_ERROR)
485 {
486 PSHFLSTRING pStrFolderPath = &pReq->StrPath;
487
488 /*
489 * Look for a wildcard filter at the end of the path, saving it all for
490 * later in NT filter speak if present.
491 */
492 PSHFLSTRING pFilter = NULL;
493 PRTUTF16 pwszFilter = RTPathFilenameUtf16(pStrFolderPath->String.utf16);
494 if ( pwszFilter
495 && ( RTUtf16Chr(pwszFilter, '*') != NULL
496 || RTUtf16Chr(pwszFilter, '?') != NULL))
497 {
498 if (RTUtf16CmpAscii(pwszFilter, "*.*") == 0)
499 {
500 /* All files, no filtering needed. Just drop the filter expression from the directory path. */
501 *pwszFilter = '\0';
502 pStrFolderPath->u16Length = (uint16_t)((uint8_t *)pwszFilter - &pStrFolderPath->String.utf8[0]);
503 }
504 else
505 {
506 /* Duplicate the whole path. */
507 pFilter = vboxSfOs2StrDup(pStrFolderPath);
508 if (pFilter)
509 {
510 /* Drop filter from directory path. */
511 *pwszFilter = '\0';
512 pStrFolderPath->u16Length = (uint16_t)((uint8_t *)pwszFilter - &pStrFolderPath->String.utf8[0]);
513
514 /* Convert filter part of the copy to NT speak. */
515 pwszFilter = (PRTUTF16)&pFilter->String.utf8[(uint8_t *)pwszFilter - &pStrFolderPath->String.utf8[0]];
516 for (;;)
517 {
518 RTUTF16 wc = *pwszFilter;
519 if (wc == '?')
520 *pwszFilter = '>'; /* The DOS question mark: Matches one char, but dots and end-of-name eats them. */
521 else if (wc == '.')
522 {
523 RTUTF16 wc2 = pwszFilter[1];
524 if (wc2 == '*' || wc2 == '?')
525 *pwszFilter = '"'; /* The DOS dot: Matches a dot or end-of-name. */
526 }
527 else if (wc == '*')
528 {
529 if (pwszFilter[1] == '.')
530 *pwszFilter = '<'; /* The DOS star: Matches zero or more chars except the DOS dot.*/
531 }
532 else if (wc == '\0')
533 break;
534 pwszFilter++;
535 }
536 }
537 else
538 rc = ERROR_NOT_ENOUGH_MEMORY;
539 }
540 }
541 /*
542 * When no wildcard is specified, we're supposed to return a single entry
543 * with the name in the final component. Exception is the root, where we
544 * always list the whole thing.
545 *
546 * Not sure if we'll ever see a trailing slash here (pszFilter == NULL),
547 * but if we do we should accept it only for the root.
548 */
549 else if (pwszFilter)
550 {
551 /* Copy the whole path for filtering. */
552 pFilter = vboxSfOs2StrDup(pStrFolderPath);
553 if (pFilter)
554 {
555 /* Strip the filename off the one we're opening. */
556 pStrFolderPath->u16Length = (uint16_t)((uintptr_t)pwszFilter - (uintptr_t)pStrFolderPath->String.utf16);
557 pStrFolderPath->u16Size = pStrFolderPath->u16Length + (uint16_t)sizeof(RTUTF16);
558 pStrFolderPath->String.utf16[pStrFolderPath->u16Length / sizeof(RTUTF16)] = '\0';
559 }
560 else
561 rc = ERROR_NOT_ENOUGH_MEMORY;
562 }
563 else if (!pwszFilter && pStrFolderPath->u16Length > 1)
564 {
565 LogFlow(("FS32_FINDFIRST: Trailing slash (%ls)\n", pStrFolderPath->String.utf16));
566 rc = ERROR_PATH_NOT_FOUND;
567 }
568 else
569 LogFlow(("FS32_FINDFIRST: Root dir (%ls)\n", pStrFolderPath->String.utf16));
570
571 /*
572 * Allocate data/request buffer and another buffer for receiving entries in.
573 */
574 if (rc == NO_ERROR)
575 {
576 PVBOXSFFSBUF pDataBuf = (PVBOXSFFSBUF)VbglR0PhysHeapAlloc(sizeof(*pDataBuf));
577 if (pDataBuf)
578 {
579#define MIN_BUF_SIZE ( RT_ALIGN_32(sizeof(SHFLDIRINFO) + CCHMAXPATHCOMP * sizeof(RTUTF16) + 64 /*fudge*/ + ALLOC_HDR_SIZE, 64) \
580 - ALLOC_HDR_SIZE)
581 RT_ZERO(*pDataBuf);
582 pDataBuf->cbBuf = cMaxMatches == 1 ? MIN_BUF_SIZE : _16K - ALLOC_HDR_SIZE;
583 pDataBuf->pBuf = (PSHFLDIRINFO)VbglR0PhysHeapAlloc(pDataBuf->cbBuf);
584 if (pDataBuf->pBuf)
585 { /* likely */ }
586 else
587 {
588 pDataBuf->pBuf = (PSHFLDIRINFO)VbglR0PhysHeapAlloc(MIN_BUF_SIZE);
589 if (pDataBuf->pBuf)
590 pDataBuf->cbBuf = MIN_BUF_SIZE;
591 else
592 rc = ERROR_NOT_ENOUGH_MEMORY;
593 }
594 }
595 if (rc == NO_ERROR)
596 {
597 /*
598 * Now, try open the directory for reading.
599 */
600 pReq->CreateParms.CreateFlags = SHFL_CF_DIRECTORY | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACT_OPEN_IF_EXISTS
601 | SHFL_CF_ACCESS_READ | SHFL_CF_ACCESS_ATTR_READ | SHFL_CF_ACCESS_DENYNONE;
602
603 int vrc = VbglR0SfHostReqCreate(pFolder->idHostRoot, pReq);
604 LogFlow(("FS32_FINDFIRST: VbglR0SfHostReqCreate(%ls) -> %Rrc Result=%d fMode=%#x hHandle=%#RX64\n",
605 pStrFolderPath->String.utf16, vrc, pReq->CreateParms.Result, pReq->CreateParms.Info.Attr.fMode,
606 pReq->CreateParms.Handle));
607 if (RT_SUCCESS(vrc))
608 {
609 switch (pReq->CreateParms.Result)
610 {
611 case SHFL_FILE_EXISTS:
612 if (pReq->CreateParms.Handle != SHFL_HANDLE_NIL)
613 {
614 /*
615 * Initialize the structures.
616 */
617 pFsFsd->hHostDir = pReq->CreateParms.Handle;
618 pFsFsd->u32Magic = VBOXSFFS_MAGIC;
619 pFsFsd->pFolder = pFolder;
620 pFsFsd->pBuf = pDataBuf;
621 pFsFsd->offLastFile = 0;
622 pDataBuf->u32Magic = VBOXSFFSBUF_MAGIC;
623 pDataBuf->cbValid = 0;
624 pDataBuf->cEntriesLeft = 0;
625 pDataBuf->pEntry = NULL;
626 pDataBuf->pFilter = pFilter;
627 pDataBuf->fMustHaveAttribs = (uint8_t)((fAttribs >> 8) & (RTFS_DOS_MASK_OS2 >> RTFS_DOS_SHIFT));
628 pDataBuf->fExcludedAttribs = (uint8_t)(~fAttribs
629 & ( (RTFS_DOS_MASK_OS2 & ~(RTFS_DOS_ARCHIVED | RTFS_DOS_READONLY)
630 >> RTFS_DOS_SHIFT)));
631 pDataBuf->fLongFilenames = RT_BOOL(fAttribs & FF_ATTR_LONG_FILENAME);
632 pDataBuf->cMinLocalTimeDelta = vboxSfOs2GetLocalTimeDelta();
633
634 rc = vboxSfOs2ReadDirEntries(pFolder, pFsFsd, pDataBuf, uLevel, fFlags,
635 pbData, cbData, cMaxMatches ? cMaxMatches : UINT16_MAX, pcMatches);
636 if (rc == NO_ERROR)
637 {
638 uint32_t cRefs = ASMAtomicIncU32(&pFolder->cOpenSearches);
639 Assert(cRefs < _4K); RT_NOREF(cRefs);
640
641 /* We keep these on success: */
642 if (pFilter == pStrFolderPath)
643 pStrFolderPath = NULL;
644 pFilter = NULL;
645 pDataBuf = NULL;
646 pFolder = NULL;
647 }
648 else
649 {
650 AssertCompile(sizeof(VBOXSFCLOSEREQ) < sizeof(*pReq));
651 vrc = VbglR0SfHostReqClose(pFolder->idHostRoot, (VBOXSFCLOSEREQ *)pReq, pFsFsd->hHostDir);
652 AssertRC(vrc);
653 pFsFsd->u32Magic = ~VBOXSFFS_MAGIC;
654 pDataBuf->u32Magic = ~VBOXSFFSBUF_MAGIC;
655 pFsFsd->pFolder = NULL;
656 pFsFsd->hHostDir = NULL;
657 }
658 }
659 else
660 {
661 LogFlow(("FS32_FINDFIRST: VbglR0SfHostReqCreate returns NIL handle for '%ls'\n",
662 pStrFolderPath->String.utf16));
663 rc = ERROR_PATH_NOT_FOUND;
664 }
665 break;
666
667 case SHFL_PATH_NOT_FOUND:
668 rc = ERROR_PATH_NOT_FOUND;
669 break;
670
671 default:
672 case SHFL_FILE_NOT_FOUND:
673 rc = ERROR_FILE_NOT_FOUND;
674 break;
675 }
676 }
677 else
678 rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_GEN_FAILURE);
679 }
680
681 if (pDataBuf)
682 {
683 VbglR0PhysHeapFree(pDataBuf->pBuf);
684 pDataBuf->pBuf = NULL;
685 VbglR0PhysHeapFree(pDataBuf);
686 }
687 }
688 vboxSfOs2StrFree(pFilter);
689 VbglR0PhysHeapFree(pReq);
690 vboxSfOs2ReleaseFolder(pFolder);
691 }
692
693 RT_NOREF_PV(pFsFsi);
694 LogFlow(("FS32_FINDFIRST: returns %u\n", rc));
695 return rc;
696}
697
698
699DECLASM(APIRET)
700FS32_FINDFROMNAME(PFSFSI pFsFsi, PVBOXSFFS pFsFsd, PBYTE pbData, ULONG cbData, PUSHORT pcMatches,
701 ULONG uLevel, ULONG uPosition, PCSZ pszName, ULONG fFlags)
702{
703 LogFlow(("FS32_FINDFROMNAME: pFsFsi=%p pFsFsd=%p pbData=%p cbData=%#x pcMatches=%p:{%#x} uLevel=%#x uPosition=%#x pszName=%p:{%s} fFlags=%#x\n",
704 pFsFsi, pFsFsd, pbData, cbData, pcMatches, *pcMatches, uLevel, uPosition, pszName, pszName, fFlags));
705
706 /*
707 * Input validation.
708 */
709 USHORT const cMaxMatches = *pcMatches;
710 *pcMatches = 0;
711 AssertReturn(pFsFsd->u32Magic == VBOXSFFS_MAGIC, ERROR_SYS_INTERNAL);
712 PVBOXSFFOLDER pFolder = pFsFsd->pFolder;
713 AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
714 Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
715 Assert(pFolder->cOpenSearches > 0);
716 PVBOXSFFSBUF pDataBuf = pFsFsd->pBuf;
717 AssertReturn(pDataBuf, ERROR_SYS_INTERNAL);
718 Assert(pDataBuf->u32Magic == VBOXSFFSBUF_MAGIC);
719
720 switch (uLevel)
721 {
722 case FI_LVL_STANDARD:
723 case FI_LVL_STANDARD_64:
724 case FI_LVL_STANDARD_EASIZE:
725 case FI_LVL_STANDARD_EASIZE_64:
726 break;
727
728 case FI_LVL_EAS_FROM_LIST:
729 case FI_LVL_EAS_FROM_LIST_64:
730 Log(("FS32_FINDFIRST: FI_LVL_EAS_FROM_LIST[_64] -> ERROR_EAS_NOT_SUPPORTED\n"));
731 return ERROR_EAS_NOT_SUPPORTED;
732
733 default:
734 LogRel(("FS32_FINDFIRST: Unsupported info level %u!\n", uLevel));
735 return ERROR_INVALID_LEVEL;
736 }
737
738 /*
739 * Check if we're just continuing. This is usually the case.
740 */
741 APIRET rc;
742 if (uPosition == pFsFsd->offLastFile)
743 rc = vboxSfOs2ReadDirEntries(pFolder, pFsFsd, pDataBuf, uLevel, fFlags, pbData, cbData,
744 cMaxMatches ? cMaxMatches : UINT16_MAX, pcMatches);
745 else
746 {
747 Log(("TODO: uPosition differs: %#x, expected %#x (%s)\n", uPosition, pFsFsd->offLastFile, pszName));
748 rc = vboxSfOs2ReadDirEntries(pFolder, pFsFsd, pDataBuf, uLevel, fFlags, pbData, cbData,
749 cMaxMatches ? cMaxMatches : UINT16_MAX, pcMatches);
750 }
751
752 RT_NOREF(pFsFsi, pszName);
753 LogFlow(("FS32_FINDFROMNAME: returns %u (*pcMatches=%#x)\n", rc, *pcMatches));
754 return rc;
755}
756
757
758DECLASM(APIRET)
759FS32_FINDNEXT(PFSFSI pFsFsi, PVBOXSFFS pFsFsd, PBYTE pbData, ULONG cbData, PUSHORT pcMatches, ULONG uLevel, ULONG fFlags)
760{
761 LogFlow(("FS32_FINDNEXT: pFsFsi=%p pFsFsd=%p pbData=%p cbData=%#x pcMatches=%p:{%#x} uLevel=%#x fFlags=%#x\n",
762 pFsFsi, pFsFsd, pbData, cbData, pcMatches, *pcMatches, uLevel, fFlags));
763
764 /*
765 * Input validation.
766 */
767 USHORT const cMaxMatches = *pcMatches;
768 *pcMatches = 0;
769 AssertReturn(pFsFsd->u32Magic == VBOXSFFS_MAGIC, ERROR_SYS_INTERNAL);
770 PVBOXSFFOLDER pFolder = pFsFsd->pFolder;
771 AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
772 Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
773 Assert(pFolder->cOpenSearches > 0);
774 PVBOXSFFSBUF pDataBuf = pFsFsd->pBuf;
775 AssertReturn(pDataBuf, ERROR_SYS_INTERNAL);
776 Assert(pDataBuf->u32Magic == VBOXSFFSBUF_MAGIC);
777
778 switch (uLevel)
779 {
780 case FI_LVL_STANDARD:
781 case FI_LVL_STANDARD_64:
782 case FI_LVL_STANDARD_EASIZE:
783 case FI_LVL_STANDARD_EASIZE_64:
784 break;
785
786 case FI_LVL_EAS_FROM_LIST:
787 case FI_LVL_EAS_FROM_LIST_64:
788 Log(("FS32_FINDFIRST: FI_LVL_EAS_FROM_LIST[_64] -> ERROR_EAS_NOT_SUPPORTED\n"));
789 return ERROR_EAS_NOT_SUPPORTED;
790
791 default:
792 LogRel(("FS32_FINDFIRST: Unsupported info level %u!\n", uLevel));
793 return ERROR_INVALID_LEVEL;
794 }
795
796 /*
797 * Read more.
798 */
799 APIRET rc = vboxSfOs2ReadDirEntries(pFolder, pFsFsd, pDataBuf, uLevel, fFlags, pbData, cbData,
800 cMaxMatches ? cMaxMatches : UINT16_MAX, pcMatches);
801
802 NOREF(pFsFsi);
803 LogFlow(("FS32_FINDNEXT: returns %u (*pcMatches=%#x)\n", rc, *pcMatches));
804 return rc;
805}
806
807
808DECLASM(APIRET)
809FS32_FINDCLOSE(PFSFSI pFsFsi, PVBOXSFFS pFsFsd)
810{
811 /*
812 * Input validation.
813 */
814 AssertReturn(pFsFsd->u32Magic == VBOXSFFS_MAGIC, ERROR_SYS_INTERNAL);
815 PVBOXSFFOLDER pFolder = pFsFsd->pFolder;
816 AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
817 Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
818 Assert(pFolder->cOpenSearches > 0);
819 PVBOXSFFSBUF pDataBuf = pFsFsd->pBuf;
820 AssertReturn(pDataBuf, ERROR_SYS_INTERNAL);
821 Assert(pDataBuf->u32Magic == VBOXSFFSBUF_MAGIC);
822
823 /*
824 * Close it.
825 */
826 if (pFsFsd->hHostDir != SHFL_HANDLE_NIL)
827 {
828 int vrc = VbglR0SfHostReqCloseSimple(pFolder->idHostRoot, pFsFsd->hHostDir);
829 AssertRC(vrc);
830 }
831
832 pFsFsd->u32Magic = ~VBOXSFFS_MAGIC;
833 pFsFsd->hHostDir = SHFL_HANDLE_NIL;
834 pFsFsd->pFolder = NULL;
835 pFsFsd->pBuf = NULL;
836 vboxSfOs2StrFree(pDataBuf->pFilter);
837 pDataBuf->pFilter = NULL;
838 pDataBuf->u32Magic = ~VBOXSFFSBUF_MAGIC;
839 pDataBuf->cbBuf = 0;
840
841 VbglR0PhysHeapFree(pDataBuf->pBuf);
842 pDataBuf->pBuf = NULL;
843 VbglR0PhysHeapFree(pDataBuf);
844
845 uint32_t cRefs = ASMAtomicDecU32(&pFolder->cOpenSearches);
846 Assert(cRefs < _4K); RT_NOREF(cRefs);
847 vboxSfOs2ReleaseFolder(pFolder);
848
849 RT_NOREF(pFsFsi);
850 LogFlow(("FS32_FINDCLOSE: returns NO_ERROR\n"));
851 return NO_ERROR;
852}
853
854
855
856
857
858DECLASM(APIRET)
859FS32_FINDNOTIFYFIRST(PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszPath, LONG offCurDirEnd, ULONG fAttribs,
860 PUSHORT phHandle, PBYTE pbData, ULONG cbData, PUSHORT pcMatches,
861 ULONG uLevel, ULONG fFlags)
862{
863 RT_NOREF(pCdFsi, pCdFsd, pszPath, offCurDirEnd, fAttribs, phHandle, pbData, cbData, pcMatches, uLevel, fFlags);
864 return ERROR_NOT_SUPPORTED;
865}
866
867
868DECLASM(APIRET)
869FS32_FINDNOTIFYNEXT(ULONG hHandle, PBYTE pbData, ULONG cbData, PUSHORT pcMatchs, ULONG uLevel, ULONG cMsTimeout)
870{
871 RT_NOREF(hHandle, pbData, cbData, pcMatchs, uLevel, cMsTimeout);
872 return ERROR_NOT_SUPPORTED;
873}
874
875
876DECLASM(APIRET)
877FS32_FINDNOTIFYCLOSE(ULONG hHandle)
878{
879 NOREF(hHandle);
880 return ERROR_NOT_SUPPORTED;
881}
882
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use