Advanced settings FAT modification: fix expanding directory

When no more directory entries are available in the existing
cluster(s) of a directory on FAT32, we allocate an
extra cluster.
Make sure to zero out that new cluster, as disk checking utilities
may not stop reading when reaching an end of directory marker but
read the rest of cluster as well.
So there must not be any garbage data from a previously deleted file
in the sector there.

Also add checks to prevent getting in an endless loop on
encoutering circular "next cluster" references.
This commit is contained in:
Floris Bos 2022-11-20 18:44:22 +01:00
parent 2694f3976a
commit a403df4385
3 changed files with 24 additions and 4 deletions

2
debian/changelog vendored
View file

@ -8,7 +8,7 @@ rpi-imager (1.7.4) unstable; urgency=medium
disk device, without having to rely on the operating system disk device, without having to rely on the operating system
to mount the partition first. to mount the partition first.
-- Floris Bos <bos@je-eigen-domein.nl> Mon, 14 Nov 2022 21:49:27 +0100 -- Floris Bos <bos@je-eigen-domein.nl> Sun, 20 Nov 2022 17:30:20 +0100
rpi-imager (1.7.3) unstable; urgency=medium rpi-imager (1.7.3) unstable; urgency=medium

View file

@ -506,6 +506,8 @@ void DeviceWrapperFatPartition::updateDirEntry(struct dir_entry *dirEntry)
struct dir_entry iterEntry; struct dir_entry iterEntry;
openDir(); openDir();
quint64 oldOffset = _offset;
while (readDir(&iterEntry)) while (readDir(&iterEntry))
{ {
/* Look for existing entry with same short filename */ /* Look for existing entry with same short filename */
@ -513,10 +515,12 @@ void DeviceWrapperFatPartition::updateDirEntry(struct dir_entry *dirEntry)
&& memcmp(dirEntry->DIR_Name, iterEntry.DIR_Name, sizeof(iterEntry.DIR_Name)) == 0) && memcmp(dirEntry->DIR_Name, iterEntry.DIR_Name, sizeof(iterEntry.DIR_Name)) == 0)
{ {
/* seek() back and write out new entry */ /* seek() back and write out new entry */
_offset -= sizeof(*dirEntry); _offset = oldOffset;
write((char *) dirEntry, sizeof(*dirEntry)); write((char *) dirEntry, sizeof(*dirEntry));
return; return;
} }
oldOffset = _offset;
} }
throw std::runtime_error("Error locating existing directory entry"); throw std::runtime_error("Error locating existing directory entry");
@ -524,6 +528,7 @@ void DeviceWrapperFatPartition::updateDirEntry(struct dir_entry *dirEntry)
void DeviceWrapperFatPartition::writeDirEntryAtCurrentPos(struct dir_entry *dirEntry) void DeviceWrapperFatPartition::writeDirEntryAtCurrentPos(struct dir_entry *dirEntry)
{ {
//qDebug() << "Write new entry" << QByteArray((char *) dirEntry->DIR_Name, 11);
write((char *) dirEntry, sizeof(*dirEntry)); write((char *) dirEntry, sizeof(*dirEntry));
if (_type == FAT32) if (_type == FAT32)
@ -532,15 +537,23 @@ void DeviceWrapperFatPartition::writeDirEntryAtCurrentPos(struct dir_entry *dirE
{ {
/* We reached the end of the cluster, allocate/seek to next cluster */ /* We reached the end of the cluster, allocate/seek to next cluster */
uint32_t nextCluster = getFAT(_fat32_currentRootDirCluster); uint32_t nextCluster = getFAT(_fat32_currentRootDirCluster);
/* FIXME: should we check for circular cluster references? */
if (nextCluster > 0xFFFFFF7) if (nextCluster > 0xFFFFFF7)
{ {
nextCluster = allocateCluster(_fat32_currentRootDirCluster); nextCluster = allocateCluster(_fat32_currentRootDirCluster);
} }
if (_currentDirClusters.contains(nextCluster))
throw std::runtime_error("Circular cluster references in FAT32 directory detected");
_currentDirClusters.append(nextCluster);
_fat32_currentRootDirCluster = nextCluster; _fat32_currentRootDirCluster = nextCluster;
seekCluster(_fat32_currentRootDirCluster); seekCluster(_fat32_currentRootDirCluster);
/* Zero out entire new cluster, as fsck.fat does not stop reading entries at end-of-directory marker */
QByteArray zeroes(_bytesPerCluster, 0);
write(zeroes.data(), zeroes.length() );
seekCluster(_fat32_currentRootDirCluster);
} }
} }
else if (pos() > (_fat16_firstRootDirSector+_fat16_rootDirSectors)*_bytesPerSector) else if (pos() > (_fat16_firstRootDirSector+_fat16_rootDirSectors)*_bytesPerSector)
@ -560,6 +573,10 @@ void DeviceWrapperFatPartition::openDir()
{ {
_fat32_currentRootDirCluster = _fat32_firstRootDirCluster; _fat32_currentRootDirCluster = _fat32_firstRootDirCluster;
seekCluster(_fat32_currentRootDirCluster); seekCluster(_fat32_currentRootDirCluster);
/* Keep track of directory clusters we seeked to, to be able
to detect circular references */
_currentDirClusters.clear();
_currentDirClusters.append(_fat32_currentRootDirCluster);
} }
} }
@ -581,11 +598,13 @@ bool DeviceWrapperFatPartition::readDir(struct dir_entry *result)
{ {
/* We reached the end of the cluster, seek to next cluster */ /* We reached the end of the cluster, seek to next cluster */
uint32_t nextCluster = getFAT(_fat32_currentRootDirCluster); uint32_t nextCluster = getFAT(_fat32_currentRootDirCluster);
/* FIXME: should we check for circular cluster references? */
if (nextCluster > 0xFFFFFF7) if (nextCluster > 0xFFFFFF7)
throw std::runtime_error("Reached end of FAT32 root directory, but no end-of-directory marker found"); throw std::runtime_error("Reached end of FAT32 root directory, but no end-of-directory marker found");
if (_currentDirClusters.contains(nextCluster))
throw std::runtime_error("Circular cluster references in FAT32 directory detected");
_currentDirClusters.append(nextCluster);
_fat32_currentRootDirCluster = nextCluster; _fat32_currentRootDirCluster = nextCluster;
seekCluster(_fat32_currentRootDirCluster); seekCluster(_fat32_currentRootDirCluster);
} }

View file

@ -31,6 +31,7 @@ protected:
uint32_t _fat32_firstRootDirCluster, _fat32_currentRootDirCluster; uint32_t _fat32_firstRootDirCluster, _fat32_currentRootDirCluster;
uint16_t _bytesPerSector, _fat32_fsinfoSector; uint16_t _bytesPerSector, _fat32_fsinfoSector;
QList<uint32_t> _fatStartOffset; QList<uint32_t> _fatStartOffset;
QList<uint32_t> _currentDirClusters;
QList<uint32_t> getClusterChain(uint32_t firstCluster); QList<uint32_t> getClusterChain(uint32_t firstCluster);
void setFAT16(uint16_t cluster, uint16_t value); void setFAT16(uint16_t cluster, uint16_t value);