Attempt to improve clarity of the orphan cluster reclaim code.

Minor optimization - remove some unnecessary checking.

Signed-off-by: Daniel Baumann <mail@daniel-baumann.ch>
This commit is contained in:
Steven J. Magnani 2009-10-04 10:58:43 +02:00 committed by Daniel Baumann
parent 8054b4a371
commit bc84254457

102
src/fat.c
View File

@ -174,6 +174,10 @@ void read_fat(DOS_FS *fs)
* @param[in,out] fs Information about the filesystem
* @param[in] cluster Cluster to change
* @param[in] new Cluster to link to
* Special values:
* 0 == free cluster
* -1 == end-of-chain
* -2 == bad cluster
*/
void set_fat(DOS_FS *fs,unsigned long cluster,unsigned long new)
{
@ -345,27 +349,52 @@ void reclaim_free(DOS_FS *fs)
}
static void tag_free(DOS_FS *fs,DOS_FILE *ptr,
const unsigned long *prev_cluster)
/**
* Assign the specified owner to all orphan chains (except cycles).
* Break cross-links between orphan chains.
*
* @param[in,out] fs Information about the filesystem
* @param[in] owner dentry to be assigned ownership of orphans
* @param[in,out] num_refs For each orphan cluster [index], how many
* clusters link to it.
* @param[in] start_cluster Where to start scanning for orphans
*/
static void tag_free(DOS_FS *fs, DOS_FILE *owner, unsigned long *num_refs,
unsigned long start_cluster)
{
DOS_FILE *owner;
int prev;
unsigned long i,walk;
for (i = 2; i < fs->clusters+2; i++) {
if (start_cluster == 0)
start_cluster = 2;
for (i = start_cluster; i < fs->clusters+2; i++) {
FAT_ENTRY curEntry;
get_fat(&curEntry, fs->fat, i, fs);
/* If the current entry is the head of an un-owned chain... */
if (curEntry.value && !FAT_IS_BAD(fs, curEntry.value) &&
!get_owner(fs,i) && !prev_cluster[i]) {
!get_owner(fs, i) && !num_refs[i]) {
prev = 0;
for (walk = i; walk > 0 && walk != -1;
/* Walk the chain, claiming ownership as we go */
for (walk = i; walk != -1;
walk = next_cluster(fs,walk)) {
if (!(owner = get_owner(fs,walk))) set_owner(fs,walk,ptr);
else if (owner != ptr)
die("Internal error: free chain collides with file");
else {
if (!get_owner(fs, walk)) {
set_owner(fs, walk, owner);
} else {
/* We've run into cross-links between orphaned chains,
* or a cycle with a tail.
* Terminate this orphan chain (break the link)
*/
set_fat(fs,prev,-1);
/* This is not necessary because 'walk' is owned and thus
* will never become the head of a chain (the only case
* that would matter during reclaim to files).
* It's easier to decrement than to prove that it's
* unnecessary.
*/
num_refs[walk]--;
break;
}
prev = walk;
@ -374,21 +403,30 @@ static void tag_free(DOS_FS *fs,DOS_FILE *ptr,
}
}
/**
* Recover orphan chains to files, handling any cycles or cross-links.
*
* @param[in,out] fs Information about the filesystem
*/
void reclaim_file(DOS_FS *fs)
{
DOS_FILE dummy;
int reclaimed,files,changed;
DOS_FILE orphan;
int reclaimed,files;
int changed = 0;
unsigned long i,next,walk;
unsigned long *prev_cluster = NULL;
unsigned long *num_refs = NULL; /* Only for orphaned clusters */
unsigned long total_num_clusters;
if (verbose)
printf("Reclaiming unconnected clusters.\n");
total_num_clusters = fs->clusters + 2UL;
prev_cluster = alloc(total_num_clusters * sizeof(unsigned long));
memset(prev_cluster, 0, (total_num_clusters * sizeof(unsigned long)));
num_refs = alloc(total_num_clusters * sizeof(unsigned long));
memset(num_refs, 0, (total_num_clusters * sizeof(unsigned long)));
/* Guarantee that all orphan chains (except cycles) end cleanly
* with an end-of-chain mark.
*/
for (i = 2; i < total_num_clusters; i++) {
FAT_ENTRY curEntry;
@ -396,37 +434,55 @@ void reclaim_file(DOS_FS *fs)
next = curEntry.value;
if (!get_owner(fs,i) && next && next < fs->clusters+2) {
/* Cluster is linked, but not owned (orphan) */
FAT_ENTRY nextEntry;
get_fat(&nextEntry, fs->fat, next, fs);
/* Mark it end-of-chain if it links into an owned cluster,
* a free cluster, or a bad cluster.
*/
if (get_owner(fs,next) || !nextEntry.value ||
FAT_IS_BAD(fs, nextEntry.value)) set_fat(fs,i,-1);
else
prev_cluster[next]++;
num_refs[next]++;
}
}
/* Scan until all the orphans are accounted for,
* and all cycles and cross-links are broken
*/
do {
tag_free(fs,&dummy, prev_cluster);
tag_free(fs, &orphan, num_refs, changed);
changed = 0;
/* Any unaccounted-for orphans must be part of a cycle */
for (i = 2; i < total_num_clusters; i++) {
FAT_ENTRY curEntry;
get_fat(&curEntry, fs->fat, i, fs);
if (curEntry.value && !FAT_IS_BAD(fs, curEntry.value) &&
!get_owner(fs, i)) {
if (!prev_cluster[curEntry.value]--)
die("Internal error: prev going below zero");
if (!num_refs[curEntry.value]--)
die("Internal error: num_refs going below zero");
set_fat(fs,i,-1);
changed = 1;
changed = curEntry.value;
printf("Broke cycle at cluster %lu in free chain.\n",i);
/* If we've created a new chain head,
* tag_free() can claim it
*/
if (num_refs[curEntry.value] == 0)
break;
}
}
}
while (changed);
/* Now we can start recovery */
files = reclaimed = 0;
for (i = 2; i < total_num_clusters; i++)
if (get_owner(fs,i) == &dummy && !prev_cluster[i]) {
/* If this cluster is the head of an orphan chain... */
if (get_owner(fs, i) == &orphan && !num_refs[i]) {
DIR_ENT de;
loff_t offset;
files++;
@ -446,7 +502,7 @@ void reclaim_file(DOS_FS *fs)
reclaimed,reclaimed == 1 ? "" : "s",(unsigned long long)reclaimed*fs->cluster_size,files,
files == 1 ? "" : "s");
free(prev_cluster);
free(num_refs);
}