Skip to content

Commit a403df4

Browse files
committed
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.
1 parent 2694f39 commit a403df4

File tree

3 files changed

+24
-4
lines changed

3 files changed

+24
-4
lines changed

debian/changelog

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ rpi-imager (1.7.4) unstable; urgency=medium
88
disk device, without having to rely on the operating system
99
to mount the partition first.
1010

11-
-- Floris Bos <[email protected]> Mon, 14 Nov 2022 21:49:27 +0100
11+
-- Floris Bos <[email protected]> Sun, 20 Nov 2022 17:30:20 +0100
1212

1313
rpi-imager (1.7.3) unstable; urgency=medium
1414

src/devicewrapperfatpartition.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -506,24 +506,29 @@ void DeviceWrapperFatPartition::updateDirEntry(struct dir_entry *dirEntry)
506506
struct dir_entry iterEntry;
507507

508508
openDir();
509+
quint64 oldOffset = _offset;
510+
509511
while (readDir(&iterEntry))
510512
{
511513
/* Look for existing entry with same short filename */
512514
if (!(iterEntry.DIR_Attr & ATTR_LONG_NAME)
513515
&& memcmp(dirEntry->DIR_Name, iterEntry.DIR_Name, sizeof(iterEntry.DIR_Name)) == 0)
514516
{
515517
/* seek() back and write out new entry */
516-
_offset -= sizeof(*dirEntry);
518+
_offset = oldOffset;
517519
write((char *) dirEntry, sizeof(*dirEntry));
518520
return;
519521
}
522+
523+
oldOffset = _offset;
520524
}
521525

522526
throw std::runtime_error("Error locating existing directory entry");
523527
}
524528

525529
void DeviceWrapperFatPartition::writeDirEntryAtCurrentPos(struct dir_entry *dirEntry)
526530
{
531+
//qDebug() << "Write new entry" << QByteArray((char *) dirEntry->DIR_Name, 11);
527532
write((char *) dirEntry, sizeof(*dirEntry));
528533

529534
if (_type == FAT32)
@@ -532,15 +537,23 @@ void DeviceWrapperFatPartition::writeDirEntryAtCurrentPos(struct dir_entry *dirE
532537
{
533538
/* We reached the end of the cluster, allocate/seek to next cluster */
534539
uint32_t nextCluster = getFAT(_fat32_currentRootDirCluster);
535-
/* FIXME: should we check for circular cluster references? */
536540

537541
if (nextCluster > 0xFFFFFF7)
538542
{
539543
nextCluster = allocateCluster(_fat32_currentRootDirCluster);
540544
}
541545

546+
if (_currentDirClusters.contains(nextCluster))
547+
throw std::runtime_error("Circular cluster references in FAT32 directory detected");
548+
_currentDirClusters.append(nextCluster);
549+
542550
_fat32_currentRootDirCluster = nextCluster;
543551
seekCluster(_fat32_currentRootDirCluster);
552+
553+
/* Zero out entire new cluster, as fsck.fat does not stop reading entries at end-of-directory marker */
554+
QByteArray zeroes(_bytesPerCluster, 0);
555+
write(zeroes.data(), zeroes.length() );
556+
seekCluster(_fat32_currentRootDirCluster);
544557
}
545558
}
546559
else if (pos() > (_fat16_firstRootDirSector+_fat16_rootDirSectors)*_bytesPerSector)
@@ -560,6 +573,10 @@ void DeviceWrapperFatPartition::openDir()
560573
{
561574
_fat32_currentRootDirCluster = _fat32_firstRootDirCluster;
562575
seekCluster(_fat32_currentRootDirCluster);
576+
/* Keep track of directory clusters we seeked to, to be able
577+
to detect circular references */
578+
_currentDirClusters.clear();
579+
_currentDirClusters.append(_fat32_currentRootDirCluster);
563580
}
564581
}
565582

@@ -581,11 +598,13 @@ bool DeviceWrapperFatPartition::readDir(struct dir_entry *result)
581598
{
582599
/* We reached the end of the cluster, seek to next cluster */
583600
uint32_t nextCluster = getFAT(_fat32_currentRootDirCluster);
584-
/* FIXME: should we check for circular cluster references? */
585601

586602
if (nextCluster > 0xFFFFFF7)
587603
throw std::runtime_error("Reached end of FAT32 root directory, but no end-of-directory marker found");
588604

605+
if (_currentDirClusters.contains(nextCluster))
606+
throw std::runtime_error("Circular cluster references in FAT32 directory detected");
607+
_currentDirClusters.append(nextCluster);
589608
_fat32_currentRootDirCluster = nextCluster;
590609
seekCluster(_fat32_currentRootDirCluster);
591610
}

src/devicewrapperfatpartition.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class DeviceWrapperFatPartition : public DeviceWrapperPartition
3131
uint32_t _fat32_firstRootDirCluster, _fat32_currentRootDirCluster;
3232
uint16_t _bytesPerSector, _fat32_fsinfoSector;
3333
QList<uint32_t> _fatStartOffset;
34+
QList<uint32_t> _currentDirClusters;
3435

3536
QList<uint32_t> getClusterChain(uint32_t firstCluster);
3637
void setFAT16(uint16_t cluster, uint16_t value);

0 commit comments

Comments
 (0)