diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2020-01-13 15:31:29 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2020-01-13 15:31:29 -0800 |
commit | 97214502ea95f9f95b3e86fe749bc8241ec26105 (patch) | |
tree | bf2ff0e8f57dacb1aefed5b6fb9b93700f6ee472 | |
parent | 21386e05d8aee3cd69124c826bf10da19726e045 (diff) | |
download | fsck_msdos-97214502ea95f9f95b3e86fe749bc8241ec26105.tar.gz |
Import revision b60894b10adb071a8dc2b6fea5f9867c24bc9c47 from FreeBSD.
Change-Id: Ida6851c44456928d45a391352dd4e552dfac93ef
-rw-r--r-- | boot.c | 23 | ||||
-rw-r--r-- | fat.c | 77 | ||||
-rw-r--r-- | fsck_msdosfs.8 | 19 |
3 files changed, 78 insertions, 41 deletions
@@ -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. */ @@ -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 |