📄 find.c
字号:
// 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) {
DWORD cb = (((MAX_SHORT_NAMES+1)+31)/32 + 2)*sizeof(DWORD);
InitNumericTail(&di);
// CreateBitArray(pdwShortNameBitArray, MAX_SHORT_NAMES+1);
if (cb > 2*sizeof(DWORD)) {
pdwShortNameBitArray = (PDWORD)HeapAlloc (hHeap, HEAP_ZERO_MEMORY, cb);
}
if (pdwShortNameBitArray) {
pdwShortNameBitArray[0] = 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???\r\n")));
dwError = ERROR_INVALID_DATA;
}
else
dwError = ERROR_FILE_NOT_FOUND;
}
else
DEBUGMSG(ZONE_ERRORS,(TEXT("FATFS!FindNext: unexpected error (%d)\r\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!\r\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)\r\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)\r\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) {
// This LFN sequence has TOO MANY entries, which also
// means it won't fit in a MAX_PATH buffer. We could
// treat the sequence like orphaned LFN entries, but
// that seems a rather risky proposition, since we may be
// talking directory corruption here. Safer to just ignore
// them and rely on SCANDSKW to fix things up. -JTP
goto resetall;
}
if (!(di.di_flags & DIRINFO_OEM) && di.di_cdeNeed > 1 && seqLFN != di.di_cdeNeed-1) {
// Optimization: this LFN sequence does not have the requisite
// number of entries, so it can't possibly be a match.
goto resetall;
}
chksum = plde->lde_chksum;
// If this LFN sequence has the wrong OEM chksum, it might simply
// be due to charset differences between the system where the OEM name
// was originally created and the current system, so unlike previous
// releases, we will no longer bail out and 'goto resetall'; we will
// instead continue to collect all the LFN chunks and do the full name
// comparison. In other words, we are no longer dependent on the
// accuracy of the OEM version of the name (as computed by UniToOEMName). -JTP
DEBUGMSGW(di.di_cdeNeed == 1 && chksum != di.di_chksum && ZONE_APIS && ZONE_ERRORS,(DBGTEXTW("FATFS!FindNext: lfn chksum (%x) doesn't match OEM chksum (%x) for '%.11hs'\r\n"), chksum, di.di_chksum, di.di_achOEM));
// Remember this LFN's starting position
di.di_posLFN = di.di_pos;
cwComp = di.di_cwTail;
pwsComp = di.di_pwsTail;
pwsBuff = &pfd->cFileName[LDE_NAME_LEN*LN_MAX_ORD_NUM];
*--pwsBuff = 0; // guarantee a NULL-terminator
// If this LFN contains the max number of LFNDIRENTRYs,
// we have to scoot pwsBuff up one WCHAR and reduce the
// amount of the final memcpy (of lde_name3) by one WCHAR,
// to insure that the NULL-terminator above is preserved.
if (seqLFN == LN_MAX_ORD_NUM) {
pwsBuff++;
cbName3 -= sizeof(WCHAR);
}
}
// Skip LFN entries we're not interested in
else if (seqLFN == -1) {
goto resetall;
}
// Recover LFN entries that are inconsistent
else if (seqLFN != plde->lde_ord || chksum != plde->lde_chksum) {
// All preceding LFN entries are deemed bogus/recoverable.
// If they can be merged with the free count, do it, otherwise
// leave them recorded as unrecoverable.
DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!FindNext: LFN sequence (0x%x vs. 0x%x) or LFN chksum (0x%x vs. 0x%x) error\n"), seqLFN, plde->lde_ord, chksum, plde->lde_chksum));
di.di_posOrphan = di.di_posLFN;
di.di_cdeOrphan = (di.di_pos - di.di_posLFN)/sizeof(LFNDIRENTRY);
if (di.di_posFree + di.di_cdeFree * sizeof(LFNDIRENTRY) == di.di_posLFN) {
di.di_cdeFree += di.di_cdeOrphan;
di.di_cdeOrphan = 0;
}
goto resetall;
}
if (di.di_cdeFree < di.di_cdeNeed)
di.di_cdeFree = 0;
// LFN directory entry in sequence. Copy the name entry by
// entry into a scratch buffer to be matched when all of the
// entries in the name have been processed.
pwsBuff -= LDE_NAME_LEN;
memcpy(pwsBuff, plde->lde_name1, sizeof(plde->lde_name1));
memcpy(pwsBuff+LDE_LEN1, plde->lde_name2, sizeof(plde->lde_name2));
memcpy(pwsBuff+LDE_LEN1+LDE_LEN2, plde->lde_name3, cbName3);
// If we can compare individual pieces, do it now
if (fComp) {
// We perform a quick comparison on the first
// character past the end of the LFN (if one exists)
// and if that character isn't a NULL, then this
// entry isn't a match; otherwise, compare entire piece.
if (cwComp != LDE_NAME_LEN && pwsBuff[cwComp] != 0 ||
_wcsnicmp(pwsBuff, pwsComp, cwComp) != 0) {
goto resetlfn;
}
pwsComp -= LDE_NAME_LEN;
}
--seqLFN;
continue;
}
#ifdef TFAT
// As an optimization for TFAT, if the first cluster in the directory contains
// a volume label, then we can skip this cluster because the entire cluster is
// filled with volume labels.
if (pstmDir->s_pvol->v_fTfat && ((di.di_pde->de_attr & ATTR_DIR_MASK) == ATTR_VOLUME_ID) &&
(di.di_pos < pstmDir->s_pvol->v_cbClus) && (di.di_cdeFree == 0) && (di.di_posLFN == INVALID_POS))
{
// Set the position a cluster into the directory and ReadStream again.
di.di_pos = pstmDir->s_pvol->v_cbClus;
break;
}
#endif
// Check for a "normal" directory entry.
if ((di.di_pde->de_attr & ATTR_DIR_MASK) != ATTR_VOLUME_ID) {
int len;
if (!(psh->sh_flags & SHF_DOTDOT) &&
(*(PDWORD)di.di_pde->de_name == 0x2020202e ||
*(PDWORD)di.di_pde->de_name == 0x20202e2e)) {
goto resetall;
}
// Normal directory entry. If preceeding LFN
// directory entries matched, handle this entry
// as a match. Otherwise, if the name is 8.3
// compatible, then try to match the 8.3 OEM name
// contained in this entry.
if (seqLFN == 0) {
if (chksum == ChkSumName(di.di_pde->de_name)) {
len = wcslen(pwsBuff);
// We processed all the LFN chunks for the current entry,
// (since seqLFN is now 0), so if fComp is TRUE, that means
// we compared every chunk as we went, and they must have
// all matched, so let's just 'goto match'
if (fComp)
goto match;
// If cdeNeed == -1, we're dealing with wildcards *or* a
// a non-name-based search, so we have to explicitly check
// for a match.
if (di.di_cdeNeed == -1)
goto checkmatch;
// Well, we must be performing a name-based search, and since
// we didn't compare the LFN chunks as we went, we did at least
// collect them all in pwsBuff, so let's see if the full LFN
// matches....
//
// Note: we didn't *always* used to do this, which meant that we
// always fell into the "normal directory entry" case below, which
// which only compared the OEM forms of the filenames; normally,
// that was sufficient, but in cases where the OEM name was created
// on a system with different charset mappings, that comparison could
// fail. We should rely on the 8.3 OEM name IFF there are no LFN
// entries -- and in this case, there *is*.... -JTP
if (len == psh->sh_cwPattern && _wcsnicmp(pwsBuff, psh->sh_awcPattern, len) == 0)
goto match;
}
else {
// All preceding LFN entries are deemed bogus/recoverable.
// If they can be merged with the free count, do it, otherwise
// leave them recorded as unrecoverable.
DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!FindNext: LFN chksum (0x%x vs. 0x%x) error\n"), chksum, ChkSumName(di.di_pde->de_name)));
di.di_posOrphan = di.di_posLFN;
di.di_cdeOrphan = (di.di_pos - di.di_posLFN)/sizeof(LFNDIRENTRY);
if (di.di_posFree + di.di_cdeFree * sizeof(LFNDIRENTRY) != di.di_posLFN) {
di.di_cdeFree = 0;
} else {
di.di_cdeFree += di.di_cdeOrphan;
di.di_cdeOrphan = 0;
}
// We used to bail at this point (goto checkshort), but the right
// thing to do is just treat the current DIRENTRY as a short-name only.
seqLFN = -1;
}
}
// Normal directory entry, and either there were no preceding LFN
// entries, or there were but we couldn't make use of them.
if (di.di_cdeNeed == -1 ||
(di.di_flags & DIRINFO_OEM) &&
memcmp(di.di_pde->de_name,
di.di_achOEM,
sizeof(di.di_pde
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -