Tighten FAT checks and fix off-by-one error in corner case.
sbin/fsck_msdosfs/fat.c: - readfat: * Only truncate out-of-range cluster pointers (1, or greater than NumClusters but smaller than CLUST_RSRVD), as the current cluster may contain some data. We can't fix reserved cluster pointers at this pass, because we do no know the potential cluster preceding it. * Accept valid cluster for head bitmap. This is a no-op, and mainly to improve code readability, because the 1 is already handled in the previous else if block. - truncate_at: absorbed into checkchain. - checkchain: save the previous node we have traversed in case that we have a chain that ends with a special (>= CLUST_RSRVD) cluster, or is free. In these cases, we need to truncate at the cluster preceding the current cluster, as the current cluster contains a marker instead of a next pointer and can not be changed to CLUST_EOF (the else case can happen if the user answered "no" at some point in readfat()). - clearchain: correct the iterator for next cluster so that we don't stop after clearing the first cluster. - checklost: If checkchain() thinks the chain have no cluster, it doesn't make sense to reconnect it, so don't bother asking. Reviewed by: kevlo MFC after: 24 days X-MFC-With: r356313 Differential Revision: https://reviews.freebsd.org/D23065
This commit is contained in:
parent
d199ad3b44
commit
d14a599d69
@ -935,17 +935,16 @@ readfat(int fs, struct bootblock *boot, struct fat_descriptor **fp)
|
||||
fat_clear_cl_head(fat, cl);
|
||||
}
|
||||
boot->NumBad++;
|
||||
} else if (!valid_cl(fat, nextcl) && nextcl < CLUST_EOFS) {
|
||||
pwarn("Cluster %u continues with %s "
|
||||
} else if (!valid_cl(fat, nextcl) && nextcl < CLUST_RSRVD) {
|
||||
pwarn("Cluster %u continues with out of range "
|
||||
"cluster number %u\n",
|
||||
cl, (nextcl < CLUST_RSRVD) ?
|
||||
"out of range" : "reserved",
|
||||
cl,
|
||||
nextcl & boot->ClustMask);
|
||||
if (ask(0, "Truncate")) {
|
||||
ret |= fat_set_cl_next(fat, cl, CLUST_EOF);
|
||||
ret |= FSFATMOD;
|
||||
}
|
||||
} else if (nextcl < boot->NumClusters) {
|
||||
} else if (valid_cl(fat, nextcl)) {
|
||||
if (fat_is_cl_head(fat, nextcl)) {
|
||||
fat_clear_cl_head(fat, nextcl);
|
||||
} else {
|
||||
@ -984,30 +983,14 @@ rsrvdcltype(cl_t cl)
|
||||
return "bad";
|
||||
}
|
||||
|
||||
/*
|
||||
* Offer to truncate a chain at the specified CL, called by checkchain().
|
||||
*/
|
||||
static inline int
|
||||
truncate_at(struct fat_descriptor *fat, cl_t current_cl, size_t *chainsize)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (ask(0, "Truncate")) {
|
||||
ret = fat_set_cl_next(fat, current_cl, CLUST_EOF);
|
||||
(*chainsize)++;
|
||||
return (ret | FSFATMOD);
|
||||
} else {
|
||||
return FSERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Examine a cluster chain for errors and count its size.
|
||||
*/
|
||||
int
|
||||
checkchain(struct fat_descriptor *fat, cl_t head, size_t *chainsize)
|
||||
{
|
||||
cl_t current_cl, next_cl;
|
||||
cl_t prev_cl, current_cl, next_cl;
|
||||
const char *op;
|
||||
|
||||
/*
|
||||
* We expect that the caller to give us a real, unvisited 'head'
|
||||
@ -1038,10 +1021,10 @@ checkchain(struct fat_descriptor *fat, cl_t head, size_t *chainsize)
|
||||
* it as EOF) when the next node violates that.
|
||||
*/
|
||||
*chainsize = 0;
|
||||
current_cl = head;
|
||||
prev_cl = current_cl = head;
|
||||
for (next_cl = fat_get_cl_next(fat, current_cl);
|
||||
valid_cl(fat, next_cl);
|
||||
current_cl = next_cl, next_cl = fat_get_cl_next(fat, current_cl))
|
||||
prev_cl = current_cl, current_cl = next_cl, next_cl = fat_get_cl_next(fat, current_cl))
|
||||
(*chainsize)++;
|
||||
|
||||
/* A natural end */
|
||||
@ -1050,12 +1033,40 @@ checkchain(struct fat_descriptor *fat, cl_t head, size_t *chainsize)
|
||||
return FSOK;
|
||||
}
|
||||
|
||||
/* The chain ended with an out-of-range cluster number. */
|
||||
pwarn("Cluster %u continues with %s cluster number %u\n",
|
||||
current_cl,
|
||||
next_cl < CLUST_RSRVD ? "out of range" : "reserved",
|
||||
next_cl & boot_of_(fat)->ClustMask);
|
||||
return (truncate_at(fat, current_cl, chainsize));
|
||||
/*
|
||||
* The chain ended with an out-of-range cluster number.
|
||||
*
|
||||
* If the current node is e.g. CLUST_FREE, CLUST_BAD, etc.,
|
||||
* it should not be present in a chain and we has to truncate
|
||||
* at the previous node.
|
||||
*
|
||||
* If the current cluster points to an invalid cluster, the
|
||||
* current cluster might have useful data and we truncate at
|
||||
* the current cluster instead.
|
||||
*/
|
||||
if (next_cl == CLUST_FREE || next_cl >= CLUST_RSRVD) {
|
||||
pwarn("Cluster chain starting at %u ends with cluster marked %s\n",
|
||||
head, rsrvdcltype(next_cl));
|
||||
current_cl = prev_cl;
|
||||
} else {
|
||||
pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n",
|
||||
head,
|
||||
next_cl & boot_of_(fat)->ClustMask);
|
||||
(*chainsize)++;
|
||||
}
|
||||
|
||||
if (*chainsize > 0) {
|
||||
op = "Truncate";
|
||||
next_cl = CLUST_EOF;
|
||||
} else {
|
||||
op = "Clear";
|
||||
next_cl = CLUST_FREE;
|
||||
}
|
||||
if (ask(0, "%s", op)) {
|
||||
return (fat_set_cl_next(fat, current_cl, next_cl) | FSFATMOD);
|
||||
} else {
|
||||
return (FSERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1070,7 +1081,7 @@ clearchain(struct fat_descriptor *fat, cl_t head)
|
||||
current_cl = head;
|
||||
|
||||
while (valid_cl(fat, current_cl)) {
|
||||
next_cl = fat_get_cl_next(fat, head);
|
||||
next_cl = fat_get_cl_next(fat, current_cl);
|
||||
(void)fat_set_cl_next(fat, current_cl, CLUST_FREE);
|
||||
boot->NumFree++;
|
||||
current_cl = next_cl;
|
||||
@ -1218,7 +1229,7 @@ checklost(struct fat_descriptor *fat)
|
||||
}
|
||||
if (fat_is_cl_head(fat, head)) {
|
||||
ret = checkchain(fat, head, &chainlength);
|
||||
if (ret != FSERROR) {
|
||||
if (ret != FSERROR && chainlength > 0) {
|
||||
pwarn("Lost cluster chain at cluster %u\n"
|
||||
"%zd Cluster(s) lost\n",
|
||||
head, chainlength);
|
||||
|
Loading…
x
Reference in New Issue
Block a user