Fix FAT delete of items with long name (#21528)
This commit is contained in:
parent
ba5644376c
commit
0b5c25aa7c
@ -1049,6 +1049,20 @@ int16_t SdBaseFile::read(void *buf, uint16_t nbyte) {
|
|||||||
return nbyte;
|
return nbyte;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate a checksum for an 8.3 filename
|
||||||
|
*
|
||||||
|
* \param name The 8.3 file name to calculate
|
||||||
|
*
|
||||||
|
* \return The checksum byte
|
||||||
|
*/
|
||||||
|
uint8_t lfn_checksum(const uint8_t *name) {
|
||||||
|
uint8_t sum = 0;
|
||||||
|
for (uint8_t i = 11; i; i--)
|
||||||
|
sum = ((sum & 1) << 7) + (sum >> 1) + *name++;
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the next entry in a directory.
|
* Read the next entry in a directory.
|
||||||
*
|
*
|
||||||
@ -1065,36 +1079,47 @@ int8_t SdBaseFile::readDir(dir_t *dir, char *longFilename) {
|
|||||||
// if not a directory file or miss-positioned return an error
|
// if not a directory file or miss-positioned return an error
|
||||||
if (!isDir() || (0x1F & curPosition_)) return -1;
|
if (!isDir() || (0x1F & curPosition_)) return -1;
|
||||||
|
|
||||||
|
#define INVALIDATE_LONGNAME() (longFilename[0] = longFilename[1] = '\0')
|
||||||
|
|
||||||
// If we have a longFilename buffer, mark it as invalid.
|
// If we have a longFilename buffer, mark it as invalid.
|
||||||
// If a long filename is found it will be filled automatically.
|
// If a long filename is found it will be filled automatically.
|
||||||
if (longFilename) { longFilename[0] = '\0'; longFilename[1] = '\0'; }
|
if (longFilename) INVALIDATE_LONGNAME();
|
||||||
|
|
||||||
|
uint8_t checksum_error = 0xFF, checksum = 0;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
n = read(dir, sizeof(dir_t));
|
n = read(dir, sizeof(dir_t));
|
||||||
if (n != sizeof(dir_t)) return n ? -1 : 0;
|
if (n != sizeof(dir_t)) return n ? -1 : 0;
|
||||||
|
|
||||||
// last entry if DIR_NAME_FREE
|
// Last entry if DIR_NAME_FREE
|
||||||
if (dir->name[0] == DIR_NAME_FREE) return 0;
|
if (dir->name[0] == DIR_NAME_FREE) return 0;
|
||||||
|
|
||||||
// skip deleted entry and entry for . and ..
|
// Skip deleted entry and entry for . and ..
|
||||||
if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') {
|
if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') {
|
||||||
if (longFilename) { longFilename[0] = '\0'; longFilename[1] = '\0'; } // Invalidate erased file long name, if any
|
if (longFilename) INVALIDATE_LONGNAME(); // Invalidate erased file long name, if any
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (longFilename) {
|
||||||
// Fill the long filename if we have a long filename entry.
|
// Fill the long filename if we have a long filename entry.
|
||||||
// Long filename entries are stored before the short filename.
|
// Long filename entries are stored before the short filename.
|
||||||
if (longFilename && DIR_IS_LONG_NAME(dir)) {
|
if (DIR_IS_LONG_NAME(dir)) {
|
||||||
vfat_t *VFAT = (vfat_t*)dir;
|
vfat_t *VFAT = (vfat_t*)dir;
|
||||||
// Sanity-check the VFAT entry. The first cluster is always set to zero. And the sequence number should be higher than 0
|
// Sanity-check the VFAT entry. The first cluster is always set to zero. And the sequence number should be higher than 0
|
||||||
if (VFAT->firstClusterLow == 0) {
|
if (VFAT->firstClusterLow == 0) {
|
||||||
const uint8_t seq = VFAT->sequenceNumber & 0x1F;
|
const uint8_t seq = VFAT->sequenceNumber & 0x1F;
|
||||||
if (WITHIN(seq, 1, MAX_VFAT_ENTRIES)) {
|
if (WITHIN(seq, 1, MAX_VFAT_ENTRIES)) {
|
||||||
// TODO: Store the filename checksum to verify if a long-filename-unaware system modified the file table.
|
|
||||||
n = (seq - 1) * (FILENAME_LENGTH);
|
n = (seq - 1) * (FILENAME_LENGTH);
|
||||||
|
if (n == 0) {
|
||||||
|
checksum = VFAT->checksum;
|
||||||
|
checksum_error = 0;
|
||||||
|
}
|
||||||
|
else if (checksum != VFAT->checksum) // orphan detected
|
||||||
|
checksum_error = 1;
|
||||||
|
|
||||||
LOOP_L_N(i, FILENAME_LENGTH) {
|
LOOP_L_N(i, FILENAME_LENGTH) {
|
||||||
uint16_t utf16_ch = (i < 5) ? VFAT->name1[i] : (i < 11) ? VFAT->name2[i - 5] : VFAT->name3[i - 11];
|
const uint16_t utf16_ch = (i >= 11) ? VFAT->name3[i - 11] : (i >= 5) ? VFAT->name2[i - 5] : VFAT->name1[i];
|
||||||
#if ENABLED(UTF_FILENAME_SUPPORT)
|
#if ENABLED(UTF_FILENAME_SUPPORT)
|
||||||
// We can't reconvert to UTF-8 here as UTF-8 is variable-size encoding, but joining LFN blocks
|
// We can't reconvert to UTF-8 here as UTF-8 is variable-size encoding, but joining LFN blocks
|
||||||
// needs static bytes addressing. So here just store full UTF-16LE words to re-convert later.
|
// needs static bytes addressing. So here just store full UTF-16LE words to re-convert later.
|
||||||
@ -1107,10 +1132,16 @@ int8_t SdBaseFile::readDir(dir_t *dir, char *longFilename) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
// If this VFAT entry is the last one, add a NUL terminator at the end of the string
|
// If this VFAT entry is the last one, add a NUL terminator at the end of the string
|
||||||
if (VFAT->sequenceNumber & 0x40) longFilename[(n + FILENAME_LENGTH) * LONG_FILENAME_CHARSIZE] = '\0';
|
if (VFAT->sequenceNumber & 0x40)
|
||||||
|
longFilename[(n + FILENAME_LENGTH) * LONG_FILENAME_CHARSIZE] = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
if (!checksum_error && lfn_checksum(dir->name) != checksum) checksum_error = 1; // orphan detected
|
||||||
|
if (checksum_error) INVALIDATE_LONGNAME();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Post-process normal file or subdirectory longname, if any
|
// Post-process normal file or subdirectory longname, if any
|
||||||
if (DIR_IS_FILE_OR_SUBDIR(dir)) {
|
if (DIR_IS_FILE_OR_SUBDIR(dir)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user