📄 find.c
字号:
if (psh->sh_pstm == NULL)
goto exit;
if (len == 0) {
// The caller must be trying to find '\\'. That's normally
// a no-no. FAT_FindFirstFileW makes an exception, but it makes
// no sense internally, because the root is never contained by
// another directory stream.
dwError = ERROR_FILE_NOT_FOUND;
goto exit;
}
psh->sh_pos = 0;
psh->sh_cwPattern = (WORD)len;
memcpy(psh->sh_awcPattern, pwsName, len*sizeof(WCHAR));
psh->sh_awcPattern[len] = 0;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
dwError = ERROR_INVALID_PARAMETER;
goto exit;
}
dwError = FindNext(psh, pdi, pfd);
exit:
if (dwError == ERROR_FILE_NOT_FOUND && (flName & (NAME_FILE|NAME_DIR)) == NAME_DIR)
dwError = ERROR_PATH_NOT_FOUND;
if (dwError)
SetLastError(dwError);
if (!pdi) {
if (psh->sh_pstm) {
CloseStream(psh->sh_pstm);
psh->sh_pstm = NULL;
}
}
return psh->sh_pstm;
}
/* MatchNext - worker for FindNext
*
* ENTRY
* psh - pointer to SHANDLE structure
* len - length of current name
* pws - pointer to current name
* pde - pointer to DIRENTRY to read from if match
*
* EXIT
* TRUE if (another) file was found, FALSE if not
*/
__inline BOOL MatchNext(PSHANDLE psh, PWSTR pws, int len, PDIRENTRY pde)
{
int fMask = psh->sh_flags & SHF_BYMASK;
// Check the match criteria, and then check for that kind of match
ASSERT(fMask); // make sure flags are set to something
if (psh->sh_pstm->s_cwPath + max(3,psh->sh_pstm->s_pvol->v_cwsHostRoot) + len + 2 > MAX_PATH)
return FALSE;
return(
fMask == SHF_BYNAME &&
MatchesWildcard(psh->sh_cwPattern, psh->sh_awcPattern, len, pws)
||
fMask == SHF_BYORD &&
(DWORD)(psh->sh_h) == psh->sh_pos/sizeof(DIRENTRY)
||
fMask == SHF_BYCLUS &&
(DWORD)(psh->sh_h) == GETDIRENTRYCLUSTER(psh->sh_pstm->s_pvol, pde)
);
}
/* CopyTimes - worker for FindNext
*
* ENTRY
* pfd - pointer to WIN32_FIND_DATAW structure
* pde - pointer to DIRENTRY containing timestamps to transfer
*
* EXIT
* None
*/
__inline void CopyTimes(PWIN32_FIND_DATAW pfd, PDIRENTRY pde)
{
// DOSTimeToFileTime can fail because it doesn't range-check any of
// the date/time values in the directory entry; it just pulls them apart
// and calls SystemTimeToFileTime, which won't initialize the FILETIME
// structure if there's an error. So we preset all the FILETIMEs to zero,
// because that's what the Win32 API dictates when a time is unknown/not
// supported.
memset(&pfd->ftCreationTime, 0, sizeof(pfd->ftCreationTime) +
sizeof(pfd->ftLastAccessTime) +
sizeof(pfd->ftLastWriteTime));
DOSTimeToFileTime(pde->de_createdDate,
pde->de_createdTime,
pde->de_createdTimeMsec, &pfd->ftCreationTime);
DOSTimeToFileTime(pde->de_date, pde->de_time, 0, &pfd->ftLastWriteTime);
DOSTimeToFileTime(pde->de_lastAccessDate, 0, 0, &pfd->ftLastAccessTime);
}
/* FindNext - worker for FindFirstFileW, FindNextFileW, etc.
*
* Callers need to supply a WIN32_FIND_DATAW structure even if they're
* not interested in "find data". This is because the search may involve
* comparisons against LFN directory entries, which have to be built a piece
* at a time, and a wildcard comparison cannot be performed until the entire
* name has been built. NON-wildcard searches can perform their comparisons
* a piece at a time, and don't necessarily need the MAX_PATH buffer that
* WIN32_FIND_DATAW provides us, but using the same buffer strategy in both
* cases yields better code.
*
* ENTRY
* psh - pointer to SHANDLE structure
* pdi - pointer to DIRINFO (optional, NULL if not needed)
* pfd - pointer to user's WIN32_FIND_DATAW structure (required)
*
* EXIT
* 0 if successful, error code if not
*
* On success, most of the fields in the WIN32_FIND_DATAW structure are filled
* in (eg, dwFileAttributes, nFileSizeHigh, nFileSizeLow, cFileName, and some
* CE-specific fields like dwOID). Some are only filled in depending on the
* type of search however, like all the FILETIME fields (eg, ftCreationTime,
* ftLastAccessTime, ftLastWriteTime). For example, SHF_BYCLUS searches, which
* are only used and supported internally, don't care about those fields, so we
* don't waste time filling them in.
*/
DWORD FindNext(PSHANDLE psh, PDIRINFO pdi, PWIN32_FIND_DATAW pfd)
{
PDIRENTRY pdeEnd; // ptr to ending directory entry
BYTE chksum; // LFN entry checksum of 8.3 name
int seqLFN = -1; // next LFN DIRENTRY seq # expected
PCWSTR pwsComp;
PWSTR pwsBuff;
BOOL fComp = FALSE;
DIRINFO di;
DWORD dwError = ERROR_SUCCESS;
PDSTREAM pstmDir = psh->sh_pstm;
PDWORD pdwShortNameBitArray = NULL;
ERRFALSE(sizeof(pfd->cFileName) >= sizeof(WCHAR [LDE_NAME_LEN*LN_MAX_ORD_NUM]));
ASSERT(pstmDir);
ASSERT(psh->sh_flags); // make sure flags are set to something
ASSERT(OWNCRITICALSECTION(&pstmDir->s_cs));
memset(&di, 0, sizeof(DIRINFO));
di.di_pos = psh->sh_pos;
di.di_posLFN = INVALID_POS;
di.di_clusEntry = UNKNOWN_CLUSTER;
di.di_cdeNeed = -1;
if (psh->sh_pos == INVALID_POS) {
dwError = ERROR_FILE_NOT_FOUND;
goto norelease;
}
if ((psh->sh_flags & SHF_BYMASK) == SHF_BYNAME) {
di.di_cdeNeed = UniToOEMName(di.di_achOEM, psh->sh_awcPattern, psh->sh_cwPattern);
// If the name is invalid (-2), or contains wildcards when wildcards
// are not allowed, fail immediately.
if (di.di_cdeNeed == -2 || di.di_cdeNeed == -1 && !(psh->sh_flags & SHF_WILD)) {
dwError = ERROR_INVALID_NAME;
goto norelease;
}
// If the name is OEM, then force the CREATE flag off. Otherwise,
// we'll leave the flag on if the caller turned it on (meaning he
// wants us to check every normal DIRENTRY for a collision with the
// short name in achOEM and set it to some name that doesn't conflict).
if (di.di_cdeNeed >= 1) {
di.di_flags |= DIRINFO_OEM;
di.di_chksum = ChkSumName(di.di_achOEM);
if (di.di_cdeNeed == 1) {
psh->sh_flags &= ~SHF_CREATE;
}
// If the name fits in achOEM but contains lower-case, then cdeNeed
// is already correct (2), but cwTail and pwsTail still need to be set.
else if (di.di_cdeNeed == 2) {
di.di_cwTail = psh->sh_cwPattern;
di.di_pwsTail = psh->sh_awcPattern;
}
// If there's some uncertainty about the charset mapping, then revert to
// the UNICODE case.
else {
ASSERT(di.di_cdeNeed == 3);
di.di_cdeNeed = 0;
}
}
// If the name is UNICODE (0), then cdeNeed needs to be recomputed,
// based on the length of the UNICODE name. Furthermore, unlike the
// wildcard case, we will be able to compare it piece by piece.
// pwsTail points to the end-most piece to compare, and cwTail is its
// length.
if (di.di_cdeNeed == 0) {
fComp = TRUE;
di.di_pwsTail = psh->sh_awcPattern + psh->sh_cwPattern;
di.di_cdeNeed = ((psh->sh_cwPattern+LDE_NAME_LEN-1)/LDE_NAME_LEN) + 1;
if ((di.di_cwTail = psh->sh_cwPattern % LDE_NAME_LEN) == 0)
di.di_cwTail = LDE_NAME_LEN;
di.di_pwsTail -= di.di_cwTail;
}
// Also record the complete length of the name. If we actually
// find a match, we will update this field again (since the match
// might have been based on a short name and we always return
// long names), but if we don't find a match, this still needs to
// be set in case the caller wants to create it (via CreateName).
di.di_cwName = psh->sh_cwPattern;
}
// Create a short name conflict bit array if the caller may need
// to create a new LFN entry. We must add 1 to MAX_SHORT_NAMES because
// the set of addressible bits in the array starts with bit 0.
if (psh->sh_flags & SHF_CREATE) {
InitNumericTail(&di);
CreateBitArray(pdwShortNameBitArray, MAX_SHORT_NAMES+1);
}
while (!dwError) {
dwError = ReadStream(pstmDir, di.di_pos, &di.di_pde, &pdeEnd);
if (dwError) {
if (dwError == ERROR_HANDLE_EOF) {
if (di.di_pos == 0) {
DEBUGMSG(ZONE_ERRORS,(TEXT("FATFS!FindNext: 0-length directory???\n")));
dwError = ERROR_INVALID_DATA;
}
else
dwError = ERROR_FILE_NOT_FOUND;
}
else
DEBUGMSG(ZONE_ERRORS,(TEXT("FATFS!FindNext: unexpected error (%d)\n"), dwError));
di.di_posLFN = INVALID_POS;
break;
}
// Let's do a little more error-checking before we dive in; in particular,
// if this is not a root directory, then it must have "dot" and "dotdot" entries
// at the top, else the directory is corrupt.
if (!ISROOTDIR(pstmDir) && di.di_pos == 0 && di.di_pde+1 < pdeEnd) {
DWORD dwClus;
if (*(PDWORD)di.di_pde->de_name != 0x2020202e ||
*(PDWORD)(di.di_pde+1)->de_name != 0x20202e2e) {
DEBUGMSG(ZONE_ERRORS,(TEXT("FATFS!FindNext: bogus directory!\n")));
dwError = ERROR_INVALID_DATA;
break;
}
// NOTE: I'm reserving ERROR_FILE_CORRUPT to indicate "correctable" directory
// errors. All the caller has to do is plug in correct cluster values. See the
// code in SCAN.C for more details.
dwClus = GETDIRENTRYCLUSTER(pstmDir->s_pvol, di.di_pde);
if (dwClus != pstmDir->s_clusFirst) {
DEBUGMSG(ZONE_ERRORS,(TEXT("FATFS!FindNext: bogus '.' cluster (0x%08x instead of 0x%08x)\n"), dwClus, pstmDir->s_clusFirst));
dwError = ERROR_FILE_CORRUPT;
break;
}
dwClus = GETDIRENTRYCLUSTER(pstmDir->s_pvol, di.di_pde+1);
if (!ISPARENTROOTDIR(pstmDir) || dwClus != NO_CLUSTER) {
if (pstmDir->s_sid.sid_clusDir >= DATA_CLUSTER &&
pstmDir->s_sid.sid_clusDir != UNKNOWN_CLUSTER && dwClus != pstmDir->s_sid.sid_clusDir) {
DEBUGMSG(ZONE_ERRORS,(TEXT("FATFS!FindNext: bogus '..' cluster (0x%08x instead of 0x%08x)\n"), dwClus, pstmDir->s_sid.sid_clusDir));
dwError = ERROR_FILE_CORRUPT;
break;
}
}
}
__try {
do {
// Check for a deleted entry.
if (di.di_pde->de_name[0] == DIRFREE) {
// Deleted file entry. If this is the first one,
// remember its location.
if (di.di_cdeFree < di.di_cdeNeed && di.di_cdeFree++ == 0)
di.di_posFree = di.di_pos;
// Reset LFN sequence/position info
goto resetlfn;
}
// Check for an ending entry.
if (di.di_pde->de_name[0] == DIREND) {
// Empty file entry. If this is the first one,
// remember its location.
if (di.di_cdeFree < di.di_cdeNeed && di.di_cdeFree++ == 0)
di.di_posFree = di.di_pos;
di.di_posLFN = INVALID_POS;
dwError = ERROR_FILE_NOT_FOUND;
goto release;
}
// Check for an LFN entry.
if (ISLFNDIRENTRY(di.di_pde)) {
int cwComp = LDE_NAME_LEN;
PLFNDIRENTRY plde = (PLFNDIRENTRY)di.di_pde;
int cbName3 = sizeof(plde->lde_name3);
// If lde_flags is not zero, our assumption must be that
// this is some new kind of LFN entry that we don't fully
// understand, and must therefore ignore....
if (plde->lde_flags != 0)
goto resetall;
// Initialization code for initial LFN entry
if (plde->lde_ord & LN_ORD_LAST_MASK) {
seqLFN = plde->lde_ord & ~LN_ORD_LAST_MASK;
if (seqLFN > LN_MAX_ORD_NUM) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -