svn commit: r356657 - head/sbin/fsck_msdosfs
Xin LI
delphij at FreeBSD.org
Sun Jan 12 06:13:53 UTC 2020
Author: delphij
Date: Sun Jan 12 06:13:52 2020
New Revision: 356657
URL: https://svnweb.freebsd.org/changeset/base/356657
Log:
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
Modified:
head/sbin/fsck_msdosfs/fat.c
Modified: head/sbin/fsck_msdosfs/fat.c
==============================================================================
--- head/sbin/fsck_msdosfs/fat.c Sun Jan 12 06:09:10 2020 (r356656)
+++ head/sbin/fsck_msdosfs/fat.c Sun Jan 12 06:13:52 2020 (r356657)
@@ -935,17 +935,16 @@ readfat(int fs, struct bootblock *boot, struct fat_des
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 {
@@ -985,29 +984,13 @@ rsrvdcltype(cl_t cl)
}
/*
- * 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
* 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
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);
More information about the svn-src-head
mailing list