summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2020-01-13 15:31:29 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2020-01-13 15:31:29 -0800
commit97214502ea95f9f95b3e86fe749bc8241ec26105 (patch)
treebf2ff0e8f57dacb1aefed5b6fb9b93700f6ee472
parent21386e05d8aee3cd69124c826bf10da19726e045 (diff)
downloadfsck_msdos-97214502ea95f9f95b3e86fe749bc8241ec26105.tar.gz
Import revision b60894b10adb071a8dc2b6fea5f9867c24bc9c47 from FreeBSD.
Change-Id: Ida6851c44456928d45a391352dd4e552dfac93ef
-rw-r--r--boot.c23
-rw-r--r--fat.c77
-rw-r--r--fsck_msdosfs.819
3 files changed, 78 insertions, 41 deletions
diff --git a/boot.c b/boot.c
index a3ba7ab..86528ce 100644
--- a/boot.c
+++ b/boot.c
@@ -28,7 +28,7 @@
#include <sys/cdefs.h>
#ifndef lint
-__RCSID("$NetBSD: boot.c,v 1.11 2006/06/05 16:51:18 christos Exp ");
+__RCSID("$NetBSD: boot.c,v 1.21 2018/02/08 09:05:17 dholland Exp $");
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
@@ -250,7 +250,7 @@ readboot(int dosfs, struct bootblock *boot)
boot->FATsecs = boot->bpbFATsmall;
}
- if (boot->FATsecs > UINT32_MAX / boot->bpbFATs) {
+ if (boot->FATsecs < 1 || boot->FATsecs > UINT32_MAX / boot->bpbFATs) {
pfatal("Invalid FATs(%u) with FATsecs(%zu)",
boot->bpbFATs, (size_t)boot->FATsecs);
return FSFATAL;
@@ -266,8 +266,11 @@ readboot(int dosfs, struct bootblock *boot)
return FSFATAL;
}
- boot->NumClusters = (boot->NumSectors - boot->FirstCluster) / boot->bpbSecPerClust +
- CLUST_FIRST;
+ /*
+ * The number of clusters is derived from available data sectors, divided
+ * by sectors per cluster.
+ */
+ boot->NumClusters = (boot->NumSectors - boot->FirstCluster) / boot->bpbSecPerClust;
if (boot->flags & FAT32) {
if (boot->NumClusters > (CLUST_RSRVD & CLUST32_MASK)) {
@@ -310,11 +313,19 @@ readboot(int dosfs, struct bootblock *boot)
break;
}
- if (boot->NumFatEntries < boot->NumClusters - CLUST_FIRST) {
+ if (boot->NumFatEntries < boot->NumClusters) {
pfatal("FAT size too small, %u entries won't fit into %u sectors\n",
boot->NumClusters, boot->FATsecs);
return FSFATAL;
}
+
+ /*
+ * There are two reserved clusters. To avoid adding CLUST_FIRST every time
+ * when we perform boundary checks, we increment the NumClusters by 2,
+ * which is CLUST_FIRST to denote the first out-of-range cluster number.
+ */
+ boot->NumClusters += CLUST_FIRST;
+
boot->ClusterSize = boot->bpbBytesPerSec * boot->bpbSecPerClust;
boot->NumFiles = 1;
@@ -356,7 +367,7 @@ writefsinfo(int dosfs, struct bootblock *boot)
* support for FAT32) doesn't maintain the FSINFO block
* correctly, it has to be fixed pretty often.
*
- * Therefor, we handle the FSINFO block only informally,
+ * Therefore, we handle the FSINFO block only informally,
* fixing it if necessary, but otherwise ignoring the
* fact that it was incorrect.
*/
diff --git a/fat.c b/fat.c
index 12050c2..cb92010 100644
--- a/fat.c
+++ b/fat.c
@@ -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 {
@@ -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_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);
diff --git a/fsck_msdosfs.8 b/fsck_msdosfs.8
index f953dbd..602451b 100644
--- a/fsck_msdosfs.8
+++ b/fsck_msdosfs.8
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd October 3, 2016
+.Dd January 6, 2020
.Dt FSCK_MSDOSFS 8
.Os
.Sh NAME
@@ -37,7 +37,7 @@
.Op Fl Cf
.Ar filesystem ...
.Nm
-.Op Fl Cny
+.Op Fl CMny
.Ar filesystem ...
.Sh DESCRIPTION
The
@@ -84,6 +84,21 @@ which seeks to determine whether the file system needs to be cleaned
immediately in foreground, or if its cleaning can be deferred to background.
FAT (MS-DOS) file systems must always be cleaned in the foreground.
A non-zero exit code is always returned for this option.
+.It Fl M
+Causes
+.Nm
+to not use
+.Xr mmap 2
+when checking a FAT32 file system.
+This option is mainly for debugging purposes and is not normally necessary.
+The
+.Nm
+utility will automatically fall back to use a simple LRU cache of 4 MiB
+when it failed to perform
+.Xr mmap 2 ,
+or when
+.Fl M
+is specified.
.It Fl f
Force
.Nm